cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ste...@apache.org
Subject [25/51] [abbrv] [partial] cordova-lib git commit: CB-11980: added missing node modules for test fixtures
Date Wed, 07 Jun 2017 06:04:16 GMT
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/RELEASENOTES.md
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/RELEASENOTES.md b/spec-plugman/projects/android/cordova/node_modules/cordova-common/RELEASENOTES.md
new file mode 100644
index 0000000..83777f5
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/RELEASENOTES.md
@@ -0,0 +1,97 @@
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+-->
+# Cordova-common Release Notes
+
+### 2.0.1 (Mar 09, 2017)
+* [CB-12557](https://issues.apache.org/jira/browse/CB-12557) add both stdout and stderr properties to the error object passed to superspawn reject handler.
+
+### 2.0.0 (Jan 17, 2017)
+* [CB-8978](https://issues.apache.org/jira/browse/CB-8978) Add `resource-file` parsing to `config.xml`
+* [CB-12018](https://issues.apache.org/jira/browse/CB-12018): updated `jshint` and updated tests to work with `jasmine@2` instead of `jasmine-node`
+* [CB-12163](https://issues.apache.org/jira/browse/CB-12163) Add reference attrib to `resource-file` for **Windows**
+* Move windows-specific logic to `cordova-windows`
+* [CB-12189](https://issues.apache.org/jira/browse/CB-12189) Add implementation attribute to framework
+
+### 1.5.1 (Oct 12, 2016)
+* [CB-12002](https://issues.apache.org/jira/browse/CB-12002) Add `getAllowIntents()` to `ConfigParser`
+* [CB-11998](https://issues.apache.org/jira/browse/CB-11998) `cordova platform add` error with `cordova-common@1.5.0`
+
+### 1.5.0 (Oct 06, 2016)
+* [CB-11776](https://issues.apache.org/jira/browse/CB-11776) Add test case for different `edit-config` targets
+* [CB-11908](https://issues.apache.org/jira/browse/CB-11908) Add `edit-config` to `config.xml`
+* [CB-11936](https://issues.apache.org/jira/browse/CB-11936) Support four new **App Transport Security (ATS)** keys
+* update `config.xml` location if it is a **Android Studio** project.
+* use `array` methods and `object.keys` for iterating. avoiding `for-in` loops
+* [CB-11517](https://issues.apache.org/jira/browse/CB-11517) Allow `.folder` matches
+* [CB-11776](https://issues.apache.org/jira/browse/CB-11776) check `edit-config` target exists
+
+### 1.4.1 (Aug 09, 2016)
+* Add general purpose `ConfigParser.getAttribute` API
+* [CB-11653](https://issues.apache.org/jira/browse/CB-11653) moved `findProjectRoot` from `cordova-lib` to `cordova-common`
+* [CB-11636](https://issues.apache.org/jira/browse/CB-11636) Handle attributes with quotes correctly
+* [CB-11645](https://issues.apache.org/jira/browse/CB-11645) added check to see if `getEditConfig` exists before trying to use it
+* [CB-9825](https://issues.apache.org/jira/browse/CB-9825) framework tag spec parsing
+
+### 1.4.0 (Jul 12, 2016)
+* [CB-11023](https://issues.apache.org/jira/browse/CB-11023) Add edit-config functionality
+
+### 1.3.0 (May 12, 2016)
+* [CB-11259](https://issues.apache.org/jira/browse/CB-11259): Improving prepare and build logging
+* [CB-11194](https://issues.apache.org/jira/browse/CB-11194) Improve cordova load time
+* [CB-1117](https://issues.apache.org/jira/browse/CB-1117) Add `FileUpdater` module to `cordova-common`.
+* [CB-11131](https://issues.apache.org/jira/browse/CB-11131) Fix `TypeError: message.toUpperCase` is not a function in `CordovaLogger`
+
+### 1.2.0 (Apr 18, 2016)
+* [CB-11022](https://issues.apache.org/jira/browse/CB-11022) Save modulesMetadata to both www and platform_www when necessary
+* [CB-10833](https://issues.apache.org/jira/browse/CB-10833) Deduplicate common logic for plugin installation/uninstallation
+* [CB-10822](https://issues.apache.org/jira/browse/CB-10822) Manage plugins/modules metadata using PlatformJson
+* [CB-10940](https://issues.apache.org/jira/browse/CB-10940) Can't add Android platform from path
+* [CB-10965](https://issues.apache.org/jira/browse/CB-10965) xml helper allows multiple instances to be merge in config.xml
+
+### 1.1.1 (Mar 18, 2016)
+* [CB-10694](https://issues.apache.org/jira/browse/CB-10694) Update test to reflect merging of [CB-9264](https://issues.apache.org/jira/browse/CB-9264) fix
+* [CB-10694](https://issues.apache.org/jira/browse/CB-10694) Platform-specific configuration preferences don't override global settings
+* [CB-9264](https://issues.apache.org/jira/browse/CB-9264) Duplicate entries in `config.xml`
+* [CB-10791](https://issues.apache.org/jira/browse/CB-10791) Add `adjustLoggerLevel` to `cordova-common.CordovaLogger`
+* [CB-10662](https://issues.apache.org/jira/browse/CB-10662) Add tests for `ConfigParser.getStaticResources`
+* [CB-10622](https://issues.apache.org/jira/browse/CB-10622) fix target attribute being ignored for images in `config.xml`.
+* [CB-10583](https://issues.apache.org/jira/browse/CB-10583) Protect plugin preferences from adding extra Array properties.
+
+### 1.1.0 (Feb 16, 2016)
+* [CB-10482](https://issues.apache.org/jira/browse/CB-10482) Remove references to windows8 from cordova-lib/cli
+* [CB-10430](https://issues.apache.org/jira/browse/CB-10430) Adds forwardEvents method to easily connect two EventEmitters
+* [CB-10176](https://issues.apache.org/jira/browse/CB-10176) Adds CordovaLogger class, based on logger module from cordova-cli
+* [CB-10052](https://issues.apache.org/jira/browse/CB-10052) Expose child process' io streams via promise progress notification
+* [CB-10497](https://issues.apache.org/jira/browse/CB-10497) Prefer .bat over .cmd on windows platform
+* [CB-9984](https://issues.apache.org/jira/browse/CB-9984) Bumps plist version and fixes failing cordova-common test
+
+### 1.0.0 (Oct 29, 2015)
+
+* [CB-9890](https://issues.apache.org/jira/browse/CB-9890) Documents cordova-common
+* [CB-9598](https://issues.apache.org/jira/browse/CB-9598) Correct cordova-lib -> cordova-common in README
+* Pick ConfigParser changes from apache@0c3614e
+* [CB-9743](https://issues.apache.org/jira/browse/CB-9743) Removes system frameworks handling from ConfigChanges
+* [CB-9598](https://issues.apache.org/jira/browse/CB-9598) Cleans out code which has been moved to `cordova-common`
+* Pick ConfigParser changes from apache@ddb027b
+* Picking CordovaError changes from apache@a3b1fca
+* [CB-9598](https://issues.apache.org/jira/browse/CB-9598) Adds tests and fixtures based on existing cordova-lib ones
+* [CB-9598](https://issues.apache.org/jira/browse/CB-9598) Initial implementation for cordova-common
+

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/cordova-common.js
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/cordova-common.js b/spec-plugman/projects/android/cordova/node_modules/cordova-common/cordova-common.js
new file mode 100644
index 0000000..801d510
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/cordova-common.js
@@ -0,0 +1,47 @@
+/**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+
+var addProperty = require('./src/util/addProperty');
+
+module.exports = { };
+
+addProperty(module, 'events', './src/events');
+addProperty(module, 'superspawn', './src/superspawn');
+
+addProperty(module, 'ActionStack', './src/ActionStack');
+addProperty(module, 'CordovaError', './src/CordovaError/CordovaError');
+addProperty(module, 'CordovaLogger', './src/CordovaLogger');
+addProperty(module, 'CordovaCheck', './src/CordovaCheck');
+addProperty(module, 'CordovaExternalToolErrorContext', './src/CordovaError/CordovaExternalToolErrorContext');
+addProperty(module, 'PlatformJson', './src/PlatformJson');
+addProperty(module, 'ConfigParser', './src/ConfigParser/ConfigParser');
+addProperty(module, 'FileUpdater', './src/FileUpdater');
+
+addProperty(module, 'PluginInfo', './src/PluginInfo/PluginInfo');
+addProperty(module, 'PluginInfoProvider', './src/PluginInfo/PluginInfoProvider');
+
+addProperty(module, 'PluginManager', './src/PluginManager');
+
+addProperty(module, 'ConfigChanges', './src/ConfigChanges/ConfigChanges');
+addProperty(module, 'ConfigKeeper', './src/ConfigChanges/ConfigKeeper');
+addProperty(module, 'ConfigFile', './src/ConfigChanges/ConfigFile');
+addProperty(module, 'mungeUtil', './src/ConfigChanges/munge-util');
+
+addProperty(module, 'xmlHelpers', './src/util/xml-helpers');
+

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/package.json
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/package.json b/spec-plugman/projects/android/cordova/node_modules/cordova-common/package.json
new file mode 100644
index 0000000..eb493d8
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/package.json
@@ -0,0 +1,133 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "cordova-common@^2.0.1",
+        "scope": null,
+        "escapedName": "cordova-common",
+        "name": "cordova-common",
+        "rawSpec": "^2.0.1",
+        "spec": ">=2.0.1 <3.0.0",
+        "type": "range"
+      },
+      "/Users/steveng/repo/cordova/cordova-android"
+    ]
+  ],
+  "_from": "cordova-common@>=2.0.1 <3.0.0",
+  "_id": "cordova-common@2.0.1",
+  "_inCache": true,
+  "_location": "/cordova-android/cordova-common",
+  "_nodeVersion": "6.9.4",
+  "_npmOperationalInternal": {
+    "host": "packages-18-east.internal.npmjs.com",
+    "tmp": "tmp/cordova-common-2.0.1.tgz_1489432932737_0.5238456283695996"
+  },
+  "_npmUser": {
+    "name": "filmaj",
+    "email": "maj.fil@gmail.com"
+  },
+  "_npmVersion": "3.10.10",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "cordova-common@^2.0.1",
+    "scope": null,
+    "escapedName": "cordova-common",
+    "name": "cordova-common",
+    "rawSpec": "^2.0.1",
+    "spec": ">=2.0.1 <3.0.0",
+    "type": "range"
+  },
+  "_requiredBy": [
+    "/cordova-android"
+  ],
+  "_resolved": "http://registry.npmjs.org/cordova-common/-/cordova-common-2.0.1.tgz",
+  "_shasum": "99af318d7cb8988047cfe37bb9f25ea881d52815",
+  "_shrinkwrap": null,
+  "_spec": "cordova-common@^2.0.1",
+  "_where": "/Users/steveng/repo/cordova/cordova-android",
+  "author": {
+    "name": "Apache Software Foundation"
+  },
+  "bugs": {
+    "url": "https://issues.apache.org/jira/browse/CB",
+    "email": "dev@cordova.apache.org"
+  },
+  "contributors": [],
+  "dependencies": {
+    "ansi": "^0.3.1",
+    "bplist-parser": "^0.1.0",
+    "cordova-registry-mapper": "^1.1.8",
+    "elementtree": "^0.1.6",
+    "glob": "^5.0.13",
+    "minimatch": "^3.0.0",
+    "osenv": "^0.1.3",
+    "plist": "^1.2.0",
+    "q": "^1.4.1",
+    "semver": "^5.0.1",
+    "shelljs": "^0.5.3",
+    "underscore": "^1.8.3",
+    "unorm": "^1.3.3"
+  },
+  "description": "Apache Cordova tools and platforms shared routines",
+  "devDependencies": {
+    "istanbul": "^0.4.5",
+    "jasmine": "^2.5.2",
+    "jshint": "^2.8.0",
+    "promise-matchers": "^0.9.6",
+    "rewire": "^2.5.1"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "99af318d7cb8988047cfe37bb9f25ea881d52815",
+    "tarball": "https://registry.npmjs.org/cordova-common/-/cordova-common-2.0.1.tgz"
+  },
+  "engines": {
+    "node": ">=0.9.9"
+  },
+  "license": "Apache-2.0",
+  "main": "cordova-common.js",
+  "maintainers": [
+    {
+      "name": "bowserj",
+      "email": "bowserj@apache.org"
+    },
+    {
+      "name": "filmaj",
+      "email": "maj.fil@gmail.com"
+    },
+    {
+      "name": "kotikov.vladimir",
+      "email": "kotikov.vladimir@gmail.com"
+    },
+    {
+      "name": "purplecabbage",
+      "email": "purplecabbage@gmail.com"
+    },
+    {
+      "name": "shazron",
+      "email": "shazron@gmail.com"
+    },
+    {
+      "name": "stevegill",
+      "email": "stevengill97@gmail.com"
+    },
+    {
+      "name": "timbarham",
+      "email": "npmjs@barhams.info"
+    }
+  ],
+  "name": "cordova-common",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git://git-wip-us.apache.org/repos/asf/cordova-common.git"
+  },
+  "scripts": {
+    "cover": "istanbul cover --root src --print detail jasmine",
+    "jasmine": "jasmine --captureExceptions --color",
+    "jshint": "jshint src && jshint spec",
+    "test": "npm run jshint && npm run jasmine"
+  },
+  "version": "2.0.1"
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/.jshintrc
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/.jshintrc b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/.jshintrc
new file mode 100644
index 0000000..89a121c
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/.jshintrc
@@ -0,0 +1,10 @@
+{
+    "node": true
+  , "bitwise": true
+  , "undef": true
+  , "trailing": true
+  , "quotmark": true
+  , "indent": 4
+  , "unused": "vars"
+  , "latedef": "nofunc"
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ActionStack.js
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ActionStack.js b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ActionStack.js
new file mode 100644
index 0000000..5ef6f84
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ActionStack.js
@@ -0,0 +1,85 @@
+/**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+
+/* jshint quotmark:false */
+
+var events = require('./events'),
+    Q = require('q');
+
+function ActionStack() {
+    this.stack = [];
+    this.completed = [];
+}
+
+ActionStack.prototype = {
+    createAction:function(handler, action_params, reverter, revert_params) {
+        return {
+            handler:{
+                run:handler,
+                params:action_params
+            },
+            reverter:{
+                run:reverter,
+                params:revert_params
+            }
+        };
+    },
+    push:function(tx) {
+        this.stack.push(tx);
+    },
+    // Returns a promise.
+    process:function(platform) {
+        events.emit('verbose', 'Beginning processing of action stack for ' + platform + ' project...');
+
+        while (this.stack.length) {
+            var action = this.stack.shift();
+            var handler = action.handler.run;
+            var action_params = action.handler.params;
+
+            try {
+                handler.apply(null, action_params);
+            } catch(e) {
+                events.emit('warn', 'Error during processing of action! Attempting to revert...');
+                this.stack.unshift(action);
+                var issue = 'Uh oh!\n';
+                // revert completed tasks
+                while(this.completed.length) {
+                    var undo = this.completed.shift();
+                    var revert = undo.reverter.run;
+                    var revert_params = undo.reverter.params;
+
+                    try {
+                        revert.apply(null, revert_params);
+                    } catch(err) {
+                        events.emit('warn', 'Error during reversion of action! We probably really messed up your project now, sorry! D:');
+                        issue += 'A reversion action failed: ' + err.message + '\n';
+                    }
+                }
+                e.message = issue + e.message;
+                return Q.reject(e);
+            }
+            this.completed.push(action);
+        }
+        events.emit('verbose', 'Action stack processing complete.');
+
+        return Q();
+    }
+};
+
+module.exports = ActionStack;

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigChanges.js
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigChanges.js b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigChanges.js
new file mode 100644
index 0000000..4a58132
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigChanges.js
@@ -0,0 +1,431 @@
+/**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/ 
+
+/*
+ * This module deals with shared configuration / dependency "stuff". That is:
+ * - XML configuration files such as config.xml, AndroidManifest.xml or WMAppManifest.xml.
+ * - plist files in iOS
+ * Essentially, any type of shared resources that we need to handle with awareness
+ * of how potentially multiple plugins depend on a single shared resource, should be
+ * handled in this module.
+ *
+ * The implementation uses an object as a hash table, with "leaves" of the table tracking
+ * reference counts.
+ */
+
+/* jshint sub:true */
+
+var path = require('path'),
+    et   = require('elementtree'),
+    ConfigKeeper = require('./ConfigKeeper'),
+    CordovaLogger = require('../CordovaLogger');
+
+var mungeutil = require('./munge-util');
+var xml_helpers = require('../util/xml-helpers');
+
+exports.PlatformMunger = PlatformMunger;
+
+exports.process = function(plugins_dir, project_dir, platform, platformJson, pluginInfoProvider) {
+    var munger = new PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider);
+    munger.process(plugins_dir);
+    munger.save_all();
+};
+
+/******************************************************************************
+* PlatformMunger class
+*
+* Can deal with config file of a single project.
+* Parsed config files are cached in a ConfigKeeper object.
+******************************************************************************/
+function PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider) {
+    this.platform = platform;
+    this.project_dir = project_dir;
+    this.config_keeper = new ConfigKeeper(project_dir);
+    this.platformJson = platformJson;
+    this.pluginInfoProvider = pluginInfoProvider;
+}
+
+// Write out all unsaved files.
+PlatformMunger.prototype.save_all = PlatformMunger_save_all;
+function PlatformMunger_save_all() {
+    this.config_keeper.save_all();
+    this.platformJson.save();
+}
+
+// Apply a munge object to a single config file.
+// The remove parameter tells whether to add the change or remove it.
+PlatformMunger.prototype.apply_file_munge = PlatformMunger_apply_file_munge;
+function PlatformMunger_apply_file_munge(file, munge, remove) {
+    var self = this;
+
+    for (var selector in munge.parents) {
+        for (var xml_child in munge.parents[selector]) {
+            // this xml child is new, graft it (only if config file exists)
+            var config_file = self.config_keeper.get(self.project_dir, self.platform, file);
+            if (config_file.exists) {
+                if (remove) config_file.prune_child(selector, munge.parents[selector][xml_child]);
+                else config_file.graft_child(selector, munge.parents[selector][xml_child]);
+            }
+        }
+    }
+}
+
+
+PlatformMunger.prototype.remove_plugin_changes = remove_plugin_changes;
+function remove_plugin_changes(pluginInfo, is_top_level) {
+    var self = this;
+    var platform_config = self.platformJson.root;
+    var plugin_vars = is_top_level ?
+        platform_config.installed_plugins[pluginInfo.id] :
+        platform_config.dependent_plugins[pluginInfo.id];
+    var edit_config_changes = null;
+    if(pluginInfo.getEditConfigs) {
+        edit_config_changes = pluginInfo.getEditConfigs(self.platform);
+    }
+
+    // get config munge, aka how did this plugin change various config files
+    var config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars, edit_config_changes);
+    // global munge looks at all plugins' changes to config files
+    var global_munge = platform_config.config_munge;
+    var munge = mungeutil.decrement_munge(global_munge, config_munge);
+
+    for (var file in munge.files) {
+        self.apply_file_munge(file, munge.files[file], /* remove = */ true);
+    }
+
+    // Remove from installed_plugins
+    self.platformJson.removePlugin(pluginInfo.id, is_top_level);
+    return self;
+}
+
+
+PlatformMunger.prototype.add_plugin_changes = add_plugin_changes;
+function add_plugin_changes(pluginInfo, plugin_vars, is_top_level, should_increment, plugin_force) {
+    var self = this;
+    var platform_config = self.platformJson.root;
+
+    var edit_config_changes = null;
+    if(pluginInfo.getEditConfigs) {
+        edit_config_changes = pluginInfo.getEditConfigs(self.platform);
+    }
+
+    var config_munge;
+
+    if (!edit_config_changes || edit_config_changes.length === 0) {
+        // get config munge, aka how should this plugin change various config files
+        config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars);
+    }
+    else {
+        var isConflictingInfo = is_conflicting(edit_config_changes, platform_config.config_munge, self, plugin_force);
+
+        if (isConflictingInfo.conflictWithConfigxml) {
+            throw new Error(pluginInfo.id +
+                ' cannot be added. <edit-config> changes in this plugin conflicts with <edit-config> changes in config.xml. Conflicts must be resolved before plugin can be added.');
+        }
+        if (plugin_force) {
+            CordovaLogger.get().log(CordovaLogger.WARN, '--force is used. edit-config will overwrite conflicts if any. Conflicting plugins may not work as expected.');
+
+            // remove conflicting munges
+            var conflict_munge = mungeutil.decrement_munge(platform_config.config_munge, isConflictingInfo.conflictingMunge);
+            for (var conflict_file in conflict_munge.files) {
+                self.apply_file_munge(conflict_file, conflict_munge.files[conflict_file], /* remove = */ true);
+            }
+
+            // force add new munges
+            config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars, edit_config_changes);
+        }
+        else if(isConflictingInfo.conflictFound) {
+            throw new Error('There was a conflict trying to modify attributes with <edit-config> in plugin ' + pluginInfo.id +
+            '. The conflicting plugin, ' + isConflictingInfo.conflictingPlugin + ', already modified the same attributes. The conflict must be resolved before ' +
+            pluginInfo.id + ' can be added. You may use --force to add the plugin and overwrite the conflicting attributes.');
+        }
+        else {
+            // no conflicts, will handle edit-config
+            config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars, edit_config_changes);
+        }
+    }
+
+    self = munge_helper(should_increment, self, platform_config, config_munge);
+
+    // Move to installed/dependent_plugins
+    self.platformJson.addPlugin(pluginInfo.id, plugin_vars || {}, is_top_level);
+    return self;
+}
+
+
+// Handle edit-config changes from config.xml
+PlatformMunger.prototype.add_config_changes = add_config_changes;
+function add_config_changes(config, should_increment) {
+    var self = this;
+    var platform_config = self.platformJson.root;
+
+    var config_munge;
+    var edit_config_changes = null;
+    if(config.getEditConfigs) {
+        edit_config_changes = config.getEditConfigs(self.platform);
+    }
+
+    if (!edit_config_changes || edit_config_changes.length === 0) {
+        // There are no edit-config changes to add, return here
+        return self;
+    }
+    else {
+        var isConflictingInfo = is_conflicting(edit_config_changes, platform_config.config_munge, self, true /*always force overwrite other edit-config*/);
+
+        if(isConflictingInfo.conflictFound) {
+            var conflict_munge;
+            var conflict_file;
+
+            if (Object.keys(isConflictingInfo.configxmlMunge.files).length !== 0) {
+                // silently remove conflicting config.xml munges so new munges can be added
+                conflict_munge = mungeutil.decrement_munge(platform_config.config_munge, isConflictingInfo.configxmlMunge);
+                for (conflict_file in conflict_munge.files) {
+                    self.apply_file_munge(conflict_file, conflict_munge.files[conflict_file], /* remove = */ true);
+                }
+            }
+            if (Object.keys(isConflictingInfo.conflictingMunge.files).length !== 0) {
+                CordovaLogger.get().log(CordovaLogger.WARN, 'Conflict found, edit-config changes from config.xml will overwrite plugin.xml changes');
+
+                // remove conflicting plugin.xml munges
+                conflict_munge = mungeutil.decrement_munge(platform_config.config_munge, isConflictingInfo.conflictingMunge);
+                for (conflict_file in conflict_munge.files) {
+                    self.apply_file_munge(conflict_file, conflict_munge.files[conflict_file], /* remove = */ true);
+                }
+            }
+        }
+        // Add config.xml edit-config munges
+        config_munge = self.generate_config_xml_munge(config, edit_config_changes, 'config.xml');
+    }
+
+    self = munge_helper(should_increment, self, platform_config, config_munge);
+
+    // Move to installed/dependent_plugins
+    return self;
+}
+
+function munge_helper(should_increment, self, platform_config, config_munge) {
+    // global munge looks at all changes to config files
+
+    // TODO: The should_increment param is only used by cordova-cli and is going away soon.
+    // If should_increment is set to false, avoid modifying the global_munge (use clone)
+    // and apply the entire config_munge because it's already a proper subset of the global_munge.
+    var munge, global_munge;
+    if (should_increment) {
+        global_munge = platform_config.config_munge;
+        munge = mungeutil.increment_munge(global_munge, config_munge);
+    } else {
+        global_munge = mungeutil.clone_munge(platform_config.config_munge);
+        munge = config_munge;
+    }
+
+    for (var file in munge.files) {
+        self.apply_file_munge(file, munge.files[file]);
+    }
+
+    return self;
+}
+
+
+// Load the global munge from platform json and apply all of it.
+// Used by cordova prepare to re-generate some config file from platform
+// defaults and the global munge.
+PlatformMunger.prototype.reapply_global_munge = reapply_global_munge ;
+function reapply_global_munge () {
+    var self = this;
+
+    var platform_config = self.platformJson.root;
+    var global_munge = platform_config.config_munge;
+    for (var file in global_munge.files) {
+        self.apply_file_munge(file, global_munge.files[file]);
+    }
+
+    return self;
+}
+
+// generate_plugin_config_munge
+// Generate the munge object from config.xml
+PlatformMunger.prototype.generate_config_xml_munge = generate_config_xml_munge;
+function generate_config_xml_munge(config, edit_config_changes, type) {
+
+    var munge = { files: {} };
+    var changes = edit_config_changes;
+    var id;
+
+    if(!changes) {
+        return munge;
+    }
+
+    if (type === 'config.xml') {
+        id = type;
+    }
+    else {
+        id = config.id;
+    }
+
+    changes.forEach(function(change) {
+        change.xmls.forEach(function(xml) {
+            // 1. stringify each xml
+            var stringified = (new et.ElementTree(xml)).write({xml_declaration:false});
+            // 2. add into munge
+            if (change.mode) {
+                mungeutil.deep_add(munge, change.file, change.target, { xml: stringified, count: 1, mode: change.mode, id: id });
+            }
+        });
+    });
+    return munge;
+}
+
+
+// generate_plugin_config_munge
+// Generate the munge object from plugin.xml + vars
+PlatformMunger.prototype.generate_plugin_config_munge = generate_plugin_config_munge;
+function generate_plugin_config_munge(pluginInfo, vars, edit_config_changes) {
+    var self = this;
+
+    vars = vars || {};
+    var munge = { files: {} };
+    var changes = pluginInfo.getConfigFiles(self.platform);
+
+    if(edit_config_changes) {
+        Array.prototype.push.apply(changes, edit_config_changes);
+    }
+
+    changes.forEach(function(change) {
+        change.xmls.forEach(function(xml) {
+            // 1. stringify each xml
+            var stringified = (new et.ElementTree(xml)).write({xml_declaration:false});
+            // interp vars
+            if (vars) {
+                Object.keys(vars).forEach(function(key) {
+                    var regExp = new RegExp('\\$' + key, 'g');
+                    stringified = stringified.replace(regExp, vars[key]);
+                });
+            }
+            // 2. add into munge
+            if (change.mode) {
+                if (change.mode !== 'remove') {
+                    mungeutil.deep_add(munge, change.file, change.target, { xml: stringified, count: 1, mode: change.mode, plugin: pluginInfo.id });
+                }
+            }
+            else {
+                mungeutil.deep_add(munge, change.target, change.parent, { xml: stringified, count: 1, after: change.after });
+            }
+        });
+    });
+    return munge;
+}
+
+function is_conflicting(editchanges, config_munge, self, force) {
+    var files = config_munge.files;
+    var conflictFound = false;
+    var conflictWithConfigxml = false;
+    var conflictingMunge = { files: {} };
+    var configxmlMunge = { files: {} };
+    var conflictingParent;
+    var conflictingPlugin;
+
+    editchanges.forEach(function(editchange) {
+        if (files[editchange.file]) {
+            var parents = files[editchange.file].parents;
+            var target = parents[editchange.target];
+
+            // Check if the edit target will resolve to an existing target
+            if (!target || target.length === 0) {
+                var file_xml = self.config_keeper.get(self.project_dir, self.platform, editchange.file).data;
+                var resolveEditTarget = xml_helpers.resolveParent(file_xml, editchange.target);
+                var resolveTarget;
+
+                if (resolveEditTarget) {
+                    for (var parent in parents) {
+                        resolveTarget = xml_helpers.resolveParent(file_xml, parent);
+                        if (resolveEditTarget === resolveTarget) {
+                            conflictingParent = parent;
+                            target = parents[parent];
+                            break;
+                        }
+                    }
+                }
+            }
+            else {
+                conflictingParent = editchange.target;
+            }
+
+            if (target && target.length !== 0) {
+                // conflict has been found
+                conflictFound = true;
+
+                if (editchange.id === 'config.xml') {
+                    if (target[0].id === 'config.xml') {
+                        // Keep track of config.xml/config.xml edit-config conflicts
+                        mungeutil.deep_add(configxmlMunge, editchange.file, conflictingParent, target[0]);
+                    }
+                    else {
+                        // Keep track of config.xml x plugin.xml edit-config conflicts
+                        mungeutil.deep_add(conflictingMunge, editchange.file, conflictingParent, target[0]);
+                    }
+                }
+                else {
+                    if (target[0].id === 'config.xml') {
+                        // plugin.xml cannot overwrite config.xml changes even if --force is used
+                        conflictWithConfigxml = true;
+                        return;
+                    }
+
+                    if (force) {
+                        // Need to find all conflicts when --force is used, track conflicting munges
+                        mungeutil.deep_add(conflictingMunge, editchange.file, conflictingParent, target[0]);
+                    }
+                    else {
+                        // plugin cannot overwrite other plugin changes without --force
+                        conflictingPlugin = target[0].plugin;
+                        return;
+                    }
+                }
+            }
+        }
+    });
+
+    return {conflictFound: conflictFound, conflictingPlugin: conflictingPlugin, conflictingMunge: conflictingMunge,
+        configxmlMunge: configxmlMunge, conflictWithConfigxml:conflictWithConfigxml};
+}
+
+// Go over the prepare queue and apply the config munges for each plugin
+// that has been (un)installed.
+PlatformMunger.prototype.process = PlatformMunger_process;
+function PlatformMunger_process(plugins_dir) {
+    var self = this;
+    var platform_config = self.platformJson.root;
+
+    // Uninstallation first
+    platform_config.prepare_queue.uninstalled.forEach(function(u) {
+        var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin));
+        self.remove_plugin_changes(pluginInfo, u.topLevel);
+    });
+
+    // Now handle installation
+    platform_config.prepare_queue.installed.forEach(function(u) {
+        var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin));
+        self.add_plugin_changes(pluginInfo, u.vars, u.topLevel, true, u.force);
+    });
+
+    // Empty out installed/ uninstalled queues.
+    platform_config.prepare_queue.uninstalled = [];
+    platform_config.prepare_queue.installed = [];
+}
+/**** END of PlatformMunger ****/

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigFile.js
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigFile.js b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigFile.js
new file mode 100644
index 0000000..4a58008
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigFile.js
@@ -0,0 +1,240 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+var fs = require('fs');
+var path = require('path');
+
+var modules = {};
+var addProperty = require('../util/addProperty');
+
+// Use delay loading to ensure plist and other node modules to not get loaded
+// on Android, Windows platforms
+addProperty(module, 'bplist', 'bplist-parser', modules);
+addProperty(module, 'et', 'elementtree', modules);
+addProperty(module, 'glob', 'glob', modules);
+addProperty(module, 'plist', 'plist', modules);
+addProperty(module, 'plist_helpers', '../util/plist-helpers', modules);
+addProperty(module, 'xml_helpers', '../util/xml-helpers', modules);
+
+/******************************************************************************
+* ConfigFile class
+*
+* Can load and keep various types of config files. Provides some functionality
+* specific to some file types such as grafting XML children. In most cases it
+* should be instantiated by ConfigKeeper.
+*
+* For plugin.xml files use as:
+* plugin_config = self.config_keeper.get(plugin_dir, '', 'plugin.xml');
+*
+* TODO: Consider moving it out to a separate file and maybe partially with
+* overrides in platform handlers.
+******************************************************************************/
+function ConfigFile(project_dir, platform, file_tag) {
+    this.project_dir = project_dir;
+    this.platform = platform;
+    this.file_tag = file_tag;
+    this.is_changed = false;
+
+    this.load();
+}
+
+// ConfigFile.load()
+ConfigFile.prototype.load = ConfigFile_load;
+function ConfigFile_load() {
+    var self = this;
+
+    // config file may be in a place not exactly specified in the target
+    var filepath = self.filepath = resolveConfigFilePath(self.project_dir, self.platform, self.file_tag);
+
+    if ( !filepath || !fs.existsSync(filepath) ) {
+        self.exists = false;
+        return;
+    }
+    self.exists = true;
+    self.mtime = fs.statSync(self.filepath).mtime;
+
+    var ext = path.extname(filepath);
+    // Windows8 uses an appxmanifest, and wp8 will likely use
+    // the same in a future release
+    if (ext == '.xml' || ext == '.appxmanifest') {
+        self.type = 'xml';
+        self.data = modules.xml_helpers.parseElementtreeSync(filepath);
+    } else {
+        // plist file
+        self.type = 'plist';
+        // TODO: isBinaryPlist() reads the file and then parse re-reads it again.
+        //       We always write out text plist, not binary.
+        //       Do we still need to support binary plist?
+        //       If yes, use plist.parseStringSync() and read the file once.
+        self.data = isBinaryPlist(filepath) ?
+                modules.bplist.parseBuffer(fs.readFileSync(filepath)) :
+                modules.plist.parse(fs.readFileSync(filepath, 'utf8'));
+    }
+}
+
+ConfigFile.prototype.save = function ConfigFile_save() {
+    var self = this;
+    if (self.type === 'xml') {
+        fs.writeFileSync(self.filepath, self.data.write({indent: 4}), 'utf-8');
+    } else {
+        // plist
+        var regExp = new RegExp('<string>[ \t\r\n]+?</string>', 'g');
+        fs.writeFileSync(self.filepath, modules.plist.build(self.data).replace(regExp, '<string></string>'));
+    }
+    self.is_changed = false;
+};
+
+ConfigFile.prototype.graft_child = function ConfigFile_graft_child(selector, xml_child) {
+    var self = this;
+    var filepath = self.filepath;
+    var result;
+    if (self.type === 'xml') {
+        var xml_to_graft = [modules.et.XML(xml_child.xml)];
+        switch (xml_child.mode) {
+            case 'merge':
+                result = modules.xml_helpers.graftXMLMerge(self.data, xml_to_graft, selector, xml_child);
+                break;
+            case 'overwrite':
+                result = modules.xml_helpers.graftXMLOverwrite(self.data, xml_to_graft, selector, xml_child);
+                break;
+            case 'remove':
+                result= true;
+                break;
+            default:
+                result = modules.xml_helpers.graftXML(self.data, xml_to_graft, selector, xml_child.after);
+        }
+        if ( !result) {
+            throw new Error('Unable to graft xml at selector "' + selector + '" from "' + filepath + '" during config install');
+        }
+    } else {
+        // plist file
+        result = modules.plist_helpers.graftPLIST(self.data, xml_child.xml, selector);
+        if ( !result ) {
+            throw new Error('Unable to graft plist "' + filepath + '" during config install');
+        }
+    }
+    self.is_changed = true;
+};
+
+ConfigFile.prototype.prune_child = function ConfigFile_prune_child(selector, xml_child) {
+    var self = this;
+    var filepath = self.filepath;
+    var result;
+    if (self.type === 'xml') {
+        var xml_to_graft = [modules.et.XML(xml_child.xml)];
+        switch (xml_child.mode) {
+            case 'merge':
+            case 'overwrite':
+                result = modules.xml_helpers.pruneXMLRestore(self.data, selector, xml_child);
+                break;
+            case 'remove':
+                result = modules.xml_helpers.prunXMLRemove(self.data, selector, xml_to_graft);
+                break;
+            default:
+                result = modules.xml_helpers.pruneXML(self.data, xml_to_graft, selector);
+        }
+    } else {
+        // plist file
+        result = modules.plist_helpers.prunePLIST(self.data, xml_child.xml, selector);
+    }
+    if (!result) {
+        var err_msg = 'Pruning at selector "' + selector + '" from "' + filepath + '" went bad.';
+        throw new Error(err_msg);
+    }
+    self.is_changed = true;
+};
+
+// Some config-file target attributes are not qualified with a full leading directory, or contain wildcards.
+// Resolve to a real path in this function.
+// TODO: getIOSProjectname is slow because of glob, try to avoid calling it several times per project.
+function resolveConfigFilePath(project_dir, platform, file) {
+    var filepath = path.join(project_dir, file);
+    var matches;
+
+    if (file.indexOf('*') > -1) {
+        // handle wildcards in targets using glob.
+        matches = modules.glob.sync(path.join(project_dir, '**', file));
+        if (matches.length) filepath = matches[0];
+
+        // [CB-5989] multiple Info.plist files may exist. default to $PROJECT_NAME-Info.plist
+        if(matches.length > 1 && file.indexOf('-Info.plist')>-1){
+            var plistName =  getIOSProjectname(project_dir)+'-Info.plist';
+            for (var i=0; i < matches.length; i++) {
+                if(matches[i].indexOf(plistName) > -1){
+                    filepath = matches[i];
+                    break;
+                }
+            }
+        }
+        return filepath;
+    }
+
+    // special-case config.xml target that is just "config.xml". This should be resolved to the real location of the file.
+    // TODO: move the logic that contains the locations of config.xml from cordova CLI into plugman.
+    if (file == 'config.xml') {
+        if (platform == 'ubuntu') {
+            filepath = path.join(project_dir, 'config.xml');
+        } else if (platform == 'ios') {
+            var iospath = getIOSProjectname(project_dir);
+            filepath = path.join(project_dir,iospath, 'config.xml');
+        } else if (platform == 'android') {
+            filepath = path.join(project_dir, 'res', 'xml', 'config.xml');
+        } else {
+            matches = modules.glob.sync(path.join(project_dir, '**', 'config.xml'));
+            if (matches.length) filepath = matches[0];
+        }
+        return filepath;
+    }
+
+    // XXX this checks for android studio projects
+    // only if none of the options above are satisfied does this get called
+    if(platform === 'android' && !fs.existsSync(filepath)) {
+      filepath = path.join(project_dir, 'app', 'src', 'main', 'res', 'xml', 'config.xml');
+    }
+
+    // None of the special cases matched, returning project_dir/file.
+    return filepath;
+}
+
+// Find out the real name of an iOS project
+// TODO: glob is slow, need a better way or caching, or avoid using more than once.
+function getIOSProjectname(project_dir) {
+    var matches = modules.glob.sync(path.join(project_dir, '*.xcodeproj'));
+    var iospath;
+    if (matches.length === 1) {
+        iospath = path.basename(matches[0],'.xcodeproj');
+    } else {
+        var msg;
+        if (matches.length === 0) {
+            msg = 'Does not appear to be an xcode project, no xcode project file in ' + project_dir;
+        } else {
+            msg = 'There are multiple *.xcodeproj dirs in ' + project_dir;
+        }
+        throw new Error(msg);
+    }
+    return iospath;
+}
+
+// determine if a plist file is binary
+function isBinaryPlist(filename) {
+    // I wish there was a synchronous way to read only the first 6 bytes of a
+    // file. This is wasteful :/
+    var buf = '' + fs.readFileSync(filename, 'utf8');
+    // binary plists start with a magic header, "bplist"
+    return buf.substring(0, 6) === 'bplist';
+}
+
+module.exports = ConfigFile;

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigKeeper.js
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigKeeper.js b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigKeeper.js
new file mode 100644
index 0000000..894e922
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/ConfigKeeper.js
@@ -0,0 +1,65 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+/* jshint sub:true */
+
+var path = require('path');
+var ConfigFile = require('./ConfigFile');
+
+/******************************************************************************
+* ConfigKeeper class
+*
+* Used to load and store config files to avoid re-parsing and writing them out
+* multiple times.
+*
+* The config files are referred to by a fake path constructed as
+* project_dir/platform/file
+* where file is the name used for the file in config munges.
+******************************************************************************/
+function ConfigKeeper(project_dir, plugins_dir) {
+    this.project_dir = project_dir;
+    this.plugins_dir = plugins_dir;
+    this._cached = {};
+}
+
+ConfigKeeper.prototype.get = function ConfigKeeper_get(project_dir, platform, file) {
+    var self = this;
+
+    // This fixes a bug with older plugins - when specifying config xml instead of res/xml/config.xml
+    // https://issues.apache.org/jira/browse/CB-6414
+    if(file == 'config.xml' && platform == 'android'){
+        file = 'res/xml/config.xml';
+    }
+    var fake_path = path.join(project_dir, platform, file);
+
+    if (self._cached[fake_path]) {
+        return self._cached[fake_path];
+    }
+    // File was not cached, need to load.
+    var config_file = new ConfigFile(project_dir, platform, file);
+    self._cached[fake_path] = config_file;
+    return config_file;
+};
+
+
+ConfigKeeper.prototype.save_all = function ConfigKeeper_save_all() {
+    var self = this;
+    Object.keys(self._cached).forEach(function (fake_path) {
+        var config_file = self._cached[fake_path];
+        if (config_file.is_changed) config_file.save();
+    });
+};
+
+module.exports = ConfigKeeper;

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/munge-util.js
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/munge-util.js b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/munge-util.js
new file mode 100644
index 0000000..0149bab
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigChanges/munge-util.js
@@ -0,0 +1,163 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+/* jshint sub:true  */
+
+var _ = require('underscore');
+
+// add the count of [key1][key2]...[keyN] to obj
+// return true if it didn't exist before
+exports.deep_add = function deep_add(obj, keys /* or key1, key2 .... */ ) {
+    if ( !Array.isArray(keys) ) {
+        keys = Array.prototype.slice.call(arguments, 1);
+    }
+
+    return exports.process_munge(obj, true/*createParents*/, function (parentArray, k) {
+        var found = _.find(parentArray, function(element) {
+            return element.xml == k.xml;
+        });
+        if (found) {
+            found.after = found.after || k.after;
+            found.count += k.count;
+        } else {
+            parentArray.push(k);
+        }
+        return !found;
+    }, keys);
+};
+
+// decrement the count of [key1][key2]...[keyN] from obj and remove if it reaches 0
+// return true if it was removed or not found
+exports.deep_remove = function deep_remove(obj, keys /* or key1, key2 .... */ ) {
+    if ( !Array.isArray(keys) ) {
+        keys = Array.prototype.slice.call(arguments, 1);
+    }
+
+    var result = exports.process_munge(obj, false/*createParents*/, function (parentArray, k) {
+        var index = -1;
+        var found = _.find(parentArray, function (element) {
+            index++;
+            return element.xml == k.xml;
+        });
+        if (found) {
+            if (parentArray[index].oldAttrib) {
+                k.oldAttrib = _.extend({}, parentArray[index].oldAttrib);
+            }
+            found.count -= k.count;
+            if (found.count > 0) {
+                return false;
+            }
+            else {
+                parentArray.splice(index, 1);
+            }
+        }
+        return undefined;
+    }, keys);
+
+    return typeof result === 'undefined' ? true : result;
+};
+
+// search for [key1][key2]...[keyN]
+// return the object or undefined if not found
+exports.deep_find = function deep_find(obj, keys /* or key1, key2 .... */ ) {
+    if ( !Array.isArray(keys) ) {
+        keys = Array.prototype.slice.call(arguments, 1);
+    }
+
+    return exports.process_munge(obj, false/*createParents?*/, function (parentArray, k) {
+        return _.find(parentArray, function (element) {
+            return element.xml == (k.xml || k);
+        });
+    }, keys);
+};
+
+// Execute func passing it the parent array and the xmlChild key.
+// When createParents is true, add the file and parent items  they are missing
+// When createParents is false, stop and return undefined if the file and/or parent items are missing
+
+exports.process_munge = function process_munge(obj, createParents, func, keys /* or key1, key2 .... */ ) {
+    if ( !Array.isArray(keys) ) {
+        keys = Array.prototype.slice.call(arguments, 1);
+    }
+    var k = keys[0];
+    if (keys.length == 1) {
+        return func(obj, k);
+    } else if (keys.length == 2) {
+        if (!obj.parents[k] && !createParents) {
+            return undefined;
+        }
+        obj.parents[k] = obj.parents[k] || [];
+        return exports.process_munge(obj.parents[k], createParents, func, keys.slice(1));
+    } else if (keys.length == 3){
+        if (!obj.files[k] && !createParents) {
+            return undefined;
+        }
+        obj.files[k] = obj.files[k] || { parents: {} };
+        return exports.process_munge(obj.files[k], createParents, func, keys.slice(1));
+    } else {
+        throw new Error('Invalid key format. Must contain at most 3 elements (file, parent, xmlChild).');
+    }
+};
+
+// All values from munge are added to base as
+// base[file][selector][child] += munge[file][selector][child]
+// Returns a munge object containing values that exist in munge
+// but not in base.
+exports.increment_munge = function increment_munge(base, munge) {
+    var diff = { files: {} };
+
+    for (var file in munge.files) {
+        for (var selector in munge.files[file].parents) {
+            for (var xml_child in munge.files[file].parents[selector]) {
+                var val = munge.files[file].parents[selector][xml_child];
+                // if node not in base, add it to diff and base
+                // else increment it's value in base without adding to diff
+                var newlyAdded = exports.deep_add(base, [file, selector, val]);
+                if (newlyAdded) {
+                    exports.deep_add(diff, file, selector, val);
+                }
+            }
+        }
+    }
+    return diff;
+};
+
+// Update the base munge object as
+// base[file][selector][child] -= munge[file][selector][child]
+// nodes that reached zero value are removed from base and added to the returned munge
+// object.
+exports.decrement_munge = function decrement_munge(base, munge) {
+    var zeroed = { files: {} };
+
+    for (var file in munge.files) {
+        for (var selector in munge.files[file].parents) {
+            for (var xml_child in munge.files[file].parents[selector]) {
+                var val = munge.files[file].parents[selector][xml_child];
+                // if node not in base, add it to diff and base
+                // else increment it's value in base without adding to diff
+                var removed = exports.deep_remove(base, [file, selector, val]);
+                if (removed) {
+                    exports.deep_add(zeroed, file, selector, val);
+                }
+            }
+        }
+    }
+    return zeroed;
+};
+
+// For better readability where used
+exports.clone_munge = function clone_munge(munge) {
+    return exports.increment_munge({}, munge);
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js
new file mode 100644
index 0000000..e477a89
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js
@@ -0,0 +1,568 @@
+/**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+
+/* jshint sub:true */
+
+var et = require('elementtree'),
+    xml= require('../util/xml-helpers'),
+    CordovaError = require('../CordovaError/CordovaError'),
+    fs = require('fs'),
+    events = require('../events');
+
+
+/** Wraps a config.xml file */
+function ConfigParser(path) {
+    this.path = path;
+    try {
+        this.doc = xml.parseElementtreeSync(path);
+        this.cdvNamespacePrefix = getCordovaNamespacePrefix(this.doc);
+        et.register_namespace(this.cdvNamespacePrefix, 'http://cordova.apache.org/ns/1.0');
+    } catch (e) {
+        console.error('Parsing '+path+' failed');
+        throw e;
+    }
+    var r = this.doc.getroot();
+    if (r.tag !== 'widget') {
+        throw new CordovaError(path + ' has incorrect root node name (expected "widget", was "' + r.tag + '")');
+    }
+}
+
+function getNodeTextSafe(el) {
+    return el && el.text && el.text.trim();
+}
+
+function findOrCreate(doc, name) {
+    var ret = doc.find(name);
+    if (!ret) {
+        ret = new et.Element(name);
+        doc.getroot().append(ret);
+    }
+    return ret;
+}
+
+function getCordovaNamespacePrefix(doc){
+    var rootAtribs = Object.getOwnPropertyNames(doc.getroot().attrib);
+    var prefix = 'cdv';
+    for (var j = 0; j < rootAtribs.length; j++ ) {
+        if(rootAtribs[j].indexOf('xmlns:') === 0 &&
+            doc.getroot().attrib[rootAtribs[j]] === 'http://cordova.apache.org/ns/1.0'){
+            var strings = rootAtribs[j].split(':');
+            prefix = strings[1];
+            break;
+        }
+    }
+    return prefix;
+}
+
+/**
+ * Finds the value of an element's attribute
+ * @param  {String} attributeName Name of the attribute to search for
+ * @param  {Array}  elems         An array of ElementTree nodes
+ * @return {String}
+ */
+function findElementAttributeValue(attributeName, elems) {
+
+    elems = Array.isArray(elems) ? elems : [ elems ];
+
+    var value = elems.filter(function (elem) {
+        return elem.attrib.name.toLowerCase() === attributeName.toLowerCase();
+    }).map(function (filteredElems) {
+        return filteredElems.attrib.value;
+    }).pop();
+
+    return value ? value : '';
+}
+
+ConfigParser.prototype = {
+    getAttribute: function(attr) {
+        return this.doc.getroot().attrib[attr];
+    },
+
+    packageName: function(id) {
+        return this.getAttribute('id');
+    },
+    setPackageName: function(id) {
+        this.doc.getroot().attrib['id'] = id;
+    },
+    android_packageName: function() {
+        return this.getAttribute('android-packageName');
+    },
+    android_activityName: function() {
+        return this.getAttribute('android-activityName');
+    },
+    ios_CFBundleIdentifier: function() {
+        return this.getAttribute('ios-CFBundleIdentifier');
+    },
+    name: function() {
+        return getNodeTextSafe(this.doc.find('name'));
+    },
+    setName: function(name) {
+        var el = findOrCreate(this.doc, 'name');
+        el.text = name;
+    },
+    description: function() {
+        return getNodeTextSafe(this.doc.find('description'));
+    },
+    setDescription: function(text) {
+        var el = findOrCreate(this.doc, 'description');
+        el.text = text;
+    },
+    version: function() {
+        return this.getAttribute('version');
+    },
+    windows_packageVersion: function() {
+        return this.getAttribute('windows-packageVersion');
+    },
+    android_versionCode: function() {
+        return this.getAttribute('android-versionCode');
+    },
+    ios_CFBundleVersion: function() {
+        return this.getAttribute('ios-CFBundleVersion');
+    },
+    setVersion: function(value) {
+        this.doc.getroot().attrib['version'] = value;
+    },
+    author: function() {
+        return getNodeTextSafe(this.doc.find('author'));
+    },
+    getGlobalPreference: function (name) {
+        return findElementAttributeValue(name, this.doc.findall('preference'));
+    },
+    setGlobalPreference: function (name, value) {
+        var pref = this.doc.find('preference[@name="' + name + '"]');
+        if (!pref) {
+            pref = new et.Element('preference');
+            pref.attrib.name = name;
+            this.doc.getroot().append(pref);
+        }
+        pref.attrib.value = value;
+    },
+    getPlatformPreference: function (name, platform) {
+        return findElementAttributeValue(name, this.doc.findall('platform[@name=\'' + platform + '\']/preference'));
+    },
+    getPreference: function(name, platform) {
+
+        var platformPreference = '';
+
+        if (platform) {
+            platformPreference = this.getPlatformPreference(name, platform);
+        }
+
+        return platformPreference ? platformPreference : this.getGlobalPreference(name);
+
+    },
+    /**
+     * Returns all resources for the platform specified.
+     * @param  {String} platform     The platform.
+     * @param {string}  resourceName Type of static resources to return.
+     *                               "icon" and "splash" currently supported.
+     * @return {Array}               Resources for the platform specified.
+     */
+    getStaticResources: function(platform, resourceName) {
+        var ret = [],
+            staticResources = [];
+        if (platform) { // platform specific icons
+            this.doc.findall('platform[@name=\'' + platform + '\']/' + resourceName).forEach(function(elt){
+                elt.platform = platform; // mark as platform specific resource
+                staticResources.push(elt);
+            });
+        }
+        // root level resources
+        staticResources = staticResources.concat(this.doc.findall(resourceName));
+        // parse resource elements
+        var that = this;
+        staticResources.forEach(function (elt) {
+            var res = {};
+            res.src = elt.attrib.src;
+            res.target = elt.attrib.target || undefined;
+            res.density = elt.attrib['density'] || elt.attrib[that.cdvNamespacePrefix+':density'] || elt.attrib['gap:density'];
+            res.platform = elt.platform || null; // null means icon represents default icon (shared between platforms)
+            res.width = +elt.attrib.width || undefined;
+            res.height = +elt.attrib.height || undefined;
+
+            // default icon
+            if (!res.width && !res.height && !res.density) {
+                ret.defaultResource = res;
+            }
+            ret.push(res);
+        });
+
+        /**
+         * Returns resource with specified width and/or height.
+         * @param  {number} width Width of resource.
+         * @param  {number} height Height of resource.
+         * @return {Resource} Resource object or null if not found.
+         */
+        ret.getBySize = function(width, height) {
+            return ret.filter(function(res) {
+                if (!res.width && !res.height) {
+                    return false;
+                }
+                return ((!res.width || (width == res.width)) &&
+                    (!res.height || (height == res.height)));
+            })[0] || null;
+        };
+
+        /**
+         * Returns resource with specified density.
+         * @param  {string} density Density of resource.
+         * @return {Resource}       Resource object or null if not found.
+         */
+        ret.getByDensity = function(density) {
+            return ret.filter(function(res) {
+                return res.density == density;
+            })[0] || null;
+        };
+
+        /** Returns default icons */
+        ret.getDefault = function() {
+            return ret.defaultResource;
+        };
+
+        return ret;
+    },
+
+    /**
+     * Returns all icons for specific platform.
+     * @param  {string} platform Platform name
+     * @return {Resource[]}      Array of icon objects.
+     */
+    getIcons: function(platform) {
+        return this.getStaticResources(platform, 'icon');
+    },
+
+    /**
+     * Returns all splash images for specific platform.
+     * @param  {string} platform Platform name
+     * @return {Resource[]}      Array of Splash objects.
+     */
+    getSplashScreens: function(platform) {
+        return this.getStaticResources(platform, 'splash');
+    },
+
+    /**
+     * Returns all resource-files for a specific platform.
+     * @param  {string} platform Platform name
+     * @return {Resource[]}      Array of resource file objects.
+     */
+    getFileResources: function(platform) {
+        var fileResources = [];
+
+        if (platform) { // platform specific resources
+            fileResources = this.doc.findall('platform[@name=\'' + platform + '\']/resource-file').map(function(tag) {
+                return {
+                    platform: platform,
+                    src: tag.attrib.src,
+                    target: tag.attrib.target,
+                    versions: tag.attrib.versions,
+                    deviceTarget: tag.attrib['device-target'],
+                    arch: tag.attrib.arch
+                };
+            });
+        }
+
+        return fileResources;
+    },
+
+    /**
+     * Returns all hook scripts for the hook type specified.
+     * @param  {String} hook     The hook type.
+     * @param {Array}  platforms Platforms to look for scripts into (root scripts will be included as well).
+     * @return {Array}               Script elements.
+     */
+    getHookScripts: function(hook, platforms) {
+        var self = this;
+        var scriptElements = self.doc.findall('./hook');
+
+        if(platforms) {
+            platforms.forEach(function (platform) {
+                scriptElements = scriptElements.concat(self.doc.findall('./platform[@name="' + platform + '"]/hook'));
+            });
+        }
+
+        function filterScriptByHookType(el) {
+            return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
+        }
+
+        return scriptElements.filter(filterScriptByHookType);
+    },
+   /**
+    * Returns a list of plugin (IDs).
+    *
+    * This function also returns any plugin's that
+    * were defined using the legacy <feature> tags.
+    * @return {string[]} Array of plugin IDs
+    */
+    getPluginIdList: function () {
+        var plugins = this.doc.findall('plugin');
+        var result = plugins.map(function(plugin){
+            return plugin.attrib.name;
+        });
+        var features = this.doc.findall('feature');
+        features.forEach(function(element ){
+            var idTag = element.find('./param[@name="id"]');
+            if(idTag){
+                result.push(idTag.attrib.value);
+            }
+        });
+        return result;
+    },
+    getPlugins: function () {
+        return this.getPluginIdList().map(function (pluginId) {
+            return this.getPlugin(pluginId);
+        }, this);
+    },
+    /**
+     * Adds a plugin element. Does not check for duplicates.
+     * @name addPlugin
+     * @function
+     * @param {object} attributes name and spec are supported
+     * @param {Array|object} variables name, value or arbitary object
+     */
+    addPlugin: function (attributes, variables) {
+        if (!attributes && !attributes.name) return;
+        var el = new et.Element('plugin');
+        el.attrib.name = attributes.name;
+        if (attributes.spec) {
+            el.attrib.spec = attributes.spec;
+        }
+
+        // support arbitrary object as variables source
+        if (variables && typeof variables === 'object' && !Array.isArray(variables)) {
+            variables = Object.keys(variables)
+            .map(function (variableName) {
+                return {name: variableName, value: variables[variableName]};
+            });
+        }
+
+        if (variables) {
+            variables.forEach(function (variable) {
+                el.append(new et.Element('variable', { name: variable.name, value: variable.value }));
+            });
+        }
+        this.doc.getroot().append(el);
+    },
+    /**
+     * Retrives the plugin with the given id or null if not found.
+     *
+     * This function also returns any plugin's that
+     * were defined using the legacy <feature> tags.
+     * @name getPlugin
+     * @function
+     * @param {String} id
+     * @returns {object} plugin including any variables
+     */
+    getPlugin: function(id){
+        if(!id){
+            return undefined;
+        }
+        var pluginElement = this.doc.find('./plugin/[@name="' + id + '"]');
+        if (null === pluginElement) {
+            var legacyFeature =  this.doc.find('./feature/param[@name="id"][@value="' + id + '"]/..');
+            if(legacyFeature){
+                 events.emit('log', 'Found deprecated feature entry for ' + id +' in config.xml.');
+                return featureToPlugin(legacyFeature);
+            }
+            return undefined;
+        }
+        var plugin = {};
+
+        plugin.name = pluginElement.attrib.name;
+        plugin.spec = pluginElement.attrib.spec || pluginElement.attrib.src || pluginElement.attrib.version;
+        plugin.variables = {};
+        var variableElements = pluginElement.findall('variable');
+        variableElements.forEach(function(varElement){
+            var name = varElement.attrib.name;
+            var value = varElement.attrib.value;
+            if(name){
+                plugin.variables[name] = value;
+            }
+        });
+        return plugin;
+    },
+    /**
+     * Remove the plugin entry with give name (id).
+     *
+     * This function also operates on any plugin's that
+     * were defined using the legacy <feature> tags.
+     * @name removePlugin
+     * @function
+     * @param id name of the plugin
+     */
+    removePlugin: function(id){
+        if(id){
+            var plugins = this.doc.findall('./plugin/[@name="' + id + '"]')
+                .concat(this.doc.findall('./feature/param[@name="id"][@value="' + id + '"]/..'));
+            var children = this.doc.getroot().getchildren();
+            plugins.forEach(function (plugin) {
+                var idx = children.indexOf(plugin);
+                if (idx > -1) {
+                    children.splice(idx, 1);
+                }
+            });
+        }
+    },
+
+    // Add any element to the root
+    addElement: function(name, attributes) {
+        var el = et.Element(name);
+        for (var a in attributes) {
+            el.attrib[a] = attributes[a];
+        }
+        this.doc.getroot().append(el);
+    },
+
+    /**
+     * Adds an engine. Does not check for duplicates.
+     * @param  {String} name the engine name
+     * @param  {String} spec engine source location or version (optional)
+     */
+    addEngine: function(name, spec){
+        if(!name) return;
+        var el = et.Element('engine');
+        el.attrib.name = name;
+        if(spec){
+            el.attrib.spec = spec;
+        }
+        this.doc.getroot().append(el);
+    },
+    /**
+     * Removes all the engines with given name
+     * @param  {String} name the engine name.
+     */
+    removeEngine: function(name){
+        var engines = this.doc.findall('./engine/[@name="' +name+'"]');
+        for(var i=0; i < engines.length; i++){
+            var children = this.doc.getroot().getchildren();
+            var idx = children.indexOf(engines[i]);
+            if(idx > -1){
+                children.splice(idx,1);
+            }
+        }
+    },
+    getEngines: function(){
+        var engines = this.doc.findall('./engine');
+        return engines.map(function(engine){
+            var spec = engine.attrib.spec || engine.attrib.version;
+            return {
+                'name': engine.attrib.name,
+                'spec': spec ? spec : null
+            };
+        });
+    },
+    /* Get all the access tags */
+    getAccesses: function() {
+        var accesses = this.doc.findall('./access');
+        return accesses.map(function(access){
+            var minimum_tls_version = access.attrib['minimum-tls-version']; /* String */
+            var requires_forward_secrecy = access.attrib['requires-forward-secrecy']; /* Boolean */
+            var requires_certificate_transparency = access.attrib['requires-certificate-transparency']; /* Boolean */
+            var allows_arbitrary_loads_in_web_content = access.attrib['allows-arbitrary-loads-in-web-content']; /* Boolean */
+            var allows_arbitrary_loads_in_media = access.attrib['allows-arbitrary-loads-in-media']; /* Boolean */
+            var allows_local_networking = access.attrib['allows-local-networking']; /* Boolean */
+            
+            return {
+                'origin': access.attrib.origin,
+                'minimum_tls_version': minimum_tls_version,
+                'requires_forward_secrecy' : requires_forward_secrecy,
+                'requires_certificate_transparency' : requires_certificate_transparency,
+                'allows_arbitrary_loads_in_web_content' : allows_arbitrary_loads_in_web_content,
+                'allows_arbitrary_loads_in_media' : allows_arbitrary_loads_in_media,
+                'allows_local_networking' : allows_local_networking
+            };
+        });
+    },
+    /* Get all the allow-navigation tags */
+    getAllowNavigations: function() {
+        var allow_navigations = this.doc.findall('./allow-navigation');
+        return allow_navigations.map(function(allow_navigation){
+            var minimum_tls_version = allow_navigation.attrib['minimum-tls-version']; /* String */
+            var requires_forward_secrecy = allow_navigation.attrib['requires-forward-secrecy']; /* Boolean */
+            var requires_certificate_transparency = allow_navigation.attrib['requires-certificate-transparency']; /* Boolean */
+
+            return {
+                'href': allow_navigation.attrib.href,
+                'minimum_tls_version': minimum_tls_version,
+                'requires_forward_secrecy' : requires_forward_secrecy,
+                'requires_certificate_transparency' : requires_certificate_transparency
+            };
+        });
+    },
+    /* Get all the allow-intent tags */
+    getAllowIntents: function() {
+        var allow_intents = this.doc.findall('./allow-intent');
+        return allow_intents.map(function(allow_intent){
+            return {
+                'href': allow_intent.attrib.href
+            };
+        });
+    },
+    /* Get all edit-config tags */
+    getEditConfigs: function(platform) {
+        var platform_tag = this.doc.find('./platform[@name="' + platform + '"]');
+        var platform_edit_configs = platform_tag ? platform_tag.findall('edit-config') : [];
+
+        var edit_configs = this.doc.findall('edit-config').concat(platform_edit_configs);
+
+        return edit_configs.map(function(tag) {
+            var editConfig =
+                {
+                    file : tag.attrib['file'],
+                    target : tag.attrib['target'],
+                    mode : tag.attrib['mode'],
+                    id : 'config.xml',
+                    xmls : tag.getchildren()
+                };
+            return editConfig;
+        });
+    },
+    write:function() {
+        fs.writeFileSync(this.path, this.doc.write({indent: 4}), 'utf-8');
+    }
+};
+
+function featureToPlugin(featureElement) {
+    var plugin = {};
+    plugin.variables = [];
+    var pluginVersion,
+        pluginSrc;
+
+    var nodes = featureElement.findall('param');
+    nodes.forEach(function (element) {
+        var n = element.attrib.name;
+        var v = element.attrib.value;
+        if (n === 'id') {
+            plugin.name = v;
+        } else if (n === 'version') {
+            pluginVersion = v;
+        } else if (n === 'url' || n === 'installPath') {
+            pluginSrc = v;
+        } else {
+            plugin.variables[n] = v;
+        }
+    });
+
+    var spec = pluginSrc || pluginVersion;
+    if (spec) {
+        plugin.spec = spec;
+    }
+
+    return plugin;
+}
+module.exports = ConfigParser;

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md
new file mode 100644
index 0000000..e5cd1bf
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md
@@ -0,0 +1,86 @@
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+-->
+
+# Cordova-Lib
+
+## ConfigParser
+
+wraps a valid cordova config.xml file 
+
+### Usage
+
+### Include the ConfigParser module in a projet
+
+    var ConfigParser = require('cordova-lib').configparser;
+
+### Create a new ConfigParser
+
+    var config = new ConfigParser('path/to/config/xml/');
+    
+### Utility Functions
+
+#### packageName(id)
+returns document root 'id' attribute value
+#### Usage
+
+    config.packageName: function(id) 
+
+/*
+ * sets document root element 'id' attribute to @id
+ *
+ * @id - new id value
+ *
+ */
+#### setPackageName(id)
+set document root 'id' attribute to 
+ function(id) {
+        this.doc.getroot().attrib['id'] = id;
+    },
+
+### 
+    name: function() {
+        return getNodeTextSafe(this.doc.find('name'));
+    },
+    setName: function(name) {
+        var el = findOrCreate(this.doc, 'name');
+        el.text = name;
+    },
+
+### read the description element
+    
+    config.description()
+
+    var text = "New and improved description of App"
+    setDescription(text)
+    
+### version management
+    version()
+    android_versionCode()
+    ios_CFBundleVersion()
+    setVersion()
+    
+### read author element
+
+   config.author();
+
+### read preference
+
+    config.getPreference(name);

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js
new file mode 100644
index 0000000..46e733f
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js
@@ -0,0 +1,76 @@
+/**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+
+var fs = require('fs'),
+    path = require('path');
+
+function isRootDir(dir) {
+    if (fs.existsSync(path.join(dir, 'www'))) {
+        if (fs.existsSync(path.join(dir, 'config.xml'))) {
+            // For sure is.
+            if (fs.existsSync(path.join(dir, 'platforms'))) {
+                return 2;
+            } else {
+                return 1;
+            }
+        }
+        // Might be (or may be under platforms/).
+        if (fs.existsSync(path.join(dir, 'www', 'config.xml'))) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+// Runs up the directory chain looking for a .cordova directory.
+// IF it is found we are in a Cordova project.
+// Omit argument to use CWD.
+function isCordova(dir) {
+    if (!dir) {
+        // Prefer PWD over cwd so that symlinked dirs within your PWD work correctly (CB-5687).
+        var pwd = process.env.PWD;
+        var cwd = process.cwd();
+        if (pwd && pwd != cwd && pwd != 'undefined') {
+            return isCordova(pwd) || isCordova(cwd);
+        }
+        return isCordova(cwd);
+    }
+    var bestReturnValueSoFar = false;
+    for (var i = 0; i < 1000; ++i) {
+        var result = isRootDir(dir);
+        if (result === 2) {
+            return dir;
+        }
+        if (result === 1) {
+            bestReturnValueSoFar = dir;
+        }
+        var parentDir = path.normalize(path.join(dir, '..'));
+        // Detect fs root.
+        if (parentDir == dir) {
+            return bestReturnValueSoFar;
+        }
+        dir = parentDir;
+    }
+    console.error('Hit an unhandled case in CordovaCheck.isCordova');
+    return false;
+}
+
+module.exports = {
+    findProjectRoot : isCordova
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/cef2959c/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js
----------------------------------------------------------------------
diff --git a/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js
new file mode 100644
index 0000000..7262448
--- /dev/null
+++ b/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js
@@ -0,0 +1,91 @@
+/**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+
+/* jshint proto:true */
+
+var EOL = require('os').EOL;
+
+/**
+ * A derived exception class. See usage example in cli.js
+ * Based on:
+ * stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/8460753#8460753
+ * @param {String} message Error message
+ * @param {Number} [code=0] Error code
+ * @param {CordovaExternalToolErrorContext} [context] External tool error context object
+ * @constructor
+ */
+function CordovaError(message, code, context) {
+    Error.captureStackTrace(this, this.constructor);
+    this.name = this.constructor.name;
+    this.message = message;
+    this.code = code || CordovaError.UNKNOWN_ERROR;
+    this.context = context;
+}
+CordovaError.prototype.__proto__ = Error.prototype;
+
+// TODO: Extend error codes according the projects specifics
+CordovaError.UNKNOWN_ERROR = 0;
+CordovaError.EXTERNAL_TOOL_ERROR = 1;
+
+/**
+ * Translates instance's error code number into error code name, e.g. 0 -> UNKNOWN_ERROR
+ * @returns {string} Error code string name
+ */
+CordovaError.prototype.getErrorCodeName = function() {
+    for(var key in CordovaError) {
+        if(CordovaError.hasOwnProperty(key)) {
+            if(CordovaError[key] === this.code) {
+                return key;
+            }
+        }
+    }
+};
+
+/**
+ * Converts CordovaError instance to string representation
+ * @param   {Boolean}  [isVerbose]  Set up verbose mode. Used to provide more
+ *   details including information about error code name and context
+ * @return  {String}              Stringified error representation
+ */
+CordovaError.prototype.toString = function(isVerbose) {
+    var message = '', codePrefix = '';
+
+    if(this.code !== CordovaError.UNKNOWN_ERROR) {
+        codePrefix = 'code: ' + this.code + (isVerbose ? (' (' + this.getErrorCodeName() + ')') : '') + ' ';
+    }
+
+    if(this.code === CordovaError.EXTERNAL_TOOL_ERROR) {
+        if(typeof this.context !== 'undefined') {
+            if(isVerbose) {
+                message = codePrefix + EOL + this.context.toString(isVerbose) + '\n failed with an error: ' +
+                    this.message + EOL + 'Stack trace: ' + this.stack;
+            } else {
+                message = codePrefix + '\'' + this.context.toString(isVerbose) + '\' ' + this.message;
+            }
+        } else {
+            message = 'External tool failed with an error: ' + this.message;
+        }
+    } else {
+        message = isVerbose ? codePrefix + this.stack : codePrefix + this.message;
+    }
+
+    return message;
+};
+
+module.exports = CordovaError;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org


Mime
View raw message