cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kam...@apache.org
Subject [09/24] Split out cordova-lib: move cordova-cli files
Date Fri, 02 May 2014 18:33:56 GMT
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/b51e1c12/cordova-lib/src/cordova/info.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/info.js b/cordova-lib/src/cordova/info.js
new file mode 100644
index 0000000..c8b8e24
--- /dev/null
+++ b/cordova-lib/src/cordova/info.js
@@ -0,0 +1,111 @@
+/**
+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.
+ */
+
+/*
+A utility funciton to help output the information needed
+when submitting a help request.
+Outputs to a file
+ */
+var cordova_util = require('./util'),
+    superspawn   = require('./superspawn'),
+    package      = require('../package'),
+    path         = require('path'),
+    fs           = require('fs'),
+    Q            = require('q');
+
+// Execute using a child_process exec, for any async command
+function execSpawn(command, args, resultMsg, errorMsg) {
+    return superspawn.spawn(command, args).then(function(result) {
+        return resultMsg + result;
+    }, function(error) {
+        return errorMsg + error;
+    });
+}
+
+function getPlatformInfo(platform, projectRoot) {
+    switch (platform) {
+    case 'ios':
+        return execSpawn('xcodebuild', ['-version'], 'iOS platform:\n\n', 'Error retrieving iOS platform information: ');
+    case 'android':
+        return execSpawn('android', ['list', 'target'], 'Android platform:\n\n', 'Error retrieving Android platform information: ');
+    }
+}
+
+
+module.exports = function info() {
+    //Get projectRoot
+    var projectRoot = cordova_util.cdProjectRoot();
+    var output = '';
+    if (!projectRoot) {
+        return Q.reject(new Error('Current working directory is not a Cordova-based project.'));
+    }
+
+    //Array of functions, Q.allSettled
+    console.log('Collecting Data...\n\n');
+    return Q.allSettled([
+            //Get Node version
+            Q('Node version: ' + process.version),
+            //Get Cordova version
+            Q('Cordova version: ' + package.version),
+            //Get project config.xml file using ano
+            getProjectConfig(projectRoot),
+            //Get list of plugins
+            listPlugins(projectRoot),
+            //Get Platforms information
+            getPlatforms(projectRoot)
+        ]).then(function(promises) {
+        promises.forEach(function(p) {
+            output += p.state === 'fulfilled' ? p.value + '\n\n' : p.reason + '\n\n';
+        });
+        console.info(output);
+        fs.writeFile(path.join(projectRoot, 'info.txt'), output, 'utf-8', function (err) {
+            if (err)
+                throw err;
+        });
+    });
+};
+
+function getPlatforms(projectRoot) {
+    var platforms = cordova_util.listPlatforms(projectRoot);
+    if (platforms.length) {
+        return Q.all(platforms.map(function(p) {
+            return getPlatformInfo(p, projectRoot);
+        })).then(function(outs) {
+          return outs.join('\n\n');
+        });
+    }
+    return Q.reject('No Platforms Currently Installed');
+}
+
+function listPlugins(projectRoot) {
+    var pluginPath = path.join(projectRoot, 'plugins'),
+        plugins    = cordova_util.findPlugins(pluginPath);
+
+    if (!plugins.length) {
+        return Q.reject('No Plugins Currently Installed');
+    }
+    return Q('Plugins: \n\n' + plugins);
+}
+
+function getProjectConfig(projectRoot) {
+    if (!fs.existsSync(projectRoot)  ) {
+        return Q.reject('Config.xml file not found');
+    }
+    return Q('Config.xml file: \n\n' + (fs.readFileSync(cordova_util.projectConfig(projectRoot), 'utf-8')));
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/b51e1c12/cordova-lib/src/cordova/lazy_load.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/lazy_load.js b/cordova-lib/src/cordova/lazy_load.js
new file mode 100644
index 0000000..bd6cf77
--- /dev/null
+++ b/cordova-lib/src/cordova/lazy_load.js
@@ -0,0 +1,151 @@
+/**
+    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 path          = require('path'),
+    fs            = require('fs'),
+    shell         = require('shelljs'),
+    platforms     = require('../platforms'),
+    npmconf       = require('npmconf'),
+    events        = require('./events'),
+    request       = require('request'),
+    config        = require('./config'),
+    hooker        = require('./hooker'),
+    zlib          = require('zlib'),
+    tar           = require('tar'),
+    URL           = require('url'),
+    Q             = require('q'),
+    util          = require('./util');
+
+module.exports = {
+    // Returns a promise for the path to the lazy-loaded directory.
+    cordova:function lazy_load(platform) {
+        if (!(platform in platforms)) {
+            return Q.reject(new Error('Cordova library "' + platform + '" not recognized.'));
+        }
+
+        var url = platforms[platform].url + ';a=snapshot;h=' + platforms[platform].version + ';sf=tgz';
+        return module.exports.custom(url, 'cordova', platform, platforms[platform].version);
+    },
+    // Returns a promise for the path to the lazy-loaded directory.
+    custom:function(url, id, platform, version) {
+        var download_dir;
+        var tmp_dir;
+        var lib_dir;
+
+        // Return early for already-cached remote URL, or for local URLs.
+        var uri = URL.parse(url);
+        var isUri = uri.protocol && uri.protocol[1] != ':'; // second part of conditional is for awesome windows support. fuuu windows
+        if (isUri) {
+            download_dir = (platform == 'wp7' || platform == 'wp8' ? path.join(util.libDirectory, 'wp', id, version) :
+                                                                     path.join(util.libDirectory, platform, id, version));
+            lib_dir = platforms[platform] && platforms[platform].subdirectory && platform !== "blackberry10" ? path.join(download_dir, platforms[platform].subdirectory) : download_dir;
+            if (fs.existsSync(download_dir)) {
+                events.emit('verbose', id + ' library for "' + platform + '" already exists. No need to download. Continuing.');
+                return Q(lib_dir);
+            }
+        } else {
+            // Local path.
+            lib_dir = platforms[platform] && platforms[platform].subdirectory ? path.join(url, platforms[platform].subdirectory) : url;
+            return Q(lib_dir);
+        }
+        return hooker.fire('before_library_download', {
+            platform:platform,
+            url:url,
+            id:id,
+            version:version
+        }).then(function() {
+            var uri = URL.parse(url);
+            var d = Q.defer();
+            npmconf.load(function(err, conf) {
+                // Check if NPM proxy settings are set. If so, include them in the request() call.
+                var proxy;
+                if (uri.protocol == 'https:') {
+                    proxy = conf.get('https-proxy');
+                } else if (uri.protocol == 'http:') {
+                    proxy = conf.get('proxy');
+                }
+                var strictSSL = conf.get('strict-ssl');
+
+                // Create a tmp dir. Using /tmp is a problem because it's often on a different partition and sehll.mv()
+                // fails in this case with "EXDEV, cross-device link not permitted".
+                tmp_subidr = 'tmp_' + id + '_' + process.pid + '_' + (new Date).valueOf();
+                tmp_dir = path.join(util.libDirectory, 'tmp', tmp_subidr);
+                shell.rm('-rf', tmp_dir);
+                shell.mkdir('-p', tmp_dir);
+
+                var size = 0;
+                var request_options = {uri:url};
+                if (proxy) {
+                    request_options.proxy = proxy;
+                }
+                if (typeof strictSSL == 'boolean') {
+                    request_options.strictSSL = strictSSL;
+                }
+                events.emit('verbose', 'Requesting ' + JSON.stringify(request_options) + '...');
+                events.emit('log', 'Downloading ' + id + ' library for ' + platform + '...');
+                var req = request.get(request_options, function(err, res, body) {
+                    if (err) {
+                        shell.rm('-rf', tmp_dir);
+                        d.reject(err);
+                    } else if (res.statusCode != 200) {
+                        shell.rm('-rf', tmp_dir);
+                        d.reject(new Error('HTTP error ' + res.statusCode + ' retrieving version ' + version + ' of ' + id + ' for ' + platform));
+                    } else {
+                        size = body.length;
+                    }
+                });
+
+                req.pipe(zlib.createUnzip())
+                .pipe(tar.Extract({path:tmp_dir}))
+                .on('error', function(err) {
+                    shell.rm('-rf', tmp_dir);
+                    d.reject(err);
+                })
+                .on('end', function() {
+                    events.emit('verbose', 'Downloaded, unzipped and extracted ' + size + ' byte response.');
+                    events.emit('log', 'Download complete');
+                    var entries = fs.readdirSync(tmp_dir);
+                    var entry = path.join(tmp_dir, entries[0]);
+                    shell.mkdir('-p', download_dir);
+                    shell.mv('-f', path.join(entry, (platform=='blackberry10'?'blackberry10':''), '*'), download_dir);
+                    shell.rm('-rf', tmp_dir);
+                    d.resolve(hooker.fire('after_library_download', {
+                        platform:platform,
+                        url:url,
+                        id:id,
+                        version:version,
+                        path: lib_dir,
+                        size:size,
+                        symlink:false
+                    }));
+                });
+            });
+            return d.promise.then(function () { return lib_dir; });
+        });
+    },
+    // Returns a promise for the path to the lazy-loaded directory.
+    based_on_config:function(project_root, platform) {
+        var custom_path = config.has_custom_path(project_root, platform);
+        if (custom_path) {
+            var dot_file = config.read(project_root);
+            return module.exports.custom(dot_file.lib[platform].uri, dot_file.lib[platform].id, platform, dot_file.lib[platform].version);
+        } else {
+            return module.exports.cordova(platform);
+        }
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/b51e1c12/cordova-lib/src/cordova/metadata/amazon_fireos_parser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/metadata/amazon_fireos_parser.js b/cordova-lib/src/cordova/metadata/amazon_fireos_parser.js
new file mode 100644
index 0000000..f119279
--- /dev/null
+++ b/cordova-lib/src/cordova/metadata/amazon_fireos_parser.js
@@ -0,0 +1,183 @@
+/**
+    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'),
+    xml           = require('../xml-helpers'),
+    util          = require('../util'),
+    events        = require('../events'),
+    shell         = require('shelljs'),
+    project_config= require('../config'),
+    Q             = require('q'),
+    ConfigParser = require('../ConfigParser'),
+    CordovaError = require('../CordovaError');
+
+var awv_interface='awv_interface.jar';
+
+var default_prefs = {
+    "useBrowserHistory":"true",
+    "exit-on-suspend":"false"
+};
+
+module.exports = function android_parser(project) {
+    if (!fs.existsSync(path.join(project, 'AndroidManifest.xml'))) {
+        throw new CordovaError('The provided path "' + project + '" is not an Android project.');
+    }
+    this.path = project;
+    this.strings = path.join(this.path, 'res', 'values', 'strings.xml');
+    this.manifest = path.join(this.path, 'AndroidManifest.xml');
+    this.android_config = path.join(this.path, 'res', 'xml', 'config.xml');
+};
+
+// Returns a promise.
+// Returns a promise.
+module.exports.check_requirements = function(project_root) {
+    // Rely on platform's bin/create script to check requirements.
+    return Q(true);
+};
+
+module.exports.prototype = {
+    findOrientationPreference: function(config) {
+        var ret = config.getPreference('orientation');
+        if (ret && ret != 'default' && ret != 'portrait' && ret != 'landscape') {
+            events.emit('warn', 'Unknown value for orientation preference: ' + ret);
+            ret = null;
+        }
+
+        return ret;
+    },
+
+    update_from_config:function(config) {
+        if (config instanceof ConfigParser) {
+        } else throw new Error('update_from_config requires a ConfigParser object');
+
+        // Update app name by editing res/values/strings.xml
+        var name = config.name();
+        var strings = xml.parseElementtreeSync(this.strings);
+        strings.find('string[@name="app_name"]').text = name;
+        fs.writeFileSync(this.strings, strings.write({indent: 4}), 'utf-8');
+        events.emit('verbose', 'Wrote out Android application name to "' + name + '"');
+
+        var manifest = xml.parseElementtreeSync(this.manifest);
+        // Update the version by changing the AndroidManifest android:versionName
+        var version = config.version();
+        manifest.getroot().attrib["android:versionName"] = version;
+
+        // Update package name by changing the AndroidManifest id and moving the entry class around to the proper package directory
+        var pkg = config.packageName();
+        pkg = pkg.replace(/-/g, '_'); // Java packages cannot support dashes
+        var orig_pkg = manifest.getroot().attrib.package;
+        manifest.getroot().attrib.package = pkg;
+
+         // Set the orientation in the AndroidManifest
+        var orientationPref = this.findOrientationPreference(config);
+        if (orientationPref) {
+            var act = manifest.getroot().find('./application/activity');
+            switch (orientationPref) {
+                case 'default':
+                    delete act.attrib["android:screenOrientation"];
+                    break;
+                case 'portrait':
+                    act.attrib["android:screenOrientation"] = 'userPortrait';
+                    break;
+                case 'landscape':
+                    act.attrib["android:screenOrientation"] = 'userLandscape';
+            }
+        }
+
+
+        // Write out AndroidManifest.xml
+        fs.writeFileSync(this.manifest, manifest.write({indent: 4}), 'utf-8');
+
+        var orig_pkgDir = path.join(this.path, 'src', path.join.apply(null, orig_pkg.split('.')));
+        var java_files = fs.readdirSync(orig_pkgDir).filter(function(f) {
+          return f.indexOf('.svn') == -1 && f.indexOf('.java') >= 0 && fs.readFileSync(path.join(orig_pkgDir, f), 'utf-8').match(/extends\s+CordovaActivity/);
+        });
+        if (java_files.length == 0) {
+          throw new Error('No Java files found which extend CordovaActivity.');
+        } else if(java_files.length > 1) {
+          events.emit('log', 'Multiple candidate Java files (.java files which extend CordovaActivity) found. Guessing at the first one, ' + java_files[0]);
+        }
+
+        var orig_java_class = java_files[0];
+        var pkgDir = path.join(this.path, 'src', path.join.apply(null, pkg.split('.')));
+        shell.mkdir('-p', pkgDir);
+        var orig_javs = path.join(orig_pkgDir, orig_java_class);
+        var new_javs = path.join(pkgDir, orig_java_class);
+        var javs_contents = fs.readFileSync(orig_javs, 'utf-8');
+        javs_contents = javs_contents.replace(/package [\w\.]*;/, 'package ' + pkg + ';');
+        events.emit('verbose', 'Wrote out Android package name to "' + pkg + '"');
+        fs.writeFileSync(new_javs, javs_contents, 'utf-8');
+    },
+
+    // Returns the platform-specific www directory.
+    www_dir:function() {
+        return path.join(this.path, 'assets', 'www');
+    },
+
+    config_xml:function(){
+        return this.android_config;
+    },
+
+     // Used for creating platform_www in projects created by older versions.
+    cordovajs_path:function(libDir) {
+        var jsPath = path.join(libDir, 'framework', 'assets', 'www', 'cordova.js');
+        return path.resolve(jsPath);
+    },
+
+    // Replace the www dir with contents of platform_www and app www.
+    update_www:function() {
+        var projectRoot = util.isCordova(this.path);
+        var app_www = util.projectWww(projectRoot);
+        var platform_www = path.join(this.path, 'platform_www');
+
+        // Clear the www dir
+        shell.rm('-rf', this.www_dir());
+        shell.mkdir(this.www_dir());
+        // Copy over all app www assets
+        shell.cp('-rf', path.join(app_www, '*'), this.www_dir());
+        // Copy over stock platform www assets (cordova.js)
+        shell.cp('-rf', path.join(platform_www, '*'), this.www_dir());
+    },
+
+    // update the overrides folder into the www folder
+    update_overrides:function() {
+        var projectRoot = util.isCordova(this.path);
+        var merges_path = path.join(util.appDir(projectRoot), 'merges', 'amazon-fireos');
+        if (fs.existsSync(merges_path)) {
+            var overrides = path.join(merges_path, '*');
+            shell.cp('-rf', overrides, this.www_dir());
+        }
+    },
+
+
+    // Returns a promise.
+    update_project:function(cfg) {
+        var platformWww = path.join(this.path, 'assets');
+        try {
+            this.update_from_config(cfg);
+        } catch(e) {
+            return Q.reject(e);
+        }
+        this.update_overrides();
+        // delete any .svn folders copied over
+        util.deleteSvnFolders(platformWww);
+        return Q();
+    }
+};
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/b51e1c12/cordova-lib/src/cordova/metadata/android_parser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/metadata/android_parser.js b/cordova-lib/src/cordova/metadata/android_parser.js
new file mode 100644
index 0000000..ef50caf
--- /dev/null
+++ b/cordova-lib/src/cordova/metadata/android_parser.js
@@ -0,0 +1,303 @@
+/**
+    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'),
+    xml           = require('../xml-helpers'),
+    util          = require('../util'),
+    events        = require('../events'),
+    shell         = require('shelljs'),
+    project_config= require('../config'),
+    Q             = require('q'),
+    ConfigParser  = require('../ConfigParser'),
+    CordovaError  = require('../CordovaError');
+
+var default_prefs = {
+    "useBrowserHistory":"true",
+    "exit-on-suspend":"false"
+};
+
+module.exports = function android_parser(project) {
+    if (!fs.existsSync(path.join(project, 'AndroidManifest.xml'))) {
+        throw new CordovaError('The provided path "' + project + '" is not an Android project.');
+    }
+    this.path = project;
+    this.strings = path.join(this.path, 'res', 'values', 'strings.xml');
+    this.manifest = path.join(this.path, 'AndroidManifest.xml');
+    this.android_config = path.join(this.path, 'res', 'xml', 'config.xml');
+};
+
+// Returns a promise.
+module.exports.check_requirements = function(project_root) {
+    // Rely on platform's bin/create script to check requirements.
+    return Q(true);
+};
+
+module.exports.prototype = {
+    findOrientationPreference: function(config) {
+        var ret = config.getPreference('orientation');
+        if (ret && ret != 'default' && ret != 'portrait' && ret != 'landscape') {
+            events.emit('warn', 'Unknown value for orientation preference: ' + ret);
+            ret = null;
+        }
+
+        return ret;
+    },
+
+    findAndroidLaunchModePreference: function(config) {
+        var ret = config.getPreference('AndroidLaunchMode');
+        var valid = ['standard', 'singleTop', 'singleTask', 'singleInstance'].indexOf(ret) !== -1;
+        if (ret && !valid) {
+            events.emit('warn', 'Unknown value for launchMode preference: ' + ret);
+            ret = null;
+        }
+
+        return ret;
+    },
+
+    update_from_config:function(config) {
+        if (config instanceof ConfigParser) {
+        } else throw new Error('update_from_config requires a ConfigParser object');
+
+        // Update app name by editing res/values/strings.xml
+        var name = config.name();
+        var strings = xml.parseElementtreeSync(this.strings);
+        strings.find('string[@name="app_name"]').text = name;
+        fs.writeFileSync(this.strings, strings.write({indent: 4}), 'utf-8');
+        events.emit('verbose', 'Wrote out Android application name to "' + name + '"');
+
+        var icons = config.getIcons('android');
+        // if there are icon elements in config.xml
+        if (icons) {
+          var haveSeenDefaultIcon = false;
+          var iconCount = 0;
+          var android_icons = {};
+          var projectRoot = util.isCordova(this.path);
+          var default_icon;
+          var max_size;
+          var max_density;
+          // http://developer.android.com/design/style/iconography.html
+          var densities = {
+            "ldpi" : 36,
+            "mdpi" : 48,
+            "hdpi" : 72,
+            "xhdpi" : 96
+          };
+          for (var i=0; i<icons.length; i++) {
+            var icon = icons[i];
+            var destfilepath;
+            var size = icon.width;
+            if (!size) {
+              size = icon.height;
+            }
+            if (!size && !icon.density) {
+              if (default_icon) {
+                  events.emit('verbose', "more than one default icon: " + JSON.stringify(icon));
+              } else {
+                  default_icon = icon;
+              }
+            } else {
+              var parseIcon = function(icon, icon_size, size, density) {
+                // if density is explicitly defined, no need to calculate it from width/height
+                if (icon.density) {
+                    android_icons[icon.density] = icon;
+                    return;
+                }
+
+                var i = parseInt(icon_size);
+                if (size == parseInt(icon_size)) {
+                  var previous = android_icons[density];
+                  if (previous) {
+                    // already have that density. platform rules
+                    if (!previous.platform) {
+                      android_icons[density] = icon;
+                    } // else already have a platform icon of that density
+                  } else {
+                    android_icons[density] = icon;
+                  }
+                  android_icons[density] = icon;
+                  if (!max_size) {
+                    max_size = size;
+                    max_density = density;
+                  } else {
+                    if (max_size < size) {
+                      max_size = size
+                      max_density = density;
+                    }
+                  }
+                }
+              };
+              for (var density in densities) {
+                parseIcon(icon, size, densities[density], density);
+              }
+            }
+          }
+
+          var copyIcon = function(density) {
+            var srcfilepath;
+            var destfilepath = path.join(this.path, 'res', 'drawable-'+density, 'icon.png');
+            if (android_icons[density]) {
+              srcfilepath = path.join(projectRoot, android_icons[density].src);
+            } else {
+              if (default_icon) {
+                srcfilepath = path.join(projectRoot, default_icon.src);
+              } else {
+                if (max_density) {
+                  srcfilepath = path.join(projectRoot, android_icons[max_density].src);
+                } else {
+                  events.emit('verbose', 'no icon found matching Android typical densities');
+                }
+              }
+            }
+            if (srcfilepath) {
+                events.emit('verbose', 'Copying icon from ' + srcfilepath + ' to ' + destfilepath);
+                shell.cp('-f', srcfilepath, destfilepath);
+            }
+          }.bind(this);
+          for (var density in densities) {
+            copyIcon(density);
+          }
+
+        }
+
+        var manifest = xml.parseElementtreeSync(this.manifest);
+        // Update the version by changing the AndroidManifest android:versionName
+        var version = config.version();
+        var versionCode = config.android_versionCode() || default_versionCode(version);
+        manifest.getroot().attrib["android:versionName"] = version;
+        manifest.getroot().attrib["android:versionCode"] = versionCode;
+
+        // Update package name by changing the AndroidManifest id and moving the entry class around to the proper package directory
+        var pkg = config.packageName();
+        pkg = pkg.replace(/-/g, '_'); // Java packages cannot support dashes
+        var orig_pkg = manifest.getroot().attrib.package;
+        manifest.getroot().attrib.package = pkg;
+
+        var act = manifest.getroot().find('./application/activity');
+
+        // Set the orientation in the AndroidManifest
+        var orientationPref = this.findOrientationPreference(config);
+        if (orientationPref) {
+            switch (orientationPref) {
+                case 'default':
+                    delete act.attrib["android:screenOrientation"];
+                    break;
+                case 'portrait':
+                    act.attrib["android:screenOrientation"] = 'portrait';
+                    break;
+                case 'landscape':
+                    act.attrib["android:screenOrientation"] = 'landscape';
+            }
+        }
+
+        // Set android:launchMode in AndroidManifest
+        var androidLaunchModePref = this.findAndroidLaunchModePreference(config);
+        if (androidLaunchModePref) {
+            act.attrib["android:launchMode"] = androidLaunchModePref;
+        } else { // User has (explicitly) set an invalid value for AndroidLaunchMode preference
+            delete act.attrib["android:launchMode"]; // use Android default value (standard)
+        }
+
+        // Write out AndroidManifest.xml
+        fs.writeFileSync(this.manifest, manifest.write({indent: 4}), 'utf-8');
+
+        var orig_pkgDir = path.join(this.path, 'src', path.join.apply(null, orig_pkg.split('.')));
+        var java_files = fs.readdirSync(orig_pkgDir).filter(function(f) {
+          return f.indexOf('.svn') == -1 && f.indexOf('.java') >= 0 && fs.readFileSync(path.join(orig_pkgDir, f), 'utf-8').match(/extends\s+CordovaActivity/);
+        });
+        if (java_files.length == 0) {
+          throw new Error('No Java files found which extend CordovaActivity.');
+        } else if(java_files.length > 1) {
+          events.emit('log', 'Multiple candidate Java files (.java files which extend CordovaActivity) found. Guessing at the first one, ' + java_files[0]);
+        }
+
+        var orig_java_class = java_files[0];
+        var pkgDir = path.join(this.path, 'src', path.join.apply(null, pkg.split('.')));
+        shell.mkdir('-p', pkgDir);
+        var orig_javs = path.join(orig_pkgDir, orig_java_class);
+        var new_javs = path.join(pkgDir, orig_java_class);
+        var javs_contents = fs.readFileSync(orig_javs, 'utf-8');
+        javs_contents = javs_contents.replace(/package [\w\.]*;/, 'package ' + pkg + ';');
+        events.emit('verbose', 'Wrote out Android package name to "' + pkg + '"');
+        fs.writeFileSync(new_javs, javs_contents, 'utf-8');
+    },
+
+    // Returns the platform-specific www directory.
+    www_dir:function() {
+        return path.join(this.path, 'assets', 'www');
+    },
+
+    config_xml:function(){
+        return this.android_config;
+    },
+
+    // Used for creating platform_www in projects created by older versions.
+    cordovajs_path:function(libDir) {
+        var jsPath = path.join(libDir, 'framework', 'assets', 'www', 'cordova.js');
+        return path.resolve(jsPath);
+    },
+
+    // Replace the www dir with contents of platform_www and app www.
+    update_www:function() {
+        var projectRoot = util.isCordova(this.path);
+        var app_www = util.projectWww(projectRoot);
+        var platform_www = path.join(this.path, 'platform_www');
+
+        // Clear the www dir
+        shell.rm('-rf', this.www_dir());
+        shell.mkdir(this.www_dir());
+        // Copy over all app www assets
+        shell.cp('-rf', path.join(app_www, '*'), this.www_dir());
+        // Copy over stock platform www assets (cordova.js)
+        shell.cp('-rf', path.join(platform_www, '*'), this.www_dir());
+    },
+
+    // update the overrides folder into the www folder
+    update_overrides:function() {
+        var projectRoot = util.isCordova(this.path);
+        var merges_path = path.join(util.appDir(projectRoot), 'merges', 'android');
+        if (fs.existsSync(merges_path)) {
+            var overrides = path.join(merges_path, '*');
+            shell.cp('-rf', overrides, this.www_dir());
+        }
+    },
+
+    // Returns a promise.
+    update_project:function(cfg) {
+        var platformWww = path.join(this.path, 'assets');
+        try {
+            this.update_from_config(cfg);
+            this.update_overrides();
+        } catch(e) {
+            return Q.reject(e);
+        }
+        // delete any .svn folders copied over
+        util.deleteSvnFolders(platformWww);
+        return Q();
+    }
+};
+
+
+// Consturct the default value for versionCode as
+// PATCH + MINOR * 100 + MAJOR * 10000
+// see http://developer.android.com/tools/publishing/versioning.html
+function default_versionCode(version) {
+    nums = version.split('-')[0].split('.').map(Number);
+    var versionCode = nums[0] * 10000 + nums[1] * 100 + nums[2];
+    return versionCode;
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/b51e1c12/cordova-lib/src/cordova/metadata/blackberry10_parser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/metadata/blackberry10_parser.js b/cordova-lib/src/cordova/metadata/blackberry10_parser.js
new file mode 100644
index 0000000..ba6530f
--- /dev/null
+++ b/cordova-lib/src/cordova/metadata/blackberry10_parser.js
@@ -0,0 +1,145 @@
+/**
+    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'),
+    shell         = require('shelljs'),
+    util          = require('../util'),
+    Q             = require('q'),
+    child_process = require('child_process'),
+    ConfigParser  = require('../ConfigParser'),
+    CordovaError  = require('../CordovaError'),
+    events        = require('../events'),
+    config        = require('../config');
+
+module.exports = function blackberry_parser(project) {
+    if (!fs.existsSync(path.join(project, 'www'))) {
+        throw new CordovaError('The provided path "' + project + '" is not a Cordova BlackBerry10 project.');
+    }
+    this.path = project;
+    this.config_path = path.join(this.path, 'www', 'config.xml');
+    this.xml = new ConfigParser(this.config_path);
+};
+
+// Returns a promise.
+module.exports.check_requirements = function(project_root) {
+    var custom_path = config.has_custom_path(project_root, 'blackberry10');
+    var lib_path;
+    if (custom_path) {
+        lib_path = path.resolve(custom_path);
+    } else {
+        lib_path = path.join(util.libDirectory, 'blackberry10', 'cordova', require('../../platforms').blackberry10.version);
+    }
+    var d = Q.defer();
+    child_process.exec("\"" + path.join(lib_path, 'bin', 'check_reqs') + "\"", function(err, output, stderr) {
+        if (err) {
+            d.reject(new CordovaError('Requirements check failed: ' + output + stderr));
+        } else {
+            d.resolve();
+        }
+    });
+    return d.promise;
+};
+
+module.exports.prototype = {
+    update_from_config:function(config) {
+        var projectRoot = util.isCordova(this.path),
+            resDir = path.join(this.path, 'platform_www', 'res'),
+            icons,
+            i;
+
+        if (!config instanceof ConfigParser) {
+            throw new Error('update_from_config requires a ConfigParser object');
+        }
+
+        shell.rm('-rf', resDir);
+        shell.mkdir(resDir);
+
+        icons = config.getIcons('blackberry10');
+        if (icons) {
+            for (i = 0; i < icons.length; i++) {
+                var src = path.join(projectRoot, icons[i].src),
+                    dest = path.join(this.path, 'platform_www', icons[i].src),
+                    destFolder = path.join(dest, '..');
+
+                if (!fs.existsSync(destFolder)) {
+                    shell.mkdir(destFolder); // make sure target dir exists
+                }
+                events.emit('verbose', 'Copying icon from ' + src + ' to ' + dest);
+                shell.cp('-f', src, dest);
+            }
+        }
+    },
+
+    // Returns a promise.
+    update_project:function(cfg) {
+        var self = this;
+
+        try {
+            self.update_from_config(cfg);
+        } catch(e) {
+            return Q.reject(e);
+        }
+        self.update_overrides();
+        util.deleteSvnFolders(this.www_dir());
+        return Q();
+    },
+
+    // Returns the platform-specific www directory.
+    www_dir:function() {
+        return path.join(this.path, 'www');
+    },
+
+    config_xml:function(){
+        return this.config_path;
+    },
+
+    // Used for creating platform_www in projects created by older versions.
+    cordovajs_path:function(libDir) {
+        var jsPath = path.join(libDir, 'javascript', 'cordova.blackberry10.js');
+        return path.resolve(jsPath);
+    },
+
+    // Replace the www dir with contents of platform_www and app www.
+    update_www:function() {
+        var projectRoot = util.isCordova(this.path);
+        var app_www = util.projectWww(projectRoot);
+        var platform_www = path.join(this.path, 'platform_www');
+        var platform_cfg_backup = new ConfigParser(this.config_path);
+
+        // Clear the www dir
+        shell.rm('-rf', this.www_dir());
+        shell.mkdir(this.www_dir());
+        // Copy over all app www assets
+        shell.cp('-rf', path.join(app_www, '*'), this.www_dir());
+        // Copy over stock platform www assets (cordova.js)
+        shell.cp('-rf', path.join(platform_www, '*'), this.www_dir());
+        //Re-Write config.xml
+        platform_cfg_backup.write();
+    },
+
+    // update the overrides folder into the www folder
+    update_overrides:function() {
+        var projectRoot = util.isCordova(this.path);
+        var merges_path = path.join(util.appDir(projectRoot), 'merges', 'blackberry10');
+        if (fs.existsSync(merges_path)) {
+            var overrides = path.join(merges_path, '*');
+            shell.cp('-rf', overrides, this.www_dir());
+        }
+    },
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/b51e1c12/cordova-lib/src/cordova/metadata/firefoxos_parser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/metadata/firefoxos_parser.js b/cordova-lib/src/cordova/metadata/firefoxos_parser.js
new file mode 100644
index 0000000..351a409
--- /dev/null
+++ b/cordova-lib/src/cordova/metadata/firefoxos_parser.js
@@ -0,0 +1,151 @@
+/**
+    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'),
+    shell = require('shelljs'),
+    util = require('../util'),
+    events = require('../events'),
+    Q = require('q'),
+    ConfigParser = require('../ConfigParser');
+
+module.exports = function firefoxos_parser(project) {
+    this.path = project;
+};
+
+// Returns a promise.
+module.exports.check_requirements = function(project_root) {
+    return Q(); // Requirements always met.
+};
+
+module.exports.prototype = {
+    // Returns a promise.
+    update_from_config: function() {
+        var config = new ConfigParser(this.config_xml());
+        var manifestPath = path.join(this.www_dir(), 'manifest.webapp');
+        var manifest;
+
+        if(fs.existsSync(manifestPath)) {
+            manifest = JSON.parse(fs.readFileSync(manifestPath));
+        }
+        else {
+            manifest = {
+                launch_path: "/index.html",
+                installs_allowed_from: ["*"]
+            };
+        }
+
+        manifest.version = config.version();
+        manifest.name = config.name();
+        manifest.pkgName = config.packageName();
+        manifest.description = config.description();
+        manifest.developer = {
+            name: config.author()
+        };
+
+        var authorNode = config.doc.find('author');
+        var authorUrl = authorNode.attrib['href'];
+
+        if (authorUrl) {
+            manifest.developer.url = authorUrl;
+        }
+
+        var permissionNodes = config.doc.findall('permission');
+        var privileged = false;
+
+        if (permissionNodes.length) {
+            manifest.permissions = manifest.permissions || {};
+
+            permissionNodes.forEach(function(node) {
+                var permissionName = node.attrib['name'];
+
+                // Don't change if it was already created
+                if (!manifest.permissions[permissionName]) {
+                    manifest.permissions[permissionName] = {
+                        description: node.attrib['description']
+                    };
+
+                    if (node.attrib['access']) {
+                        manifest.permissions[permissionName].access = node.attrib['access'];
+                    }
+
+                    if (node.attrib['privileged'] === "true") {
+                        privileged = true;
+                    }
+                }
+            });
+        }
+
+        if (privileged) {
+            manifest.type = 'privileged';
+        } else {
+            delete manifest.type;
+        }
+
+        fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 4));
+
+        return Q();
+    },
+
+    www_dir: function() {
+        return path.join(this.path, 'www');
+    },
+
+    // Used for creating platform_www in projects created by older versions.
+    cordovajs_path:function(libDir) {
+        var jsPath = path.join(libDir, 'cordova-lib', 'cordova.js');
+        return path.resolve(jsPath);
+    },
+
+    // Replace the www dir with contents of platform_www and app www.
+    update_www:function() {
+        var projectRoot = util.isCordova(this.path);
+        var app_www = util.projectWww(projectRoot);
+        var platform_www = path.join(this.path, 'platform_www');
+
+        // Clear the www dir
+        shell.rm('-rf', this.www_dir());
+        shell.mkdir(this.www_dir());
+        // Copy over all app www assets
+        shell.cp('-rf', path.join(app_www, '*'), this.www_dir());
+        // Copy over stock platform www assets (cordova.js)
+        shell.cp('-rf', path.join(platform_www, '*'), this.www_dir());
+    },
+
+    update_overrides: function() {
+        var projectRoot = util.isCordova(this.path);
+        var mergesPath = path.join(util.appDir(projectRoot), 'merges', 'firefoxos');
+        if(fs.existsSync(mergesPath)) {
+            var overrides = path.join(mergesPath, '*');
+            shell.cp('-rf', overrides, this.www_dir());
+        }
+    },
+
+    config_xml:function(){
+        return path.join(this.path, 'config.xml');
+    },
+
+    // Returns a promise.
+    update_project: function(cfg) {
+        return this.update_from_config()
+            .then(function(){
+                this.update_overrides();
+                util.deleteSvnFolders(this.www_dir());
+            }.bind(this));
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/b51e1c12/cordova-lib/src/cordova/metadata/ios_parser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/metadata/ios_parser.js b/cordova-lib/src/cordova/metadata/ios_parser.js
new file mode 100644
index 0000000..f906ee3
--- /dev/null
+++ b/cordova-lib/src/cordova/metadata/ios_parser.js
@@ -0,0 +1,202 @@
+/**
+    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'),
+    xcode         = require('xcode'),
+    util          = require('../util'),
+    events        = require('../events'),
+    shell         = require('shelljs'),
+    plist         = require('plist-with-patches'),
+    Q             = require('q'),
+    ConfigParser  = require('../ConfigParser'),
+    CordovaError  = require('../CordovaError'),
+    config        = require('../config');
+
+module.exports = function ios_parser(project) {
+    try {
+        var xcodeproj_dir = fs.readdirSync(project).filter(function(e) { return e.match(/\.xcodeproj$/i); })[0];
+        if (!xcodeproj_dir) throw new CordovaError('The provided path "' + project + '" is not a Cordova iOS project.');
+        this.xcodeproj = path.join(project, xcodeproj_dir);
+        this.originalName = this.xcodeproj.substring(this.xcodeproj.lastIndexOf(path.sep)+1, this.xcodeproj.indexOf('.xcodeproj'));
+        this.cordovaproj = path.join(project, this.originalName);
+    } catch(e) {
+        throw new CordovaError('The provided path "'+project+'" is not a Cordova iOS project.');
+    }
+    this.path = project;
+    this.pbxproj = path.join(this.xcodeproj, 'project.pbxproj');
+    this.config_path = path.join(this.cordovaproj, 'config.xml');
+};
+
+// Returns a promise.
+module.exports.check_requirements = function(project_root) {
+    // Rely on platform's bin/create script to check requirements.
+    return Q(true);
+};
+
+module.exports.prototype = {
+    // Returns a promise.
+    update_from_config:function(config) {
+        if (config instanceof ConfigParser) {
+        } else {
+            return Q.reject(new Error('update_from_config requires a ConfigParser object'));
+        }
+        var name = config.name();
+        var pkg = config.packageName();
+        var version = config.version();
+
+        // Update package id (bundle id)
+        var plistFile = path.join(this.cordovaproj, this.originalName + '-Info.plist');
+        var infoPlist = plist.parseFileSync(plistFile);
+        infoPlist['CFBundleIdentifier'] = pkg;
+
+        // Update version (bundle version)
+        infoPlist['CFBundleShortVersionString'] = version;
+        var CFBundleVersion = config.ios_CFBundleVersion() || default_CFBundleVersion(version);
+        infoPlist['CFBundleVersion'] = CFBundleVersion;
+
+        var info_contents = plist.build(infoPlist);
+        info_contents = info_contents.replace(/<string>[\s\r\n]*<\/string>/g,'<string></string>');
+        fs.writeFileSync(plistFile, info_contents, 'utf-8');
+        events.emit('verbose', 'Wrote out iOS Bundle Identifier to "' + pkg + '"');
+        events.emit('verbose', 'Wrote out iOS Bundle Version to "' + version + '"');
+
+        // Update icons
+        var icons = config.getIcons('ios');
+        var platformRoot = this.cordovaproj;
+        var appRoot = util.isCordova(platformRoot);
+
+        var platformIcons = [
+            {dest: "icon-60.png", width: 60, height: 60},
+            {dest: "icon-60@2x.png", width: 120, height: 120},
+            {dest: "icon-76.png", width: 76, height: 76},
+            {dest: "icon-76@2x.png", width: 152, height: 152},
+            {dest: "icon-small.png", width: 29, height: 29},
+            {dest: "icon-small@2x.png", width: 58, height: 58},
+            {dest: "icon-40.png", width: 40, height: 40},
+            {dest: "icon-40@2x.png", width: 80, height: 80},
+            {dest: "icon.png", width: 57, height: 57},
+            {dest: "icon@2x.png", width: 114, height: 114},
+            {dest: "icon-72.png", width: 72, height: 72},
+            {dest: "icon-72@2x.png", width: 144, height: 144},
+            {dest: "icon-50.png", width: 50, height: 50},
+            {dest: "icon-50@2x.png", width: 100, height: 100}
+        ];
+
+        platformIcons.forEach(function (item) {
+            icon = icons.getIconBySize(item.width, item.height) || icons.getDefault();
+            if (icon){
+                var src = path.join(appRoot, icon.src),
+                    dest = path.join(platformRoot, 'Resources/icons/', item.dest);
+                events.emit('verbose', 'Copying icon from ' + src + ' to ' + dest);
+                shell.cp('-f', src, dest);
+            }
+        });
+
+        if (name != this.originalName) {
+            // Update product name inside pbxproj file
+            var proj = new xcode.project(this.pbxproj);
+            var parser = this;
+            var d = Q.defer();
+            proj.parse(function(err,hash) {
+                if (err) {
+                    d.reject(new Error('An error occured during parsing of project.pbxproj. Start weeping. Output: ' + err));
+                } else {
+                    proj.updateProductName(name);
+                    fs.writeFileSync(parser.pbxproj, proj.writeSync(), 'utf-8');
+                    // Move the xcodeproj and other name-based dirs over.
+                    shell.mv(path.join(parser.cordovaproj, parser.originalName + '-Info.plist'), path.join(parser.cordovaproj, name + '-Info.plist'));
+                    shell.mv(path.join(parser.cordovaproj, parser.originalName + '-Prefix.pch'), path.join(parser.cordovaproj, name + '-Prefix.pch'));
+                    shell.mv(parser.xcodeproj, path.join(parser.path, name + '.xcodeproj'));
+                    shell.mv(parser.cordovaproj, path.join(parser.path, name));
+                    // Update self object with new paths
+                    var old_name = parser.originalName;
+                    parser = new module.exports(parser.path);
+                    // Hack this shi*t
+                    var pbx_contents = fs.readFileSync(parser.pbxproj, 'utf-8');
+                    pbx_contents = pbx_contents.split(old_name).join(name);
+                    fs.writeFileSync(parser.pbxproj, pbx_contents, 'utf-8');
+                    events.emit('verbose', 'Wrote out iOS Product Name and updated XCode project file names from "'+old_name+'" to "' + name + '".');
+                    d.resolve();
+                }
+            });
+            return d.promise;
+        } else {
+            events.emit('verbose', 'iOS Product Name has not changed (still "' + this.originalName + '")');
+            return Q();
+        }
+    },
+
+    // Returns the platform-specific www directory.
+    www_dir:function() {
+        return path.join(this.path, 'www');
+    },
+
+    config_xml:function(){
+        return this.config_path;
+    },
+
+    // Used for creating platform_www in projects created by older versions.
+    cordovajs_path:function(libDir) {
+        var jsPath = path.join(libDir, 'CordovaLib', 'cordova.js');
+        return path.resolve(jsPath);
+    },
+
+    // Replace the www dir with contents of platform_www and app www.
+    update_www:function() {
+        var projectRoot = util.isCordova(this.path);
+        var app_www = util.projectWww(projectRoot);
+        var platform_www = path.join(this.path, 'platform_www');
+
+        // Clear the www dir
+        shell.rm('-rf', this.www_dir());
+        shell.mkdir(this.www_dir());
+        // Copy over all app www assets
+        shell.cp('-rf', path.join(app_www, '*'), this.www_dir());
+        // Copy over stock platform www assets (cordova.js)
+        shell.cp('-rf', path.join(platform_www, '*'), this.www_dir());
+    },
+
+    // update the overrides folder into the www folder
+    update_overrides:function() {
+        var projectRoot = util.isCordova(this.path);
+        var merges_path = path.join(util.appDir(projectRoot), 'merges', 'ios');
+        if (fs.existsSync(merges_path)) {
+            var overrides = path.join(merges_path, '*');
+            shell.cp('-rf', overrides, this.www_dir());
+        }
+    },
+
+    // Returns a promise.
+    update_project:function(cfg) {
+        var self = this;
+        return this.update_from_config(cfg)
+        .then(function() {
+            self.update_overrides();
+            util.deleteSvnFolders(self.www_dir());
+        });
+    }
+};
+
+
+// Construct a default value for CFBundleVersion as the version with any
+// -rclabel stripped=.
+function default_CFBundleVersion(version) {
+    return version.split('-')[0];
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/b51e1c12/cordova-lib/src/cordova/metadata/ubuntu_parser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/metadata/ubuntu_parser.js b/cordova-lib/src/cordova/metadata/ubuntu_parser.js
new file mode 100644
index 0000000..947fb1e
--- /dev/null
+++ b/cordova-lib/src/cordova/metadata/ubuntu_parser.js
@@ -0,0 +1,162 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+var fs            = require('fs'),
+    path          = require('path'),
+    et            = require('elementtree'),
+    xml           = require('../xml-helpers'),
+    util          = require('../util'),
+    events        = require('../events'),
+    shell         = require('shelljs'),
+    project_config= require('../config'),
+    Q             = require('q'),
+    os            = require('os'),
+    ConfigParser  = require('../ConfigParser'),
+    CordovaError  = require('../CordovaError');
+
+module.exports = function(project) {
+    this.path = project;
+    this.config = new ConfigParser(this.config_xml());
+    this.update_manifest();
+};
+
+function sanitize(str) {
+    return str.replace(/\n/g, ' ').replace(/^\s+|\s+$/g, '');
+}
+
+// Returns a promise.
+module.exports.check_requirements = function(project_root, callback) {
+    var d = Q.defer();
+
+    events.emit('log', 'Checking ubuntu requirements...');
+    command = "dpkg-query -Wf'${db:Status-abbrev}' cmake debhelper libx11-dev libicu-dev pkg-config qtbase5-dev qtchooser qtdeclarative5-dev qtfeedback5-dev qtlocation5-dev qtmultimedia5-dev qtpim5-dev qtsensors5-dev qtsystems5-dev 2>/dev/null | grep -q '^i'";
+    events.emit('log', 'Running "' + command + '" (output to follow)');
+    shell.exec(command, {silent:true, async:true}, function(code, output) {
+        events.emit('log', output);
+        if (code != 0) {
+            d.reject(new CordovaError('Make sure you have the following packages installed: ' + output));
+        } else {
+            d.resolve();
+        }
+    });
+
+    return d.promise;
+};
+
+module.exports.prototype = {
+    // Returns a promise.
+    update_from_config:function(config) {
+        if (config instanceof ConfigParser) {
+        } else {
+            return Q.reject(new Error('update_from_config requires a ConfigParser object'));
+        }
+
+        this.config = new ConfigParser(this.config_xml());
+        this.config.setName(config.name());
+        this.config.setVersion(config.version());
+        this.config.setPackageName(config.packageName());
+        this.config.setDescription(config.description());
+
+        this.config.write();
+
+        return this.update_manifest();
+    },
+
+    cordovajs_path:function(libDir) {
+        var jsPath = path.join(libDir, 'www', 'cordova.js');
+        return path.resolve(jsPath);
+    },
+
+    update_manifest: function() {
+        var nodearch2debarch = { 'arm': 'armhf',
+                                 'ia32': 'i386',
+                                 'x64': 'amd64'};
+        var arch;
+        if (os.arch() in nodearch2debarch)
+            arch = nodearch2debarch[os.arch()];
+        else
+            return Q.reject(new Error('unknown cpu arch'));
+
+        if (!this.config.author())
+            return Q.reject(new Error('config.xml should contain author'));
+
+        var manifest = { name: this.config.packageName(),
+                         version: this.config.version(),
+                         title: this.config.name(),
+                         hooks: { cordova: { desktop: "cordova.desktop",
+                                             apparmor: "apparmor.json" } },
+                         framework: "ubuntu-sdk-13.10",
+                         maintainer: sanitize(this.config.author()),
+                         architecture: arch,
+                         description: sanitize(this.config.description()) };
+        fs.writeFileSync(path.join(this.path, 'manifest.json'), JSON.stringify(manifest));
+
+        var name = this.config.name().replace(/\n/g, ' '); //FIXME: escaping
+        var content = "[Desktop Entry]\nName=" + name + "\nExec=./cordova-ubuntu www/\nIcon=qmlscene\nTerminal=false\nType=Application\nX-Ubuntu-Touch=true";
+
+        fs.writeFileSync(path.join(this.path, 'cordova.desktop'), content);
+
+        var policy = { policy_groups: ["networking", "audio"], policy_version: 1 };
+
+        this.config.doc.getroot().findall('./feature/param').forEach(function (element) {
+            if (element.attrib.policy_group && policy.policy_groups.indexOf(policy.policy_groups) === -1)
+                policy.policy_groups.push(element.attrib.policy_group);
+        });
+
+        fs.writeFileSync(path.join(this.path, 'apparmor.json'), JSON.stringify(policy));
+
+        return Q();
+    },
+
+    config_xml:function(){
+        return path.join(this.path, 'config.xml');
+    },
+
+    www_dir:function() {
+        return path.join(this.path, 'www');
+    },
+
+    update_www:function() {
+        var projectRoot = util.isCordova(this.path);
+        var www = util.projectWww(projectRoot);
+
+        shell.rm('-rf', this.www_dir());
+        shell.cp('-rf', www, this.path);
+    },
+
+    update_overrides:function() {
+        var projectRoot = util.isCordova(this.path);
+        var mergesPath = path.join(util.appDir(projectRoot), 'merges', 'ubuntu');
+        if(fs.existsSync(mergesPath)) {
+            var overrides = path.join(mergesPath, '*');
+            shell.cp('-rf', overrides, this.www_dir());
+        }
+    },
+
+    // Returns a promise.
+    update_project:function(cfg) {
+        var self = this;
+
+        return this.update_from_config(cfg)
+        .then(function() {
+            self.update_overrides();
+            util.deleteSvnFolders(self.www_dir());
+        });
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/b51e1c12/cordova-lib/src/cordova/metadata/windows8_parser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/metadata/windows8_parser.js b/cordova-lib/src/cordova/metadata/windows8_parser.js
new file mode 100644
index 0000000..53c2d31
--- /dev/null
+++ b/cordova-lib/src/cordova/metadata/windows8_parser.js
@@ -0,0 +1,339 @@
+/**
+    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'),
+    et            = require('elementtree'),
+    util          = require('../util'),
+    events        = require('../events'),
+    shell         = require('shelljs'),
+    events        = require('../events'),
+    Q             = require('q'),
+    child_process = require('child_process'),
+    ConfigParser  = require('../ConfigParser'),
+    CordovaError  = require('../CordovaError'),
+    xml           = require('../xml-helpers'),
+    config        = require('../config'),
+    hooker        = require('../hooker');
+
+module.exports = function windows8_parser(project) {
+    try {
+        // TODO : Check that it's not a windows8 project?
+        var jsproj_file   = fs.readdirSync(project).filter(function(e) { return e.match(/\.jsproj$/i); })[0];
+        if (!jsproj_file) throw new CordovaError('No .jsproj file in "'+project+'"');
+        this.windows8_proj_dir = project;
+        this.jsproj_path  = path.join(this.windows8_proj_dir, jsproj_file);
+        this.sln_path     = path.join(this.windows8_proj_dir, jsproj_file.replace(/\.jsproj/, '.sln'));
+    } catch(e) {
+        throw new CordovaError('The provided path "' + project + '" is not a Windows 8 project. ' + e);
+    }
+    this.manifest_path  = path.join(this.windows8_proj_dir, 'package.appxmanifest');
+};
+
+// Returns a promise
+module.exports.check_requirements = function(project_root) {
+    events.emit('log', 'Checking windows8 requirements...');
+    var lib_path = path.join(util.libDirectory, 'windows8', 'cordova',
+                    require('../../platforms').windows8.version, 'windows8');
+
+    var custom_path = config.has_custom_path(project_root, 'windows8');
+    if (custom_path) {
+        lib_path = path.join(custom_path, "windows8");
+    }
+    var command = '"' + path.join(lib_path, 'bin', 'check_reqs') + '"';
+    events.emit('verbose', 'Running "' + command + '" (output to follow)');
+    var d = Q.defer();
+
+    child_process.exec(command, function(err, output, stderr) {
+        events.emit('verbose', output);
+        if (err) {
+            d.reject(new CordovaError('Requirements check failed: ' + output + stderr));
+        } else {
+            d.resolve();
+        }
+    });
+    return d.promise;
+};
+
+module.exports.prototype = {
+
+    update_from_config:function(config) {
+
+        //check config parser
+        if (config instanceof ConfigParser) {
+        } else throw new Error('update_from_config requires a ConfigParser object');
+
+        //Get manifest file
+        var manifest = xml.parseElementtreeSync(this.manifest_path);
+
+        var version = this.fixConfigVersion(config.version());
+        var name = config.name();
+        var pkgName = config.packageName();
+        var author = config.author();
+
+        var identityNode = manifest.find('.//Identity');
+        if(identityNode) {
+            // Update app name in identity
+            var appIdName = identityNode['attrib']['Name'];
+            if (appIdName != pkgName) {
+                identityNode['attrib']['Name'] = pkgName;
+            }
+
+            // Update app version
+            var appVersion = identityNode['attrib']['Version'];
+            if(appVersion != version) {
+                identityNode['attrib']['Version'] = version;
+            }
+        }
+
+        // Update name (windows8 has it in the Application[@Id] and Application.VisualElements[@DisplayName])
+        var app = manifest.find('.//Application');
+        if(app) {
+
+            var appId = app['attrib']['Id'];
+
+            if (appId != pkgName) {
+                app['attrib']['Id'] = pkgName;
+            }
+
+            var visualElems = manifest.find('.//VisualElements') || manifest.find('.//m2:VisualElements');
+
+            if(visualElems) {
+                var displayName = visualElems['attrib']['DisplayName'];
+                if(displayName != name) {
+                    visualElems['attrib']['DisplayName'] = name;
+                }
+            }
+            else {
+                throw new Error('update_from_config expected a valid package.appxmanifest' +
+                                ' with a <VisualElements> node');
+            }
+        }
+        else {
+            throw new Error('update_from_config expected a valid package.appxmanifest' +
+                            ' with a <Application> node');
+        }
+
+        // Update properties
+        var properties = manifest.find('.//Properties');
+        if (properties && properties.find) {
+            var displayNameElement = properties.find('.//DisplayName');
+            if (displayNameElement && displayNameElement.text != name) {
+                displayNameElement.text = name;
+            }
+
+            var publisherNameElement = properties.find('.//PublisherDisplayName');
+            if (publisherNameElement && publisherNameElement.text != author) {
+                publisherNameElement.text = author;
+            }
+        }
+
+        // sort Capability elements as per CB-5350 Windows8 build fails due to invalid 'Capabilities' definition
+        // to sort elements we remove them and then add again in the appropriate order
+        var capabilitiesRoot = manifest.find('.//Capabilities'),
+            capabilities = capabilitiesRoot._children || [];
+
+        capabilities.forEach(function(elem){
+            capabilitiesRoot.remove(0, elem);
+        });
+        capabilities.sort(function(a, b) {
+            return (a.tag > b.tag)? 1: -1;
+        });
+        capabilities.forEach(function(elem){
+            capabilitiesRoot.append(elem);
+        });
+
+        //Write out manifest
+        fs.writeFileSync(this.manifest_path, manifest.write({indent: 4}), 'utf-8');
+
+        // Update icons
+        var icons = config.getIcons('windows8');
+        var platformRoot = this.windows8_proj_dir;
+        var appRoot = util.isCordova(platformRoot);
+
+        // Icons, that should be added to platform
+        var platformIcons = [
+            {dest: "images/logo.png", width: 150, height: 150},
+            {dest: "images/smalllogo.png", width: 30, height: 30},
+            {dest: "images/storelogo.png", width: 50, height: 50},
+        ];
+
+        platformIcons.forEach(function (item) {
+            icon = icons.getIconBySize(item.width, item.height) || icons.getDefault();
+            if (icon){
+                var src = path.join(appRoot, icon.src),
+                    dest = path.join(platformRoot, item.dest);
+                events.emit('verbose', 'Copying icon from ' + src + ' to ' + dest);
+                shell.cp('-f', src, dest);
+            }
+        });
+
+    },
+    // Returns the platform-specific www directory.
+    www_dir:function() {
+        return path.join(this.windows8_proj_dir, 'www');
+    },
+    config_xml:function() {
+        return path.join(this.windows8_proj_dir,"config.xml");
+    },
+    // copy files from merges directory to actual www dir
+    copy_merges:function(merges_sub_path) {
+        var merges_path = path.join(util.appDir(util.isCordova(this.windows8_proj_dir)), 'merges', merges_sub_path);
+        if (fs.existsSync(merges_path)) {
+            var overrides = path.join(merges_path, '*');
+            shell.cp('-rf', overrides, this.www_dir());
+        }
+    },
+
+    // Used for creating platform_www in projects created by older versions.
+    cordovajs_path:function(libDir) {
+        var jsPath = path.join(libDir, "template", 'www', 'cordova.js');
+        return path.resolve(jsPath);
+    },
+
+    // Replace the www dir with contents of platform_www and app www and updates the csproj file.
+    update_www:function() {
+        var projectRoot = util.isCordova(this.windows8_proj_dir);
+        var app_www = util.projectWww(projectRoot);
+        var platform_www = path.join(this.windows8_proj_dir, 'platform_www');
+
+        // Clear the www dir
+        shell.rm('-rf', this.www_dir());
+        shell.mkdir(this.www_dir());
+        // Copy over all app www assets
+        shell.cp('-rf', path.join(app_www, '*'), this.www_dir());
+
+        // Copy all files from merges directory.
+        this.copy_merges('windows8');
+
+        // Copy over stock platform www assets (cordova.js)
+        shell.cp('-rf', path.join(platform_www, '*'), this.www_dir());
+    },
+
+    // updates the jsproj file to explicitly list all www content.
+    update_jsproj:function() {
+        var jsproj_xml = xml.parseElementtreeSync(this.jsproj_path);
+        // remove any previous references to the www files
+        var item_groups = jsproj_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, 3) == 'www') {
+                    // remove file reference
+                    group.remove(0, file);
+                    // remove ItemGroup if empty
+                    var new_group = group.findall('Content');
+                    if(new_group.length < 1) {
+                        jsproj_xml.getroot().remove(0, group);
+                    }
+                }
+            }
+        }
+
+        // now add all www references back in from the root www folder
+        var project_root = util.isCordova(this.windows8_proj_dir);
+        var www_files = this.folder_contents('www', this.www_dir());
+        for(file in www_files) {
+            var item = new et.Element('ItemGroup');
+            var content = new et.Element('Content');
+            content.attrib.Include = www_files[file];
+            item.append(content);
+            jsproj_xml.getroot().append(item);
+        }
+        // save file
+        fs.writeFileSync(this.jsproj_path, jsproj_xml.write({indent:4}), 'utf-8');
+    },
+    // Returns an array of all the files in the given directory with relative paths
+    // - name     : the name of the top level directory (i.e all files will start with this in their path)
+    // - dir     : the directory whos contents will be listed under 'name' directory
+    folder_contents:function(name, dir) {
+        var results = [];
+        var folder_dir = fs.readdirSync(dir);
+        for(item in folder_dir) {
+            var stat = fs.statSync(path.join(dir, folder_dir[item]));
+
+            if(stat.isDirectory()) {
+                var sub_dir = this.folder_contents(path.join(name, folder_dir[item]), path.join(dir, folder_dir[item]));
+                //Add all subfolder item paths
+                for(sub_item in sub_dir) {
+                    results.push(sub_dir[sub_item]);
+                }
+            }
+            else if(stat.isFile()) {
+                results.push(path.join(name, folder_dir[item]));
+            }
+            // else { it is a FIFO, or a Socket, Symbolic Link or something ... }
+        }
+        return results;
+    },
+
+    // calls the nessesary functions to update the windows8 project
+    update_project:function(cfg) {
+        //console.log("Updating windows8 project...");
+
+        try {
+            this.update_from_config(cfg);
+        } catch(e) {
+            return Q.reject(e);
+        }
+
+        var that = this;
+        var projectRoot = util.isCordova(process.cwd());
+
+        var hooks = new hooker(projectRoot);
+        return hooks.fire('pre_package', { wwwPath:this.www_dir(), platforms: ['windows8'] })
+        .then(function() {
+            // overrides (merges) are handled in update_www()
+            that.update_jsproj();
+            that.add_bom();
+            util.deleteSvnFolders(that.www_dir());
+        });
+    },
+
+    // Adjust version number as per CB-5337 Windows8 build fails due to invalid app version
+    fixConfigVersion: function (version) {
+        if(version && version.match(/\.\d/g)) {
+            var numVersionComponents = version.match(/\.\d/g).length + 1;
+            while (numVersionComponents++ < 4) {
+                version += '.0';
+            }
+        }
+        return version;
+    },
+
+    // CB-5421 Add BOM to all html, js, css files to ensure app can pass Windows Store Certification
+    add_bom: function () {
+        var www = this.www_dir();
+        var files = shell.ls('-R', www);
+
+        files.forEach(function (file) {
+            if (!file.match(/\.(js|html|css|json)/)) {
+                return;
+            }
+
+            var filePath = path.join(www, file);
+            var content = fs.readFileSync(filePath);
+
+            if (content[0] !== 0xEF && content[1] !== 0xBE && content[2] !== 0xBB) {
+                fs.writeFileSync(filePath, '\ufeff' + content);
+            }
+        });
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/b51e1c12/cordova-lib/src/cordova/metadata/wp7_parser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/metadata/wp7_parser.js b/cordova-lib/src/cordova/metadata/wp7_parser.js
new file mode 100644
index 0000000..91db24c
--- /dev/null
+++ b/cordova-lib/src/cordova/metadata/wp7_parser.js
@@ -0,0 +1,262 @@
+/**
+    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'),
+    et            = require('elementtree'),
+    xml           = require('../xml-helpers'),
+    util          = require('../util'),
+    events        = require('../events'),
+    shell         = require('shelljs'),
+    child_process = require('child_process'),
+    Q             = require('q'),
+    ConfigParser  = require('../ConfigParser'),
+    CordovaError  = require('../CordovaError'),
+    config        = require('../config'),
+    hooker        = require('../hooker');
+
+module.exports = function wp7_parser(project) {
+    try {
+        // TODO : Check that it's not a wp7 project?
+        var csproj_file   = fs.readdirSync(project).filter(function(e) { return e.match(/\.csproj$/i); })[0];
+        if (!csproj_file) throw new CordovaError('No .csproj file in "'+project+'"');
+        this.wp7_proj_dir = project;
+        this.csproj_path  = path.join(this.wp7_proj_dir, csproj_file);
+        this.sln_path     = path.join(this.wp7_proj_dir, csproj_file.replace(/\.csproj/, '.sln'));
+    } catch(e) {
+        throw new CordovaError('The provided path "' + project + '" is not a Windows Phone 7 project. ' + e);
+    }
+    this.manifest_path  = path.join(this.wp7_proj_dir, 'Properties', 'WMAppManifest.xml');
+};
+
+// Returns a promise.
+module.exports.check_requirements = function(project_root) {
+    events.emit('log', 'Checking wp7 requirements...');
+    var lib_path = path.join(util.libDirectory, 'wp', 'cordova', require('../../platforms').wp7.version, 'wp7');
+    var custom_path = config.has_custom_path(project_root, 'wp7');
+    if (custom_path) {
+        lib_path = path.join(custom_path, 'wp7');
+    }
+    var command = '"' + path.join(lib_path, 'bin', 'check_reqs') + '"';
+    events.emit('verbose', 'Running "' + command + '" (output to follow)');
+    var d = Q.defer();
+    child_process.exec(command, function(err, output, stderr) {
+        events.emit('verbose', output+stderr);
+        if (err) {
+            d.reject(new CordovaError('Requirements check failed: ' + output + stderr));
+        } else {
+            d.resolve();
+        }
+    });
+    return d.promise;
+};
+
+module.exports.prototype = {
+    update_from_config:function(config) {
+        //check config parser
+        if (config instanceof ConfigParser) {
+        } else throw new Error('update_from_config requires a ConfigParser object');
+
+        //Get manifest file
+        var manifest = xml.parseElementtreeSync(this.manifest_path);
+
+        //Update app version
+        var version = config.version();
+        manifest.find('.//App').attrib.Version = version;
+
+        // Update app name by editing app title in Properties\WMAppManifest.xml
+        var name = config.name();
+        var prev_name = manifest.find('.//App[@Title]')['attrib']['Title'];
+        if(prev_name != name) {
+            events.emit('verbose', "Updating app name from " + prev_name + " to " + name);
+            manifest.find('.//App').attrib.Title = name;
+            manifest.find('.//App').attrib.Publisher = name + " Publisher";
+            manifest.find('.//App').attrib.Author = name + " Author";
+            manifest.find('.//PrimaryToken').attrib.TokenID = name;
+            //update name of sln and csproj.
+            name = name.replace(/(\.\s|\s\.|\s+|\.+)/g, '_'); //make it a ligitamate name
+            prev_name = prev_name.replace(/(\.\s|\s\.|\s+|\.+)/g, '_');
+            var sln_name = fs.readdirSync(this.wp7_proj_dir).filter(function(e) { return e.match(/\.sln$/i); })[0];
+            var sln_path = path.join(this.wp7_proj_dir, sln_name);
+            var sln_file = fs.readFileSync(sln_path, 'utf-8');
+            var name_regex = new RegExp(prev_name, "g");
+            fs.writeFileSync(sln_path, sln_file.replace(name_regex, name), 'utf-8');
+            shell.mv('-f', this.csproj_path, path.join(this.wp7_proj_dir, name + '.csproj'));
+            this.csproj_path = path.join(this.wp7_proj_dir, name + '.csproj');
+            shell.mv('-f', sln_path, path.join(this.wp7_proj_dir, name + '.sln'));
+            this.sln_path    = path.join(this.wp7_proj_dir, name + '.sln');
+        }
+
+        // Update package name by changing:
+        /*  - CordovaAppProj.csproj
+         *  - MainPage.xaml
+         *  - MainPage.xaml.cs
+         *  - App.xaml
+         *  - App.xaml.cs
+         */
+         var pkg = config.packageName();
+         var csproj = xml.parseElementtreeSync(this.csproj_path);
+         prev_name = csproj.find('.//RootNamespace').text;
+         if(prev_name != pkg) {
+            events.emit('verbose', "Updating package name from " + prev_name + " to " + pkg);
+            //CordovaAppProj.csproj
+            csproj.find('.//RootNamespace').text = pkg;
+            csproj.find('.//AssemblyName').text = pkg;
+            csproj.find('.//XapFilename').text = pkg + '.xap';
+            csproj.find('.//SilverlightAppEntry').text = pkg + '.App';
+            fs.writeFileSync(this.csproj_path, csproj.write({indent: 4}), 'utf-8');
+            //MainPage.xaml
+            var mainPageXAML = xml.parseElementtreeSync(path.join(this.wp7_proj_dir, 'MainPage.xaml'));
+            mainPageXAML.getroot().attrib['x:Class'] = pkg + '.MainPage';
+            fs.writeFileSync(path.join(this.wp7_proj_dir, 'MainPage.xaml'), mainPageXAML.write({indent: 4}), 'utf-8');
+            //MainPage.xaml.cs
+            var mainPageCS = fs.readFileSync(path.join(this.wp7_proj_dir, 'MainPage.xaml.cs'), 'utf-8');
+            var namespaceRegEx = new RegExp('namespace ' + prev_name);
+            fs.writeFileSync(path.join(this.wp7_proj_dir, 'MainPage.xaml.cs'), mainPageCS.replace(namespaceRegEx, 'namespace ' + pkg), 'utf-8');
+            //App.xaml
+            var appXAML = xml.parseElementtreeSync(path.join(this.wp7_proj_dir, 'App.xaml'));
+            appXAML.getroot().attrib['x:Class'] = pkg + '.App';
+            fs.writeFileSync(path.join(this.wp7_proj_dir, 'App.xaml'), appXAML.write({indent: 4}), 'utf-8');
+            //App.xaml.cs
+            var appCS = fs.readFileSync(path.join(this.wp7_proj_dir, 'App.xaml.cs'), 'utf-8');
+            fs.writeFileSync(path.join(this.wp7_proj_dir, 'App.xaml.cs'), appCS.replace(namespaceRegEx, 'namespace ' + pkg), 'utf-8');
+         }
+
+         //Write out manifest
+         fs.writeFileSync(this.manifest_path, manifest.write({indent: 4}), 'utf-8');
+    },
+    // Returns the platform-specific www directory.
+    www_dir:function() {
+        return path.join(this.wp7_proj_dir, 'www');
+    },
+    config_xml:function() {
+        return path.join(this.wp7_proj_dir, 'config.xml');
+    },
+    // copy files from merges directory to actual www dir
+    copy_merges:function(merges_sub_path) {
+        var merges_path = path.join(util.appDir(util.isCordova(this.wp7_proj_dir)), 'merges', merges_sub_path);
+        if (fs.existsSync(merges_path)) {
+            var overrides = path.join(merges_path, '*');
+            shell.cp('-rf', overrides, this.www_dir());
+        }
+    },
+
+    // Used for creating platform_www in projects created by older versions.
+    cordovajs_path:function(libDir) {
+        var jsPath = path.join(libDir, '..', 'common', 'www', 'cordova.js');
+        return path.resolve(jsPath);
+    },
+
+    // Replace the www dir with contents of platform_www and app www and updates the csproj file.
+    update_www:function() {
+        var projectRoot = util.isCordova(this.wp7_proj_dir);
+        var app_www = util.projectWww(projectRoot);
+        var platform_www = path.join(this.wp7_proj_dir, 'platform_www');
+
+        // Clear the www dir
+        shell.rm('-rf', this.www_dir());
+        shell.mkdir(this.www_dir());
+        // Copy over all app www assets
+        shell.cp('-rf', path.join(app_www, '*'), this.www_dir());
+
+        // Copy all files from merges directories - wp generic first, then wp7 specific.
+        this.copy_merges('wp');
+        this.copy_merges('wp7');
+        // Copy over stock platform www assets (cordova.js)
+        shell.cp('-rf', path.join(platform_www, '*'), this.www_dir());
+    },
+
+    // updates the csproj file to explicitly list all www content.
+    update_csproj:function() {
+        var csproj_xml = xml.parseElementtreeSync(this.csproj_path);
+        // remove any previous references to the www files
+        var item_groups = 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, 3) == 'www') {
+                    // remove file reference
+                    group.remove(0, file);
+                    // remove ItemGroup if empty
+                    var new_group = group.findall('Content');
+                    if(new_group.length < 1) {
+                        csproj_xml.getroot().remove(0, group);
+                    }
+                }
+            }
+        }
+
+        // now add all www references back in from the wp www folder
+        var www_files = this.folder_contents('www', this.www_dir());
+        for(file in www_files) {
+            var item = new et.Element('ItemGroup');
+            var content = new et.Element('Content');
+            content.attrib.Include = www_files[file];
+            item.append(content);
+            csproj_xml.getroot().append(item);
+        }
+        // save file
+        fs.writeFileSync(this.csproj_path, csproj_xml.write({indent:4}), 'utf-8');
+    },
+    // Returns an array of all the files in the given directory with relative paths
+    // - name     : the name of the top level directory (i.e all files will start with this in their path)
+    // - dir      : the directory whos contents will be listed under 'name' directory
+    folder_contents:function(name, dir) {
+        var results = [];
+        var folder_dir = fs.readdirSync(dir);
+        for(item in folder_dir) {
+            var stat = fs.statSync(path.join(dir, folder_dir[item]));
+
+            if(stat.isDirectory()) {
+                var sub_dir = this.folder_contents(path.join(name, folder_dir[item]), path.join(dir, folder_dir[item]));
+                //Add all subfolder item paths
+                for(sub_item in sub_dir) {
+                    results.push(sub_dir[sub_item]);
+                }
+            }
+            else if(stat.isFile()) {
+                results.push(path.join(name, folder_dir[item]));
+            }
+            // else { it is a FIFO, or a Socket or something ... }
+        }
+        return results;
+    },
+
+    // calls the nessesary functions to update the wp7 project
+    // Returns a promise.
+    update_project:function(cfg) {
+        try {
+            this.update_from_config(cfg);
+        } catch(e) {
+            return Q.reject(e);
+        }
+        // trigger an event in case anyone needs to modify the contents of the www folder before we package it.
+        var that = this;
+        var projectRoot = util.isCordova(process.cwd());
+        var hooks = new hooker(projectRoot);
+
+        return hooks.fire('pre_package', { wwwPath:this.www_dir(), platforms: ['wp7']  })
+        .then(function() {
+            that.update_csproj();
+            // TODO: Add overrides support? Why is this missing?
+            util.deleteSvnFolders(that.www_dir());
+        });
+    }
+};


Mime
View raw message