cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kam...@apache.org
Subject [25/70] Split out cordova-lib: move cordova-plugman files
Date Fri, 02 May 2014 18:30:00 GMT
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/platforms/ios.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/platforms/ios.js b/cordova-lib/src/plugman/platforms/ios.js
new file mode 100644
index 0000000..4f570c3
--- /dev/null
+++ b/cordova-lib/src/plugman/platforms/ios.js
@@ -0,0 +1,216 @@
+/*
+ *
+ * Copyright 2013 Anis Kadri
+ *
+ * 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 path = require('path')
+  , fs   = require('fs')
+  , glob = require('glob')
+  , xcode = require('xcode')
+  , plist = require('plist-with-patches')
+  , shell = require('shelljs')
+  , events = require('../events')
+  , cachedProjectFiles = {};
+
+module.exports = {
+    www_dir:function(project_dir) {
+        return path.join(project_dir, 'www');
+    },
+    package_name:function(project_dir) {
+        var plist_file = glob.sync(path.join(project_dir, '**', '*-Info.plist'))[0];
+        return plist.parseFileSync(plist_file).CFBundleIdentifier;
+    },
+    "source-file":{
+        install:function(source_el, plugin_dir, project_dir, plugin_id, project) {
+            var src = source_el.attrib['src'];
+            var srcFile = path.resolve(plugin_dir, src);
+            var targetDir = path.resolve(project.plugins_dir, plugin_id, getRelativeDir(source_el));
+            var destFile = path.resolve(targetDir, path.basename(src));
+            var is_framework = source_el.attrib['framework'] && (source_el.attrib['framework'] == 'true' || source_el.attrib['framework'] == true);
+            var has_flags = source_el.attrib['compiler-flags'] && source_el.attrib['compiler-flags'].length ? true : false ;
+
+            if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios <source-file>');
+            if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists');
+            var project_ref = path.join('Plugins', path.relative(project.plugins_dir, destFile));
+
+            if (is_framework) {
+                var weak = source_el.attrib['weak'];
+                var opt = { weak: (weak == undefined || weak == null || weak != 'true' ? false : true ) };
+                var project_relative = path.join(path.basename(project.xcode_path), project_ref);
+                project.xcode.addFramework(project_relative, opt);
+                project.xcode.addToLibrarySearchPaths({path:project_ref});
+            } else {
+                project.xcode.addSourceFile(project_ref, has_flags ? {compilerFlags:source_el.attrib['compiler-flags']} : {});
+            }
+            shell.mkdir('-p', targetDir);
+            shell.cp(srcFile, destFile);
+        },
+        uninstall:function(source_el, project_dir, plugin_id, project) {
+            var src = source_el.attrib['src'];
+            var targetDir = path.resolve(project.plugins_dir, plugin_id, getRelativeDir(source_el));
+            var destFile = path.resolve(targetDir, path.basename(src));
+            var is_framework = source_el.attrib['framework'] && (source_el.attrib['framework'] == 'true' || source_el.attrib['framework'] == true);
+
+            var project_ref = path.join('Plugins', path.relative(project.plugins_dir, destFile));
+            project.xcode.removeSourceFile(project_ref);
+            if (is_framework) {
+                var project_relative = path.join(path.basename(project.xcode_path), project_ref);
+                project.xcode.removeFramework(project_relative);
+                project.xcode.removeFromLibrarySearchPaths({path:project_ref});
+            }
+            shell.rm('-rf', destFile);
+
+            if(fs.existsSync(targetDir) && fs.readdirSync(targetDir).length>0){
+                shell.rm('-rf', targetDir);
+            }
+        }
+    },
+    "header-file":{
+        install:function(header_el, plugin_dir, project_dir, plugin_id, project) {
+            var src = header_el.attrib['src'];
+            var srcFile = path.resolve(plugin_dir, src);
+            var targetDir = path.resolve(project.plugins_dir, plugin_id, getRelativeDir(header_el));
+            var destFile = path.resolve(targetDir, path.basename(src));
+            if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios <header-file>');
+            if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists');
+            project.xcode.addHeaderFile(path.join('Plugins', path.relative(project.plugins_dir, destFile)));
+            shell.mkdir('-p', targetDir);
+            shell.cp(srcFile, destFile);
+        },
+        uninstall:function(header_el, project_dir, plugin_id, project) {
+            var src = header_el.attrib['src'];
+            var targetDir = path.resolve(project.plugins_dir, plugin_id, getRelativeDir(header_el));
+            var destFile = path.resolve(targetDir, path.basename(src));
+            project.xcode.removeHeaderFile(path.join('Plugins', path.relative(project.plugins_dir, destFile)));
+            shell.rm('-rf', destFile);
+            if(fs.existsSync(targetDir) && fs.readdirSync(targetDir).length>0){
+                shell.rm('-rf', targetDir);
+            }
+        }
+    },
+    "resource-file":{
+        install:function(resource_el, plugin_dir, project_dir, plugin_id, project) {
+            var src = resource_el.attrib['src'],
+                srcFile = path.resolve(plugin_dir, src),
+                destFile = path.resolve(project.resources_dir, path.basename(src));
+            if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios <resource-file>');
+            if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists');
+            project.xcode.addResourceFile(path.join('Resources', path.basename(src)));
+            shell.cp('-R', srcFile, project.resources_dir);
+        },
+        uninstall:function(resource_el, project_dir, plugin_id, project) {
+            var src = resource_el.attrib['src'],
+                destFile = path.resolve(project.resources_dir, path.basename(src));
+            project.xcode.removeResourceFile(path.join('Resources', path.basename(src)));
+            shell.rm('-rf', destFile);
+        }
+    },
+    "framework":{ // CB-5238 custom frameworks only
+        install:function(framework_el, plugin_dir, project_dir, plugin_id, project) {
+            var src = framework_el.attrib['src'],
+                custom = framework_el.attrib['custom'],
+                srcFile = path.resolve(plugin_dir, src),
+                targetDir = path.resolve(project.plugins_dir, plugin_id, path.basename(src));
+            if (!custom) throw new Error('cannot add non custom frameworks.');
+            if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios <framework>');
+            if (fs.existsSync(targetDir)) throw new Error('target destination "' + targetDir + '" already exists');
+            shell.mkdir('-p', path.dirname(targetDir));
+            shell.cp('-R', srcFile, path.dirname(targetDir)); // frameworks are directories
+            var project_relative = path.relative(project_dir, targetDir);
+            project.xcode.addFramework(project_relative, {customFramework: true});
+        },
+        uninstall:function(framework_el, project_dir, plugin_id, project) {
+            var src = framework_el.attrib['src'],
+                targetDir = path.resolve(project.plugins_dir, plugin_id, path.basename(src));
+            project.xcode.removeFramework(targetDir, {customFramework: true});
+            shell.rm('-rf', targetDir);
+        }
+    },
+    "lib-file": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'lib-file.install is not supported for ios');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'lib-file.uninstall is not supported for ios');
+        }
+    },
+    parseProjectFile:function(project_dir) {
+        // TODO: With ConfigKeeper introduced in config-changes.js
+        // there is now double caching of iOS project files.
+        // Remove the cache here when install can handle
+        // a list of plugins at once.
+        if (cachedProjectFiles[project_dir]) {
+            return cachedProjectFiles[project_dir];
+        }
+        // grab and parse pbxproj
+        // we don't want CordovaLib's xcode project
+        var project_files = glob.sync(path.join(project_dir, '*.xcodeproj', 'project.pbxproj'));
+
+        if (project_files.length === 0) {
+            throw new Error("does not appear to be an xcode project (no xcode project file)");
+        }
+        var pbxPath = project_files[0];
+        var xcodeproj = xcode.project(pbxPath);
+        xcodeproj.parseSync();
+
+        // grab and parse plist file or config.xml
+        var config_files = (glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist')).length == 0 ?
+                            glob.sync(path.join(project_dir, '**', 'config.xml')) :
+                            glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist'))
+                           );
+
+        config_files = config_files.filter(function (val) {
+            return !(/^build\//.test(val)) && !(/\/www\/config.xml$/.test(val));
+        });
+
+        if (config_files.length === 0) {
+            throw new Error("could not find PhoneGap/Cordova plist file, or config.xml file.");
+        }
+
+        var config_file = config_files[0];
+        var config_filename = path.basename(config_file);
+        var xcode_dir = path.dirname(config_file);
+        var pluginsDir = path.resolve(xcode_dir, 'Plugins');
+        var resourcesDir = path.resolve(xcode_dir, 'Resources');
+
+        cachedProjectFiles[project_dir] = {
+            plugins_dir:pluginsDir,
+            resources_dir:resourcesDir,
+            xcode:xcodeproj,
+            xcode_path:xcode_dir,
+            pbx: pbxPath,
+            write: function () {
+                fs.writeFileSync(pbxPath, xcodeproj.writeSync());
+            }
+        };
+
+        return cachedProjectFiles[project_dir];
+    },
+    purgeProjectFileCache:function(project_dir) {
+        delete cachedProjectFiles[project_dir];
+    }
+
+};
+
+function getRelativeDir(file) {
+    var targetDir = file.attrib['target-dir'];
+    if (targetDir) {
+        return targetDir;
+    } else {
+        return '';
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/platforms/tizen.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/platforms/tizen.js b/cordova-lib/src/plugman/platforms/tizen.js
new file mode 100644
index 0000000..efbeba9
--- /dev/null
+++ b/cordova-lib/src/plugman/platforms/tizen.js
@@ -0,0 +1,72 @@
+var path = require('path')
+    , fs = require('fs')
+    , common = require('./common')
+    , events = require('../events')
+    , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers'));
+
+module.exports = {
+    www_dir: function(project_dir) {
+        return path.join(project_dir, 'www');
+    },
+    package_name:function(project_dir) {
+        // preferred location if cordova >= 3.4
+        var preferred_path = path.join(project_dir, 'config.xml');
+        if (!fs.existsSync(preferred_path)) {
+            // older location
+            old_config_path = path.join(module.exports.www_dir(project_dir), 'config.xml');
+            if (!fs.existsSync(old_config_path)) {
+                // output newer location and fail reading
+                config_path = preferred_path;
+                events.emit('verbose', 'unable to find '+config_path);
+            } else {
+                config_path = old_config_path;
+            }
+        } else {
+            config_path = preferred_path;
+        }
+        var widget_doc = xml_helpers.parseElementtreeSync(config_path);
+        return widget_doc._root.attrib['id'];
+    },
+    "source-file":{
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src']));
+            common.copyFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src']));
+            common.removeFile(project_dir, dest);
+        }
+    },
+    "header-file": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'header-fileinstall is not supported for firefoxos');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'header-file.uninstall is not supported for firefoxos');
+        }
+    },
+    "resource-file":{
+        install:function(el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'resource-file.install is not supported for firefoxos');
+        },
+        uninstall:function(el, project_dir, plugin_id) {
+            events.emit('verbose', 'resource-file.uninstall is not supported for firefoxos');
+        }
+    },
+    "framework": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'framework.install is not supported for firefoxos');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'framework.uninstall is not supported for firefoxos');
+        }
+    },
+    "lib-file": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'lib-file.install is not supported for firefoxos');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'lib-file.uninstall is not supported for firefoxos');
+        }
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/platforms/ubuntu.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/platforms/ubuntu.js b/cordova-lib/src/plugman/platforms/ubuntu.js
new file mode 100644
index 0000000..d04b774
--- /dev/null
+++ b/cordova-lib/src/plugman/platforms/ubuntu.js
@@ -0,0 +1,124 @@
+/*
+ *
+ * Copyright 2013 Canonical Ltd.
+ *
+ * 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.
+ *
+*/
+
+function replaceAt(str, index, char) {
+    return str.substr(0, index) + char + str.substr(index + char.length);
+}
+
+function toCamelCase(str) {
+    return str.split('-').map(function(str) {
+        return replaceAt(str, 0, str[0].toUpperCase());
+    }).join('');
+}
+
+var fs = require('fs')
+   , path = require('path')
+   , events = require('../events')
+   , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers'));
+
+module.exports = {
+    www_dir:function(project_dir) {
+        return path.join(project_dir, 'www');
+    },
+
+    package_name:function (project_dir) {
+        var config_path = path.join(project_dir, 'config.xml');
+        var widget_doc = xml_helpers.parseElementtreeSync(config_path);
+        return widget_doc._root.attrib['id'];
+    },
+    "source-file":{
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            var shell = require('shelljs');
+            var dest = path.join(project_dir, "build", "src", "plugins", plugin_id);
+            shell.mkdir(dest);
+            shell.cp(path.join(plugin_dir, source_el.attrib.src), dest);
+
+            shell.exec('touch ' + path.join(project_dir, "CMakeLists.txt"))
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            var shell = require('shelljs');
+
+            var dest = path.join(project_dir, "build", "src", "plugins", plugin_id);
+            shell.rm(path.join(dest, path.basename(source_el.attrib.src)));
+        }
+    },
+    "header-file":{
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            var shell = require('shelljs');
+            var dest = path.join(project_dir, "build", "src", "plugins", plugin_id);
+            shell.mkdir(dest);
+            shell.cp(path.join(plugin_dir, source_el.attrib.src), dest);
+
+            var plugins = path.join(project_dir, "build", "src", "coreplugins.cpp");
+            src = String(fs.readFileSync(plugins));
+
+            src = src.replace('INSERT_HEADER_HERE', '#include "plugins/' + plugin_id + "/" + path.basename(source_el.attrib.src) +'"\nINSERT_HEADER_HERE');
+            var class_name = plugin_id.match(/\.[^.]+$/)[0].substr(1);
+            class_name = toCamelCase(class_name);
+            src = src.replace('INSERT_PLUGIN_HERE', 'INIT_PLUGIN(' + class_name + ');INSERT_PLUGIN_HERE');
+
+            fs.writeFileSync(plugins, src);
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            var shell = require('shelljs');
+            var dest = path.join(project_dir, "build", "src", "plugins", plugin_id);
+            shell.rm(path.join(dest, path.basename(source_el.attrib.src)));
+
+            var plugins = path.join(project_dir, "build", "src", "coreplugins.cpp");
+            src = String(fs.readFileSync(plugins));
+
+            src = src.replace('#include "plugins/' + plugin_id + "/" + path.basename(source_el.attrib.src) +'"', '');
+            var class_name = plugin_id.match(/\.[^.]+$/)[0].substr(1);
+            class_name = toCamelCase(class_name);
+            src = src.replace('INIT_PLUGIN(' + class_name + ');', '');
+
+            fs.writeFileSync(plugins, src);
+        }
+    },
+    "resource-file":{
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            var shell = require('shelljs');
+            var dest = path.join(project_dir, "qml");
+            shell.mkdir(dest);
+            shell.cp(path.join(plugin_dir, source_el.attrib.src), dest);
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            var shell = require('shelljs');
+
+            var dest = path.join(project_dir, "qml");
+            shell.rm(path.join(dest, path.basename(source_el.attrib.src)));
+        }
+    },
+    "framework": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'framework.install is not supported for ubuntu');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'framework.uninstall is not supported for ubuntu');
+        }
+    },
+    "lib-file": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'lib-file.install is not supported for ubuntu');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'lib-file.uninstall is not supported for ubuntu');
+        }
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/platforms/windows8.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/platforms/windows8.js b/cordova-lib/src/plugman/platforms/windows8.js
new file mode 100644
index 0000000..523dc6e
--- /dev/null
+++ b/cordova-lib/src/plugman/platforms/windows8.js
@@ -0,0 +1,134 @@
+/*
+ *
+ * Copyright 2013 Jesse MacFadyen
+ *
+ * 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 common = require('./common'),
+    path = require('path'),
+    glob = require('glob'),
+    shell = require('shelljs'),
+    fs = require('fs'),
+    w8jsproj = require('../util/w8jsproj'),
+    events = require('../events'),
+    xml_helpers = require('../util/xml-helpers');
+
+
+module.exports = {
+    platformName:"windows8",
+    InvalidProjectPathError:'does not appear to be a Windows Store JS project (no .jsproj file)',
+    www_dir:function(project_dir) {
+        return path.join(project_dir, 'www');
+    },
+    package_name:function(project_dir) {
+        var manifest = xml_helpers.parseElementtreeSync(path.join(project_dir, 'package.appxmanifest'));
+        return manifest.find("Properties/DisplayName").text;
+    },
+    parseProjectFile:function(project_dir) {
+        var project_files = glob.sync('*.jsproj', { cwd:project_dir });
+        if (project_files.length == 0) {
+            throw new Error(this.InvalidProjectPathError);
+        }
+        return new w8jsproj(path.join(project_dir, project_files[0]));
+    },
+    "source-file":{
+        install:function(source_el, plugin_dir, project_dir, plugin_id, project_file) {
+            var targetDir = source_el.attrib['target-dir'] || '';
+            var dest = path.join('www', 'plugins', plugin_id, targetDir, path.basename(source_el.attrib['src']));
+
+            common.copyNewFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
+            // add reference to this file to jsproj.
+            project_file.addSourceFile(dest);
+        },
+        uninstall:function(source_el, project_dir, plugin_id, project_file) {
+            var dest = path.join('www', 'plugins', plugin_id,
+                                 source_el.attrib['target-dir'] ? source_el.attrib['target-dir'] : '',
+                                 path.basename(source_el.attrib['src']));
+            common.removeFile(project_dir, dest);
+            // remove reference to this file from csproj.
+            project_file.removeSourceFile(dest);
+        }
+    },
+    "header-file": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'header-fileinstall is not supported for Windows 8');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'header-file.uninstall is not supported for Windows 8');
+        }
+    },
+    "resource-file":{
+        install:function(el, plugin_dir, project_dir, plugin_id, project_file) {
+            events.emit('verbose', 'resource-file is not supported for Windows 8');
+        },
+        uninstall:function(el, project_dir, plugin_id, project_file) {
+        }
+    },
+    "lib-file": {
+        install:function(el, plugin_dir, project_dir, plugin_id, project_file) {
+            var inc  = el.attrib['Include'];
+            project_file.addSDKRef(inc);
+        },
+        uninstall:function(el, project_dir, plugin_id, project_file) {
+            events.emit('verbose', 'windows8 lib-file uninstall :: ' + plugin_id);
+            var inc = el.attrib['Include'];
+            project_file.removeSDKRef(inc);
+        }
+    },
+    "framework": {
+        install:function(el, plugin_dir, project_dir, plugin_id, project_file) {
+            events.emit('verbose', 'windows8 framework install :: ' + plugin_id);
+
+            var src = el.attrib['src'];
+            var dest = src; // if !isCustom, we will just add a reference to the file in place
+            // technically it is not possible to get here without isCustom == true -jm
+            // var isCustom = el.attrib.custom == "true";
+            var type = el.attrib["type"];
+
+            if(type == "projectReference") {
+                project_file.addProjectReference(path.join(plugin_dir,src));
+            }
+            else {
+                // if(isCustom) {}
+                dest = path.join('plugins', plugin_id, path.basename(src));
+                common.copyFile(plugin_dir, src, project_dir, dest);
+                project_file.addReference(dest,src);
+            }
+
+        },
+        uninstall:function(el, project_dir, plugin_id, project_file) {
+            events.emit('verbose', 'windows8 framework uninstall :: ' + plugin_id  );
+
+            var src = el.attrib['src'];
+            // technically it is not possible to get here without isCustom == true -jm
+            // var isCustom = el.attrib.custom == "true";
+            var type = el.attrib["type"];
+            // unfortunately we have to generate the plugin_dir path because it is not passed to uninstall
+            var plugin_dir = path.join(project_dir,"cordova/plugins",plugin_id,src);
+
+            if(type == "projectReference") {
+                project_file.removeProjectReference(plugin_dir);
+            }
+            else {
+                // if(isCustom) {  }
+                var targetPath = path.join('plugins', plugin_id);
+                common.removeFile(project_dir, targetPath);
+                project_file.removeReference(src);
+            }
+        }
+
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/platforms/wp7.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/platforms/wp7.js b/cordova-lib/src/plugman/platforms/wp7.js
new file mode 100644
index 0000000..e6a76d0
--- /dev/null
+++ b/cordova-lib/src/plugman/platforms/wp7.js
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright 2013 Jesse MacFadyen
+ *
+ * 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 common = require('./common'),
+    path = require('path'),
+    glob = require('glob'),
+    fs = require('fs'),
+    csproj = require('../util/csproj'),
+    events = require('../events'),
+    xml_helpers = require('../util/xml-helpers');
+
+module.exports = {
+    www_dir:function(project_dir) {
+        return path.join(project_dir, 'www');
+    },
+    package_name:function(project_dir) {
+        return xml_helpers.parseElementtreeSync(path.join(project_dir, 'Properties', 'WMAppManifest.xml')).find('App').attrib.ProductID;
+    },
+    parseProjectFile:function(project_dir) {
+        var project_files = glob.sync('*.csproj', {
+            cwd:project_dir
+        });
+        if (project_files.length === 0) {
+            throw new Error('does not appear to be a Windows Phone project (no .csproj file)');
+        }
+        return new csproj(path.join(project_dir, project_files[0]));
+    },
+    "source-file":{
+        install:function(source_el, plugin_dir, project_dir, plugin_id, project_file) {
+            var dest = path.join('Plugins', plugin_id, source_el.attrib['target-dir'] ? source_el.attrib['target-dir'] : '', path.basename(source_el.attrib['src']));
+
+            common.copyNewFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
+            // add reference to this file to csproj.
+            project_file.addSourceFile(dest);
+        },
+        uninstall:function(source_el, project_dir, plugin_id, project_file) {
+            var dest = path.join('Plugins', plugin_id, source_el.attrib['target-dir'] ? source_el.attrib['target-dir'] : '', path.basename(source_el.attrib['src']));
+            common.removeFile(project_dir, dest);
+            // remove reference to this file from csproj.
+            project_file.removeSourceFile(dest);
+        }
+    },
+    "header-file": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'header-fileinstall is not supported for wp7');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'header-file.uninstall is not supported for wp7');
+        }
+    },
+    "resource-file":{
+        install:function(el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'resource-file.install is not supported for wp7');
+        },
+        uninstall:function(el, project_dir, plugin_id) {
+            events.emit('verbose', 'resource-file.uninstall is not supported for wp7');
+        }
+    },
+    "framework": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'framework.install is not supported for wp7');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'framework.uninstall is not supported for wp7');
+        }
+    },
+    "lib-file": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'lib-file.install is not supported for wp7');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'lib-file.uninstall is not supported for wp7');
+        }
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/platforms/wp8.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/platforms/wp8.js b/cordova-lib/src/plugman/platforms/wp8.js
new file mode 100644
index 0000000..1163b44
--- /dev/null
+++ b/cordova-lib/src/plugman/platforms/wp8.js
@@ -0,0 +1,113 @@
+/*
+ *
+ * Copyright 2013 Jesse MacFadyen
+ *
+ * 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 common = require('./common'),
+    path = require('path'),
+    glob = require('glob'),
+    fs = require('fs'),
+    csproj = require('../util/csproj'),
+    events = require('../events'),
+    xml_helpers = require('../util/xml-helpers');
+
+module.exports = {
+    www_dir:function(project_dir) {
+        return path.join(project_dir, 'www');
+    },
+    package_name:function(project_dir) {
+        return xml_helpers.parseElementtreeSync(path.join(project_dir, 'Properties', 'WMAppManifest.xml')).find('App').attrib.ProductID;
+    },
+    parseProjectFile:function(project_dir) {
+        var project_files = glob.sync('*.csproj', {
+            cwd:project_dir
+        });
+        if (project_files.length === 0) {
+            throw new Error('does not appear to be a Windows Phone project (no .csproj file)');
+        }
+        return new csproj(path.join(project_dir, project_files[0]));
+    },
+    "source-file":{
+        install:function(source_el, plugin_dir, project_dir, plugin_id, project_file) {
+            var dest = path.join('Plugins', plugin_id, source_el.attrib['target-dir'] ? source_el.attrib['target-dir'] : '', path.basename(source_el.attrib['src']));
+
+            common.copyNewFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
+            // add reference to this file to csproj.
+            project_file.addSourceFile(dest);
+        },
+        uninstall:function(source_el, project_dir, plugin_id, project_file) {
+            var dest = path.join('Plugins', plugin_id, source_el.attrib['target-dir'] ? source_el.attrib['target-dir'] : '', path.basename(source_el.attrib['src']));
+            common.removeFile(project_dir, dest);
+            // remove reference to this file from csproj.
+            project_file.removeSourceFile(dest);
+        }
+    },
+    "header-file": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'header-file.install is not supported for wp8');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'header-file.uninstall is not supported for wp8');
+        }
+    },
+    "resource-file":{
+        install:function(el, plugin_dir, project_dir, plugin_id, project_file) {
+            events.emit('verbose', 'resource-file.install is not supported for wp8');
+        },
+        uninstall:function(el, project_dir, plugin_id, project_file) {
+            events.emit('verbose', 'resource-file.uninstall is not supported for wp8');
+        }
+    },
+    "framework":{
+        install:function(el, plugin_dir, project_dir, plugin_id, project_file) {
+            events.emit('verbose', 'wp8 framework install :: ' + plugin_id  );
+
+            var src = el.attrib['src'];
+            var dest = src; // if !isCustom, we will just add a reference to the file in place
+            var isCustom = el.attrib.custom == "true";
+
+            if(isCustom) {
+                dest = path.join('plugins', plugin_id, path.basename(src));
+                common.copyFile(plugin_dir, src, project_dir, dest);
+            }
+
+            project_file.addReference(dest);
+
+        },
+        uninstall:function(el, project_dir, plugin_id, project_file) {
+            events.emit('verbose', 'wp8 framework uninstall :: ' + plugin_id  );
+
+            var src = el.attrib['src'];
+            var isCustom = el.attrib.custom == "true";
+
+            if(isCustom) {
+                var dest = path.join('plugins', plugin_id);
+                common.removeFile(project_dir, dest);
+            }
+
+            project_file.removeReference(src);
+        }
+    },
+    "lib-file": {
+        install:function(source_el, plugin_dir, project_dir, plugin_id) {
+            events.emit('verbose', 'lib-file.install is not supported for wp8');
+        },
+        uninstall:function(source_el, project_dir, plugin_id) {
+            events.emit('verbose', 'lib-file.uninstall is not supported for wp8');
+        }
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/plugman.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/plugman.js b/cordova-lib/src/plugman/plugman.js
new file mode 100644
index 0000000..4aa1843
--- /dev/null
+++ b/cordova-lib/src/plugman/plugman.js
@@ -0,0 +1,207 @@
+/*
+ *
+ * Copyright 2013 Anis Kadri
+ *
+ * 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.
+ *
+*/
+
+// copyright (c) 2013 Andrew Lunny, Adobe Systems
+
+var events = require('./src/events');
+var Q = require('q');
+
+function addProperty(o, symbol, modulePath, doWrap) {
+    var val = null;
+
+    if (doWrap) {
+        o[symbol] = function() {
+            val = val || require(modulePath);
+            if (arguments.length && typeof arguments[arguments.length - 1] === 'function') {
+                // If args exist and the last one is a function, it's the callback.
+                var args = Array.prototype.slice.call(arguments);
+                var cb = args.pop();
+                val.apply(o, args).done(function(result) {cb(undefined, result)}, cb);
+            } else {
+                val.apply(o, arguments).done(null, function(err){ throw err; });
+            }
+        };
+    } else {
+        // The top-level plugman.foo
+        Object.defineProperty(o, symbol, {
+            get : function() { return val = val || require(modulePath); },
+            set : function(v) { val = v; }
+        });
+    }
+
+    // The plugman.raw.foo
+    Object.defineProperty(o.raw, symbol, {
+        get : function() { return val = val || require(modulePath); },
+        set : function(v) { val = v; }
+    });
+}
+
+plugman = {
+    on:                 events.on.bind(events),
+    off:                events.removeListener.bind(events),
+    removeAllListeners: events.removeAllListeners.bind(events),
+    emit:               events.emit.bind(events),
+    raw:                {}
+};
+
+addProperty(plugman, 'help', './src/help');
+addProperty(plugman, 'install', './src/install', true);
+addProperty(plugman, 'uninstall', './src/uninstall', true);
+addProperty(plugman, 'fetch', './src/fetch', true);
+addProperty(plugman, 'prepare', './src/prepare');
+addProperty(plugman, 'config', './src/config', true);
+addProperty(plugman, 'owner', './src/owner', true);
+addProperty(plugman, 'adduser', './src/adduser', true);
+addProperty(plugman, 'publish', './src/publish', true);
+addProperty(plugman, 'unpublish', './src/unpublish', true);
+addProperty(plugman, 'search', './src/search', true);
+addProperty(plugman, 'info', './src/info', true);
+addProperty(plugman, 'create', './src/create', true);
+addProperty(plugman, 'platform', './src/platform_operation', true);
+addProperty(plugman, 'config_changes', './src/util/config-changes');
+
+plugman.commands =  {
+    'config'   : function(cli_opts) {
+        plugman.config(cli_opts.argv.remain, function(err) {
+            if (err) throw err;
+            else console.log('done');
+        });
+    },
+    'owner'   : function(cli_opts) {
+        plugman.owner(cli_opts.argv.remain);
+    },
+    'install'  : function(cli_opts) {
+        if(!cli_opts.platform || !cli_opts.project || !cli_opts.plugin) {
+            return console.log(plugman.help());
+        }
+        var cli_variables = {}
+        if (cli_opts.variable) {
+            cli_opts.variable.forEach(function (variable) {
+                    var tokens = variable.split('=');
+                    var key = tokens.shift().toUpperCase();
+                    if (/^[\w-_]+$/.test(key)) cli_variables[key] = tokens.join('=');
+                    });
+        }
+        var opts = {
+            subdir: '.',
+            cli_variables: cli_variables,
+            www_dir: cli_opts.www,
+            searchpath: cli_opts.searchpath
+        };
+
+        var p = Q();
+        cli_opts.plugin.forEach(function (pluginSrc) {
+            p = p.then(function () {
+                return plugman.raw.install(cli_opts.platform, cli_opts.project, pluginSrc, cli_opts.plugins_dir, opts);
+            })
+        });
+        
+        return p;
+    },
+    'uninstall': function(cli_opts) {
+        if(!cli_opts.platform || !cli_opts.project || !cli_opts.plugin) {
+            return console.log(plugman.help());
+        }
+
+        var p = Q();
+        cli_opts.plugin.forEach(function (pluginSrc) {
+            p = p.then(function () {
+                return plugman.raw.uninstall(cli_opts.platform, cli_opts.project, pluginSrc, cli_opts.plugins_dir, { www_dir: cli_opts.www });
+            });
+        });
+
+        return p;
+    },
+    'adduser'  : function(cli_opts) {
+        plugman.adduser(function(err) {
+            if (err) throw err;
+            else console.log('user added');
+        });
+    },
+
+    'search'   : function(cli_opts) {
+        plugman.search(cli_opts.argv.remain, function(err, plugins) {
+            if (err) throw err;
+            else {
+                for(var plugin in plugins) {
+                    console.log(plugins[plugin].name, '-', plugins[plugin].description || 'no description provided');
+                }
+            }
+        });
+    },
+    'info'     : function(cli_opts) {
+        plugman.info(cli_opts.argv.remain, function(err, plugin_info) {
+            if (err) throw err;
+            else {
+                console.log('name:', plugin_info.name);
+                console.log('version:', plugin_info.version);
+                if (plugin_info.engines) {
+                    for(var i = 0, j = plugin_info.engines.length ; i < j ; i++) {
+                        console.log(plugin_info.engines[i].name, 'version:', plugin_info.engines[i].version);
+                    }
+                }
+            }
+        });
+    },
+
+    'publish'  : function(cli_opts) {
+        var plugin_path = cli_opts.argv.remain;
+        if(!plugin_path) {
+            return console.log(plugman.help());
+        }
+        plugman.publish(plugin_path, function(err) {
+            if (err) throw err;
+            else console.log('Plugin published');
+        });
+    },
+
+    'unpublish': function(cli_opts) {
+        var plugin = cli_opts.argv.remain;
+        if(!plugin) {
+            return console.log(plugman.help());
+        }
+        plugman.unpublish(plugin, function(err) {
+            if (err) throw err;
+            else console.log('Plugin unpublished');
+        });
+    },
+    'create': function(cli_opts) {
+        if( !cli_opts.name || !cli_opts.plugin_id || !cli_opts.plugin_version) {
+            return console.log( plugman.help() );
+        }
+        var cli_variables = {};
+        if (cli_opts.variable) {
+            cli_opts.variable.forEach(function (variable) {
+                    var tokens = variable.split('=');
+                    var key = tokens.shift().toUpperCase();
+                    if (/^[\w-_]+$/.test(key)) cli_variables[key] = tokens.join('=');
+                    });
+        }
+        plugman.create( cli_opts.name, cli_opts.plugin_id, cli_opts.plugin_version, cli_opts.path || ".", cli_variables );
+    },
+    'platform': function(cli_opts) {
+        var operation = cli_opts.argv.remain[ 0 ] || "";
+        if( ( operation !== 'add' && operation !== 'remove' ) ||  !cli_opts.platform_name ) {
+            return console.log( plugman.help() );
+        }
+        plugman.platform( { operation: operation, platform_name: cli_opts.platform_name } );
+    }
+};
+
+module.exports = plugman;

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/prepare.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/prepare.js b/cordova-lib/src/plugman/prepare.js
new file mode 100644
index 0000000..c650306
--- /dev/null
+++ b/cordova-lib/src/plugman/prepare.js
@@ -0,0 +1,232 @@
+/**
+    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 node:true */
+
+var platform_modules = require('./platforms'),
+    path            = require('path'),
+    config_changes  = require('./util/config-changes'),
+    xml_helpers     = require('./util/xml-helpers'),
+    wp7             = require('./platforms/wp7'),
+    wp8             = require('./platforms/wp8'),
+    windows8        = require('./platforms/windows8'),
+    common          = require('./platforms/common');
+    fs              = require('fs'),
+    shell           = require('shelljs'),
+    util            = require('util'),
+    events          = require('./events'),
+    plugman         = require('../plugman'),
+    et              = require('elementtree');
+
+// Called on --prepare.
+// Sets up each plugin's Javascript code to be loaded properly.
+// Expects a path to the project (platforms/android in CLI, . in plugman-only),
+// a path to where the plugins are downloaded, the www dir, and the platform ('android', 'ios', etc.).
+module.exports = function handlePrepare(project_dir, platform, plugins_dir, www_dir) {
+    // Process:
+    // - Do config munging by calling into config-changes module
+    // - List all plugins in plugins_dir
+    // - Load and parse their plugin.xml files.
+    // - Skip those without support for this platform. (No <platform> tags means JS-only!)
+    // - Build a list of all their js-modules, including platform-specific js-modules.
+    // - For each js-module (general first, then platform) build up an object storing the path and any clobbers, merges and runs for it.
+    // - Write this object into www/cordova_plugins.json.
+    // - Cordova.js contains code to load them at runtime from that file.
+    events.emit('verbose', 'Preparing ' + platform + ' project');
+    var platform_json = config_changes.get_platform_json(plugins_dir, platform);
+    var wwwDir = www_dir || platform_modules[platform].www_dir(project_dir);
+
+    // Check if there are any plugins queued for uninstallation, and if so, remove any of their plugin web assets loaded in
+    // via <js-module> elements
+    var plugins_to_uninstall = platform_json.prepare_queue.uninstalled;
+    if (plugins_to_uninstall && plugins_to_uninstall.length) {
+        var plugins_www = path.join(wwwDir, 'plugins');
+        if (fs.existsSync(plugins_www)) {
+            plugins_to_uninstall.forEach(function(plug) {
+                var id = plug.id;
+                var plugin_modules = path.join(plugins_www, id);
+                if (fs.existsSync(plugin_modules)) {
+                    events.emit('verbose', 'Removing plugins directory from www "'+plugin_modules+'"');
+                    shell.rm('-rf', plugin_modules);
+                }
+            });
+        }
+    }
+
+    events.emit('verbose', 'Processing configuration changes for plugins.');
+    config_changes.process(plugins_dir, project_dir, platform);
+
+    // for windows phone platform we need to add all www resources to the .csproj file
+    // first we need to remove them all to prevent duplicates
+    var wp_csproj;
+    if(platform == 'wp7' || platform == 'wp8') {
+        wp_csproj = (platform == wp7? wp7.parseProjectFile(project_dir) : wp8.parseProjectFile(project_dir));
+        var item_groups = wp_csproj.xml.findall('ItemGroup');
+        for (var i = 0, l = item_groups.length; i < l; i++) {
+            var group = item_groups[i];
+            var files = group.findall('Content');
+            for (var j = 0, k = files.length; j < k; j++) {
+                var file = files[j];
+                if (file.attrib.Include.substr(0,11) == "www\\plugins" || file.attrib.Include == "www\\cordova_plugins.js") {
+                    // remove file reference
+                    group.remove(0, file);
+                    // remove ItemGroup if empty
+                    var new_group = group.findall('Content');
+                    if(new_group.length < 1) {
+                        wp_csproj.xml.getroot().remove(0, group);
+                    }
+                }
+            }
+        }
+    }
+    else if(platform == "windows8") {
+        wp_csproj = windows8.parseProjectFile(project_dir);
+        var item_groups = wp_csproj.xml.findall('ItemGroup');
+        for (var i = 0, l = item_groups.length; i < l; i++) {
+            var group = item_groups[i];
+            var files = group.findall('Content');
+            for (var j = 0, k = files.length; j < k; j++) {
+                var file = files[j];
+                if (file.attrib.Include.substr(0,11) == "www\\plugins" || file.attrib.Include == "www\\cordova_plugins.js") {
+                    // remove file reference
+                    group.remove(0, file);
+                    // remove ItemGroup if empty
+                    var new_group = group.findall('Content');
+                    if(new_group.length < 1) {
+                        wp_csproj.xml.getroot().remove(0, group);
+                    }
+                }
+            }
+        }
+
+    }
+
+    platform_json = config_changes.get_platform_json(plugins_dir, platform);
+    // This array holds all the metadata for each module and ends up in cordova_plugins.json
+    var plugins = Object.keys(platform_json.installed_plugins).concat(Object.keys(platform_json.dependent_plugins));
+    var moduleObjects = [];
+    var pluginMetadata = {};
+    events.emit('verbose', 'Iterating over installed plugins:', plugins);
+
+    plugins && plugins.forEach(function(plugin) {
+        var pluginDir = path.join(plugins_dir, plugin),
+            pluginXML = path.join(pluginDir, 'plugin.xml');
+        if (!fs.existsSync(pluginXML)) {
+            plugman.emit('warn', 'Missing file: ' + pluginXML);
+            return;
+        }
+        var xml = xml_helpers.parseElementtreeSync(pluginXML);
+
+        var plugin_id = xml.getroot().attrib.id;
+
+        // pluginMetadata is a mapping from plugin IDs to versions.
+        pluginMetadata[plugin_id] = xml.getroot().attrib.version;
+
+        // add the plugins dir to the platform's www.
+        var platformPluginsDir = path.join(wwwDir, 'plugins');
+        // XXX this should not be here if there are no js-module. It leaves an empty plugins/ directory
+        shell.mkdir('-p', platformPluginsDir);
+
+        var jsModules = xml.findall('./js-module');
+        var assets = xml.findall('asset');
+        var platformTag = xml.find(util.format('./platform[@name="%s"]', platform));
+
+        if (platformTag) {
+            assets = assets.concat(platformTag.findall('./asset'));
+            jsModules = jsModules.concat(platformTag.findall('./js-module'));
+        }
+
+        // Copy www assets described in <asset> tags.
+        assets = assets || [];
+        assets.forEach(function(asset) {
+            common.asset.install(asset, pluginDir, wwwDir);
+        });
+
+        jsModules.forEach(function(module) {
+            // Copy the plugin's files into the www directory.
+            // NB: We can't always use path.* functions here, because they will use platform slashes.
+            // But the path in the plugin.xml and in the cordova_plugins.js should be always forward slashes.
+            var pathParts = module.attrib.src.split('/');
+
+            var fsDirname = path.join.apply(path, pathParts.slice(0, -1));
+            var fsDir = path.join(platformPluginsDir, plugin_id, fsDirname);
+            shell.mkdir('-p', fsDir);
+
+            // Read in the file, prepend the cordova.define, and write it back out.
+            var moduleName = plugin_id + '.';
+            if (module.attrib.name) {
+                moduleName += module.attrib.name;
+            } else {
+                var result = module.attrib.src.match(/([^\/]+)\.js/);
+                moduleName += result[1];
+            }
+
+            var fsPath = path.join.apply(path, pathParts);
+            var scriptContent = fs.readFileSync(path.join(pluginDir, fsPath), 'utf-8');
+            scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) { ' + scriptContent + '\n});\n';
+            fs.writeFileSync(path.join(platformPluginsDir, plugin_id, fsPath), scriptContent, 'utf-8');
+            if(platform == 'wp7' || platform == 'wp8' || platform == "windows8") {
+                wp_csproj.addSourceFile(path.join('www', 'plugins', plugin_id, fsPath));
+            }
+
+            // Prepare the object for cordova_plugins.json.
+            var obj = {
+                file: ['plugins', plugin_id, module.attrib.src].join('/'),
+                id: moduleName
+            };
+
+            // Loop over the children of the js-module tag, collecting clobbers, merges and runs.
+            module.getchildren().forEach(function(child) {
+                if (child.tag.toLowerCase() == 'clobbers') {
+                    if (!obj.clobbers) {
+                        obj.clobbers = [];
+                    }
+                    obj.clobbers.push(child.attrib.target);
+                } else if (child.tag.toLowerCase() == 'merges') {
+                    if (!obj.merges) {
+                        obj.merges = [];
+                    }
+                    obj.merges.push(child.attrib.target);
+                } else if (child.tag.toLowerCase() == 'runs') {
+                    obj.runs = true;
+                }
+            });
+
+            // Add it to the list of module objects bound for cordova_plugins.json
+            moduleObjects.push(obj);
+        });
+    });
+
+    // Write out moduleObjects as JSON wrapped in a cordova module to cordova_plugins.js
+    var final_contents = "cordova.define('cordova/plugin_list', function(require, exports, module) {\n";
+    final_contents += 'module.exports = ' + JSON.stringify(moduleObjects,null,'    ') + ';\n';
+    final_contents += 'module.exports.metadata = \n';
+    final_contents += '// TOP OF METADATA\n';
+    final_contents += JSON.stringify(pluginMetadata, null, '    ') + '\n';
+    final_contents += '// BOTTOM OF METADATA\n';
+    final_contents += '});'; // Close cordova.define.
+
+    events.emit('verbose', 'Writing out cordova_plugins.js...');
+    fs.writeFileSync(path.join(wwwDir, 'cordova_plugins.js'), final_contents, 'utf-8');
+
+    if(platform == 'wp7' || platform == 'wp8' || platform == "windows8") {
+        wp_csproj.addSourceFile(path.join('www', 'cordova_plugins.js'));
+        wp_csproj.write();
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/publish.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/publish.js b/cordova-lib/src/plugman/publish.js
new file mode 100644
index 0000000..05b5284
--- /dev/null
+++ b/cordova-lib/src/plugman/publish.js
@@ -0,0 +1,6 @@
+var registry = require('./registry/registry')
+
+module.exports = function(plugin_path) {
+    // plugin_path is an array of paths
+    return registry.publish(plugin_path);
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/registry/manifest.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/registry/manifest.js b/cordova-lib/src/plugman/registry/manifest.js
new file mode 100644
index 0000000..54f74b6
--- /dev/null
+++ b/cordova-lib/src/plugman/registry/manifest.js
@@ -0,0 +1,95 @@
+var xml_helpers = require('../util/xml-helpers'),
+    path = require('path'),
+    Q = require('q'),
+    fs = require('fs'),
+    whitelist = require('./whitelist');
+
+function validateName(name) {
+    if (!name.match(/^(\w+\.){2,}.*$/)) {
+        throw new Error('Invalid plugin ID. It has to follow the reverse domain `com.domain.plugin` format');
+    }
+
+    if (name.match(/org.apache.cordova\..*/) && whitelist.indexOf(name) === -1) {
+        throw new Error('Invalid Plugin ID. The "org.apache.cordova" prefix is reserved for plugins provided directly by the Cordova project.');
+    }
+
+    return true;
+}
+
+// Java world big-up!
+// Returns a promise.
+function generatePackageJsonFromPluginXml(plugin_path) {
+    return Q().then(function() {
+        var package_json = {};
+        var pluginXml = xml_helpers.parseElementtreeSync(path.join(plugin_path, 'plugin.xml'));
+
+        if(!pluginXml) throw new Error('invalid plugin.xml document');
+
+        var pluginElm = pluginXml.getroot();
+
+        if(!pluginElm) throw new Error('invalid plugin.xml document');
+
+        // REQUIRED: name, version
+        // OPTIONAL: description, license, keywords, engine
+        var name = pluginElm.attrib.id,
+            version = pluginElm.attrib.version,
+            cordova_name = pluginElm.findtext('name'),
+            description = pluginElm.findtext('description'),
+            license = pluginElm.findtext('license'),
+            keywords = pluginElm.findtext('keywords'),
+            repo = pluginElm.findtext('repo'),
+            issue = pluginElm.findtext('issue'),
+            engines = pluginElm.findall('engines/engine'),
+            platformsElm = pluginElm.findall('platform'),
+            englishdoc = "",
+            platforms = [];
+
+        platformsElm.forEach(function(plat){
+            platforms.push(plat.attrib.name);
+        })
+        if(!version) throw new Error('`version` required');
+
+        package_json.version = version;
+
+        if(!name) throw new Error('`id` is required');
+
+        validateName(name);
+
+        package_json.name = name.toLowerCase();
+
+        if(cordova_name) package_json.cordova_name = cordova_name;
+        if(description)  package_json.description  = description;
+        if(license)      package_json.license      = license;
+        if(repo)         package_json.repo         = repo;
+        if(issue)        package_json.issue        = issue;
+        if(keywords)     package_json.keywords     = keywords.split(',');
+        if(platforms)    package_json.platforms    = platforms;
+
+        // adding engines
+        if(engines) {
+            package_json.engines = [];
+            for(var i = 0, j = engines.length ; i < j ; i++) {
+                package_json.engines.push({name: engines[i].attrib.name, version: engines[i].attrib.version});
+            }
+        }
+
+        //set docs_path to doc/index.md exists
+        var docs_path = path.resolve(plugin_path, 'doc/index.md');
+        if(!(fs.existsSync(docs_path))){
+            //set docs_path to doc/en/index.md
+            docs_path = path.resolve(plugin_path, 'doc/en/index.md');
+        }
+        if(fs.existsSync(docs_path)){
+            englishdoc = fs.readFileSync(docs_path, 'utf-8');
+            package_json.englishdoc = englishdoc;
+        }
+
+        // write package.json
+        var package_json_path = path.resolve(plugin_path, 'package.json');
+        //console.log('about to write package.json');
+        fs.writeFileSync(package_json_path, JSON.stringify(package_json, null, 4), 'utf8');
+        return package_json;
+    });
+}
+
+module.exports.generatePackageJsonFromPluginXml = generatePackageJsonFromPluginXml;

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/registry/registry.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/registry/registry.js b/cordova-lib/src/plugman/registry/registry.js
new file mode 100644
index 0000000..e0d733d
--- /dev/null
+++ b/cordova-lib/src/plugman/registry/registry.js
@@ -0,0 +1,290 @@
+var npm = require('npm'),
+    path = require('path'),
+    http = require('http'),
+    url = require('url'),
+    fs = require('fs'),
+    manifest = require('./manifest'),
+    os = require('os'),
+    rc = require('rc'),
+    Q = require('q'),
+    request = require('request'),
+    zlib = require('zlib'),
+    tar = require('tar'),
+    shell = require('shelljs'),
+    home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE,
+    plugmanConfigDir = path.resolve(home, '.plugman'),
+    plugmanCacheDir = path.resolve(plugmanConfigDir, 'cache');
+
+/**
+ * @method getPackageInfo
+ * @param {String} args Package names
+ * @return {Promise.<Object>} Promised package info.
+ */
+function getPackageInfo(args) {
+    var thing = args.length ? args.shift().split("@") : [],
+        name = thing.shift(),
+        version = thing.join("@") || 'latest';
+    var settings = module.exports.settings;
+
+    var d = Q.defer();
+    var req = makeRequest('GET', settings.registry + '/' + name + '/' + version, function(err, res, body){
+        if(err || res.statusCode != 200) {
+          d.reject(new Error('Failed to fetch package information for '+name));
+        } else {
+          d.resolve(JSON.parse(body));
+        }
+    });
+    req.on('error', function(err) {
+        d.reject(err);
+    });
+    return d.promise;
+}
+
+/**
+ * @method fetchPackage
+ * @param {String} info Package info
+ * @return {Promise.<string>} Promised path to the package.
+ */
+function fetchPackage(info, cl) {
+    var settings = module.exports.settings;
+    var d = Q.defer();
+    var cached = path.resolve(settings.cache, info.name, info.version, 'package');
+    if(fs.existsSync(cached)) {
+        d.resolve(cached);
+    } else {
+        var download_dir = path.resolve(cached, '..');
+        shell.mkdir('-p', download_dir);
+
+        var req = makeRequest('GET', info.dist.tarball, function (err, res, body) {
+            if(err || res.statusCode != 200) {
+                d.reject(new Error('failed to fetch the plugin archive'));
+            } else {
+                // Update the download count for this plugin.
+                // Fingers crossed that the timestamps are unique, and that no plugin is downloaded
+                // twice in a single millisecond.
+                //
+                // This is acceptable, because the failure mode is Couch gracefully rejecting the second one
+                // (for lacking a _rev), and dropped a download count is not important.
+                var now = new Date();
+                var pkgId = info._id.substring(0, info._id.indexOf('@'));
+                var message = {
+                    day: now.getUTCFullYear() + '-' + (now.getUTCMonth()+1) + '-' + now.getUTCDate(),
+                    pkg: pkgId,
+                    client: cl
+                };
+                var remote = settings.registry + '/downloads'
+
+                makeRequest('POST', remote, message, function (err, res, body) {
+                    // ignore errors
+                });
+            }
+        });
+        req.pipe(zlib.createUnzip())
+        .pipe(tar.Extract({path:download_dir}))
+        .on('error', function(err) {
+            shell.rm('-rf', download_dir);
+            d.reject(err);
+        })
+        .on('end', function() {
+            d.resolve(path.resolve(download_dir, 'package'));
+        });
+    }
+    return d.promise;
+}
+
+module.exports = {
+    settings: null,
+    /**
+     * @method config
+     * @param {Array} args Command argument
+     * @return {Promise.<Object>} Promised configuration object.
+     */
+    config: function(args) {
+        return initSettings().then(function(settings) {
+            return Q.ninvoke(npm, 'load', settings)
+        })
+        .then(function() {
+            return Q.ninvoke(npm.commands, 'config', args);
+        });
+    },
+
+    /**
+     * @method owner
+     * @param {Array} args Command argument
+     * @return {Promise.<void>} Promise for completion.
+     */
+    owner: function(args) {
+        return initSettings().then(function(settings) {
+            return Q.ninvoke(npm, 'load', settings);
+        }).then(function() {
+            return Q.ninvoke(npm.commands, 'owner', args);
+        });
+    },
+    /**
+     * @method adduser
+     * @param {Array} args Command argument
+     * @return {Promise.<void>} Promise for completion.
+     */
+    adduser: function(args) {
+        return initSettings().then(function(settings) {
+            return Q.ninvoke(npm, 'load', settings)
+        })
+        .then(function() {
+            return Q.ninvoke(npm.commands, 'adduser', args);
+        });
+    },
+
+    /**
+     * @method publish
+     * @param {Array} args Command argument
+     * @return {Promise.<Object>} Promised published data.
+     */
+    publish: function(args) {
+        return initSettings()
+        .then(function(settings) {
+            return manifest.generatePackageJsonFromPluginXml(args[0])
+            .then(function() {
+                return Q.ninvoke(npm, 'load', settings);
+            }).then(function() {
+                return Q.ninvoke(npm.commands, 'publish', args)
+            }).fin(function() {
+                fs.unlink(path.resolve(args[0], 'package.json'));
+            });
+        });
+    },
+
+    /**
+     * @method search
+     * @param {Array} args Array of keywords
+     * @return {Promise.<Object>} Promised search results.
+     */
+    search: function(args) {
+        return initSettings()
+        .then(function(settings) {
+            return Q.ninvoke(npm, 'load', settings);
+        }).then(function() {
+            return Q.ninvoke(npm.commands, 'search', args, true);
+        });
+    },
+
+    /**
+     * @method unpublish
+     * @param {Array} args Command argument
+     * @return {Promise.<Object>} Promised results.
+     */
+    unpublish: function(args) {
+        return initSettings()
+        .then(function(settings) {
+            return Q.ninvoke(npm, 'load', settings);
+        }).then(function() {
+            return Q.ninvoke(npm.commands, 'unpublish', args);
+        }).then(function() {
+            return Q.ninvoke(npm.commands, 'cache', ["clean"]);
+        });
+    },
+
+    /**
+     * @method fetch
+     * @param {String} name Plugin name
+     * @return {Promise.<string>} Promised path to fetched package.
+     */
+    fetch: function(args, client) {
+        var cl = (client === 'plugman' ? 'plugman' : 'cordova-cli');
+        return initSettings()
+        .then(function(settings) {
+            return getPackageInfo(args);
+        }).then(function(info) {
+            return fetchPackage(info, cl);
+        });
+    },
+
+    /**
+     * @method info
+     * @param {String} name Plugin name
+     * @return {Promise.<Object>} Promised package info.
+     */
+    info: function(args) {
+        return initSettings()
+        .then(function() {
+            return getPackageInfo(args);
+        });
+    }
+}
+
+/**
+ * @method initSettings
+ * @return {Promise.<Object>} Promised settings.
+ */
+function initSettings() {
+    var settings = module.exports.settings;
+    // check if settings already set
+    if(settings != null) return Q(settings);
+
+    // setting up settings
+    // obviously if settings dir does not exist settings is going to be empty
+    if(!fs.existsSync(plugmanConfigDir)) {
+        fs.mkdirSync(plugmanConfigDir);
+        fs.mkdirSync(plugmanCacheDir);
+    }
+
+    settings =
+    module.exports.settings =
+    rc('plugman', {
+         cache: plugmanCacheDir,
+         force: true,
+         registry: 'http://registry.cordova.io',
+         logstream: fs.createWriteStream(path.resolve(plugmanConfigDir, 'plugman.log')),
+         userconfig: path.resolve(plugmanConfigDir, 'config')
+    });
+    return Q(settings);
+}
+
+
+function makeRequest (method, where, what, cb_) {
+  var settings = module.exports.settings
+  var remote = url.parse(where)
+  if (typeof cb_ !== "function") cb_ = what, what = null
+  var cbCalled = false
+  function cb () {
+    if (cbCalled) return
+    cbCalled = true
+    cb_.apply(null, arguments)
+  }
+
+  var strict = settings['strict-ssl']
+  if (strict === undefined) strict = true
+  var opts = { url: remote
+             , method: method
+             , ca: settings.ca
+             , strictSSL: strict }
+    , headers = opts.headers = {}
+
+  headers.accept = "application/json"
+
+  headers["user-agent"] = settings['user-agent'] ||
+                          'node/' + process.version
+
+  var p = settings.proxy
+  var sp = settings['https-proxy'] || p
+  opts.proxy = remote.protocol === "https:" ? sp : p
+
+  // figure out wth 'what' is
+  if (what) {
+    if (Buffer.isBuffer(what) || typeof what === "string") {
+      opts.body = what
+      headers["content-type"] = "application/json"
+      headers["content-length"] = Buffer.byteLength(what)
+    } else {
+      opts.json = what
+    }
+  }
+
+  var req = request(opts, cb)
+
+  req.on("error", cb)
+  req.on("socket", function (s) {
+    s.on("error", cb)
+  })
+
+  return req
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/registry/whitelist.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/registry/whitelist.js b/cordova-lib/src/plugman/registry/whitelist.js
new file mode 100644
index 0000000..5e7cfa6
--- /dev/null
+++ b/cordova-lib/src/plugman/registry/whitelist.js
@@ -0,0 +1,21 @@
+module.exports = [
+    'org.apache.cordova.splashscreen',
+    'org.apache.cordova.network-information',
+    'org.apache.cordova.file',
+    'org.apache.cordova.file-transfer',
+    'org.apache.cordova.media',
+    'org.apache.cordova.vibration',
+    'org.apache.cordova.media-capture',
+    'org.apache.cordova.inappbrowser',
+    'org.apache.cordova.globalization',
+    'org.apache.cordova.geolocation',
+    'org.apache.cordova.dialogs',
+    'org.apache.cordova.device-orientation',
+    'org.apache.cordova.device',
+    'org.apache.cordova.contacts',
+    'org.apache.cordova.console',
+    'org.apache.cordova.camera',
+    'org.apache.cordova.device-motion',
+    'org.apache.cordova.battery-status',
+    'org.apache.cordova.statusbar'
+]

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/search.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/search.js b/cordova-lib/src/plugman/search.js
new file mode 100644
index 0000000..000f58e
--- /dev/null
+++ b/cordova-lib/src/plugman/search.js
@@ -0,0 +1,5 @@
+var registry = require('./registry/registry')
+
+module.exports = function(search_opts) {
+    return registry.search(search_opts);
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/uninstall.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/uninstall.js b/cordova-lib/src/plugman/uninstall.js
new file mode 100644
index 0000000..04ecb1a
--- /dev/null
+++ b/cordova-lib/src/plugman/uninstall.js
@@ -0,0 +1,288 @@
+
+var path = require('path'),
+    fs   = require('fs'),
+    et   = require('elementtree'),
+    shell= require('shelljs'),
+    config_changes = require('./util/config-changes'),
+    xml_helpers = require('./util/xml-helpers'),
+    action_stack = require('./util/action-stack'),
+    dependencies = require('./util/dependencies'),
+    underscore = require('underscore'),
+    Q = require('q'),
+    plugins = require('./util/plugins'),
+    underscore = require('underscore'),
+    events = require('./events'),
+    platform_modules = require('./platforms'),
+    plugman = require('../plugman');
+
+// possible options: cli_variables, www_dir
+// Returns a promise.
+module.exports = function(platform, project_dir, id, plugins_dir, options) {
+    options = options || {};
+    options.is_top_level = true;
+    plugins_dir = plugins_dir || path.join(project_dir, 'cordova', 'plugins');
+
+    // Allow path to file to grab an ID
+    var xml_path = path.join(id, 'plugin.xml');
+    if ( fs.existsSync(xml_path) ) {
+        var plugin_et  = xml_helpers.parseElementtreeSync(xml_path),
+        id = plugin_et._root.attrib['id'];
+    }
+
+    return module.exports.uninstallPlatform(platform, project_dir, id, plugins_dir, options)
+    .then(function() {
+        return module.exports.uninstallPlugin(id, plugins_dir, options);
+    });
+}
+
+// Returns a promise.
+module.exports.uninstallPlatform = function(platform, project_dir, id, plugins_dir, options) {
+    options = options || {};
+    options.is_top_level = true;
+    plugins_dir = plugins_dir || path.join(project_dir, 'cordova', 'plugins');
+
+    if (!platform_modules[platform]) {
+        return Q.reject(new Error(platform + " not supported."));
+    }
+
+    var plugin_dir = path.join(plugins_dir, id);
+    if (!fs.existsSync(plugin_dir)) {
+        return Q.reject(new Error('Plugin "' + id + '" not found. Already uninstalled?'));
+    }
+
+    var current_stack = new action_stack();
+
+    return runUninstallPlatform(current_stack, platform, project_dir, plugin_dir, plugins_dir, options);
+};
+
+// Returns a promise.
+module.exports.uninstallPlugin = function(id, plugins_dir, options) {
+    options = options || {};
+
+    var plugin_dir = path.join(plugins_dir, id);
+
+    // @tests - important this event is checked spec/uninstall.spec.js
+    events.emit('log', 'Removing "'+ id +'"');
+
+    // If already removed, skip.
+    if ( !fs.existsSync(plugin_dir) ) {
+        events.emit('verbose', 'Plugin "'+ id +'" already removed ('+ plugin_dir +')');
+        return Q();
+    }
+
+    var xml_path  = path.join(plugin_dir, 'plugin.xml')
+      , plugin_et = xml_helpers.parseElementtreeSync(xml_path);
+
+    var doDelete = function(id) {
+        var plugin_dir = path.join(plugins_dir, id);
+        if ( !fs.existsSync(plugin_dir) ) {
+            events.emit('verbose', 'Plugin "'+ id +'" already removed ('+ plugin_dir +')');
+            return Q();
+        }
+
+        shell.rm('-rf', plugin_dir);
+        events.emit('verbose', 'Deleted "'+ id +'"');
+    };
+
+    // We've now lost the metadata for the plugins that have been uninstalled, so we can't use that info.
+    // Instead, we list all dependencies of the target plugin, and check the remaining metadata to see if
+    // anything depends on them, or if they're listed as top-level.
+    // If neither, they can be deleted.
+    var top_plugin_id = id;
+
+    // Recursively remove plugins which were installed as dependents (that are not top-level)
+    // optional?
+    var recursive = true;
+    var toDelete = recursive ? plugin_et.findall('dependency') : [];
+    toDelete = toDelete && toDelete.length ? toDelete.map(function(p) { return p.attrib.id; }) : [];
+    toDelete.push(top_plugin_id);
+
+    // Okay, now we check if any of these are depended on, or top-level.
+    // Find the installed platforms by whether they have a metadata file.
+    var platforms = Object.keys(platform_modules).filter(function(platform) {
+        return fs.existsSync(path.join(plugins_dir, platform + '.json'));
+    });
+
+    // Can have missing plugins on some platforms when not supported..
+    var dependList = {};
+    platforms.forEach(function(platform) {
+        var depsInfo = dependencies.generate_dependency_info(plugins_dir, platform);
+        var tlps = depsInfo.top_level_plugins,
+            deps, i;
+
+        // Top-level deps must always be explicitely asked to remove by user
+        tlps.forEach(function(plugin_id){
+            if(top_plugin_id == plugin_id)
+                return;
+
+            var i = toDelete.indexOf(plugin_id);
+            if(i >= 0)
+                toDelete.splice(i, 1);
+        });
+
+        toDelete.forEach(function(plugin) {
+            deps = dependencies.dependents(plugin, depsInfo);
+
+            var i = deps.indexOf(top_plugin_id);
+            if(i >= 0)
+                 deps.splice(i, 1); // remove current/top-level plugin as blocking uninstall
+
+            if(deps.length) {
+                dependList[plugin] = deps.join(', ');
+            }
+        });
+    });
+
+    var i, plugin_id, msg;
+    for(i in toDelete) {
+        plugin_id = toDelete[i];
+
+        if( dependList[plugin_id] ) {
+            msg = '"' + plugin_id + '" is required by ('+ dependList[plugin_id] + ')';
+            if(options.force) {
+                events.emit('log', msg +' but forcing removal.');
+            } else {
+                // @tests - error and event message is checked spec/uninstall.spec.js
+                msg += ' and cannot be removed (hint: use -f or --force)';
+
+                if(plugin_id == top_plugin_id) {
+                    return Q.reject( new Error(msg) );
+                } else {
+                    events.emit('warn', msg +' and cannot be removed (hint: use -f or --force)');
+                    continue;
+                }
+            }
+        }
+
+        doDelete(plugin_id);
+    }
+
+    return Q();
+};
+
+// possible options: cli_variables, www_dir, is_top_level
+// Returns a promise
+function runUninstallPlatform(actions, platform, project_dir, plugin_dir, plugins_dir, options) {
+    options = options || {};
+
+    var xml_path     = path.join(plugin_dir, 'plugin.xml');
+    var plugin_et    = xml_helpers.parseElementtreeSync(xml_path);
+    var plugin_id    = plugin_et._root.attrib['id'];
+
+    // Deps info can be passed recusively
+    var depsInfo = options.depsInfo || dependencies.generate_dependency_info(plugins_dir, platform, 'remove');
+
+    // Check that this plugin has no dependents.
+    var dependents = dependencies.dependents(plugin_id, depsInfo, platform);
+
+    if(options.is_top_level && dependents && dependents.length > 0) {
+        var msg = "The plugin '"+ plugin_id +"' is required by (" + dependents.join(', ') + ")";
+        if(options.force) {
+            events.emit("info", msg + " but forcing removal");
+        } else {
+            return Q.reject( new Error(msg + ", skipping uninstallation.") );
+        }
+    }
+
+    // Check how many dangling dependencies this plugin has.
+    var deps = depsInfo.graph.getChain(plugin_id);
+    var danglers = dependencies.danglers(plugin_id, depsInfo, platform);
+
+    var promise;
+    if (deps && deps.length && danglers && danglers.length) {
+
+        // @tests - important this event is checked spec/uninstall.spec.js
+        events.emit('log', 'Uninstalling ' + danglers.length + ' dependent plugins.');
+        promise = Q.all(
+            danglers.map(function(dangler) {
+                var dependent_path = dependencies.resolvePath(dangler, plugins_dir);
+
+                var opts = underscore.extend({}, options, {
+                    is_top_level: depsInfo.top_level_plugins.indexOf(dangler) > -1,
+                    depsInfo: depsInfo
+                });
+
+                return runUninstallPlatform(actions, platform, project_dir, dependent_path, plugins_dir, opts);
+            })
+        );
+    } else {
+        promise = Q();
+    }
+
+    return promise.then(function() {
+        return handleUninstall(actions, platform, plugin_id, plugin_et, project_dir, options.www_dir, plugins_dir, plugin_dir, options.is_top_level);
+    });
+}
+
+// Returns a promise.
+function handleUninstall(actions, platform, plugin_id, plugin_et, project_dir, www_dir, plugins_dir, plugin_dir, is_top_level) {
+    var platform_modules = require('./platforms');
+    var handler = platform_modules[platform];
+    var platformTag = plugin_et.find('./platform[@name="'+platform+'"]');
+    www_dir = www_dir || handler.www_dir(project_dir);
+    events.emit('log', 'Uninstalling ' + plugin_id + ' from ' + platform);
+
+    var assets = plugin_et.findall('./asset');
+    if (platformTag) {
+        var sourceFiles = platformTag.findall('./source-file'),
+            headerFiles = platformTag.findall('./header-file'),
+            libFiles = platformTag.findall('./lib-file'),
+            resourceFiles = platformTag.findall('./resource-file');
+            frameworkFiles = platformTag.findall('./framework[@custom="true"]');
+        assets = assets.concat(platformTag.findall('./asset'));
+
+        // queue up native stuff
+        sourceFiles && sourceFiles.forEach(function(source) {
+            actions.push(actions.createAction(handler["source-file"].uninstall,
+                                             [source, project_dir, plugin_id],
+                                             handler["source-file"].install,
+                                             [source, plugin_dir, project_dir, plugin_id]));
+        });
+
+        headerFiles && headerFiles.forEach(function(header) {
+            actions.push(actions.createAction(handler["header-file"].uninstall,
+                                             [header, project_dir, plugin_id],
+                                             handler["header-file"].install,
+                                             [header, plugin_dir, project_dir, plugin_id]));
+        });
+
+        resourceFiles && resourceFiles.forEach(function(resource) {
+            actions.push(actions.createAction(handler["resource-file"].uninstall,
+                                              [resource, project_dir, plugin_id],
+                                              handler["resource-file"].install,
+                                              [resource, plugin_dir, project_dir]));
+        });
+
+        // CB-5238 custom frameworks only
+        frameworkFiles && frameworkFiles.forEach(function(framework) {
+            actions.push(actions.createAction(handler["framework"].uninstall,
+                                              [framework, project_dir, plugin_id],
+                                              handler["framework"].install,
+                                              [framework, plugin_dir, project_dir]));
+        });
+
+        libFiles && libFiles.forEach(function(source) {
+            actions.push(actions.createAction(handler["lib-file"].uninstall,
+                                              [source, project_dir, plugin_id],
+                                              handler["lib-file"].install,
+                                              [source, plugin_dir, project_dir, plugin_id]));
+        });
+    }
+
+    // queue up asset installation
+    var common = require('./platforms/common');
+    assets && assets.forEach(function(asset) {
+        actions.push(actions.createAction(common.asset.uninstall, [asset, www_dir, plugin_id], common.asset.install, [asset, plugin_dir, www_dir]));
+    });
+
+    // run through the action stack
+    return actions.process(platform, project_dir)
+    .then(function() {
+        // WIN!
+        events.emit('verbose', plugin_id + ' uninstalled from ' + platform + '.');
+        // queue up the plugin so prepare can remove the config changes
+        config_changes.add_uninstalled_plugin_to_prepare_queue(plugins_dir, plugin_id, platform, is_top_level);
+        // call prepare after a successful uninstall
+        plugman.prepare(project_dir, platform, plugins_dir, www_dir);
+    });
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/unpublish.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/unpublish.js b/cordova-lib/src/plugman/unpublish.js
new file mode 100644
index 0000000..be1b4b4
--- /dev/null
+++ b/cordova-lib/src/plugman/unpublish.js
@@ -0,0 +1,5 @@
+var registry = require('./registry/registry')
+
+module.exports = function(plugin) {
+    return registry.unpublish(plugin);
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/cordova-lib/src/plugman/util/action-stack.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/util/action-stack.js b/cordova-lib/src/plugman/util/action-stack.js
new file mode 100644
index 0000000..dd9dafb
--- /dev/null
+++ b/cordova-lib/src/plugman/util/action-stack.js
@@ -0,0 +1,85 @@
+var platforms = require("../platforms"),
+    events = require('../events'),
+    Q = require('q'),
+    fs = require('fs');
+
+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, project_dir) {
+        events.emit('verbose', 'Beginning processing of action stack for ' + platform + ' project...');
+        var project_files;
+
+        // parse platform-specific project files once
+        if (platforms[platform].parseProjectFile) {
+            events.emit('verbose', 'Parsing ' + platform + ' project files...');
+            project_files = platforms[platform].parseProjectFile(project_dir);
+        }
+
+        while(this.stack.length) {
+            var action = this.stack.shift();
+            var handler = action.handler.run;
+            var action_params = action.handler.params;
+            if (project_files) {
+                action_params.push(project_files);
+            }
+
+            try {
+                handler.apply(null, action_params);
+            } catch(e) {
+                events.emit('warn', 'Error during processing of action! Attempting to revert...');
+                var incomplete = 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;
+
+                    if (project_files) {
+                        revert_params.push(project_files);
+                    }
+
+                    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.');
+
+        if (project_files) {
+            events.emit('verbose', 'Writing out ' + platform + ' project files...');
+            project_files.write();
+        }
+
+        return Q();
+    }
+};
+
+module.exports = ActionStack;


Mime
View raw message