You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by br...@apache.org on 2013/05/23 23:58:40 UTC

[43/50] git commit: Merge branch 'future' into master

Merge branch 'future' into master

Conflicts:
	lib/cordova-android/bin/templates/cordova/appinfo.jar
	spec/cordova-cli/config_parser.spec.js
	spec/cordova-cli/create.spec.js
	spec/cordova-cli/platform.spec.js
	src/create.js
	src/metadata/blackberry_parser.js
	src/platform.js
	src/plugin.js
	src/plugin_loader.js
	src/util.js


Project: http://git-wip-us.apache.org/repos/asf/cordova-cli/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-cli/commit/a79365d8
Tree: http://git-wip-us.apache.org/repos/asf/cordova-cli/tree/a79365d8
Diff: http://git-wip-us.apache.org/repos/asf/cordova-cli/diff/a79365d8

Branch: refs/heads/master2
Commit: a79365d86b768158555aa6e11a0c25b0b81e3581
Parents: f283c8a a18cade
Author: Ian Clelland <ic...@chromium.org>
Authored: Thu May 23 16:15:16 2013 -0400
Committer: Ian Clelland <ic...@chromium.org>
Committed: Thu May 23 16:16:30 2013 -0400

----------------------------------------------------------------------
 .reviewboardrc                                     |    8 +
 README.md                                          |   81 +-
 bootstrap.js                                       |    6 +-
 package.json                                       |    2 +-
 spec/cordova-cli/platform.spec.js                  |   91 +-
 spec/cordova-cli/plugin.spec.js                    |   22 +-
 spec/cordova-cli/serve.spec.js                     |    2 +-
 .../platform-script/android/android_parser.spec.js |    8 +-
 .../blackberry/blackberry_parser.spec.js           |   10 +-
 spec/platform-script/ios/ios_parser.spec.js        |    8 +-
 src/compile.js                                     |    3 +-
 src/create.js                                      |   15 +-
 src/emulate.js                                     |    2 +-
 src/metadata/android_parser.js                     |   24 +-
 src/metadata/blackberry_parser.js                  |   22 +-
 src/metadata/ios_parser.js                         |   27 +-
 src/platform.js                                    |   21 +-
 src/plugin.js                                      |  109 +-
 src/plugin_loader.js                               |  200 --
 src/prepare.js                                     |    9 +-
 src/serve.js                                       |   14 +-
 templates/app/config.xml                           |   49 +
 templates/app/www/css/index.css                    |  100 +
 templates/app/www/img/cordova.png                  |  Bin 0 -> 19932 bytes
 templates/app/www/index.html                       |   24 +
 templates/app/www/js/index.js                      |   20 +
 templates/app/www/res/icon/cordova_128.png         |  Bin 0 -> 11401 bytes
 templates/app/www/res/icon/cordova_16.png          |  Bin 0 -> 1699 bytes
 templates/app/www/res/icon/cordova_24.png          |  Bin 0 -> 2215 bytes
 templates/app/www/res/icon/cordova_256.png         |  Bin 0 -> 27408 bytes
 templates/app/www/res/icon/cordova_32.png          |  Bin 0 -> 2843 bytes
 templates/app/www/res/icon/cordova_48.png          |  Bin 0 -> 4111 bytes
 templates/app/www/res/icon/cordova_512.png         |  Bin 0 -> 39830 bytes
 templates/app/www/res/icon/cordova_64.png          |  Bin 0 -> 5463 bytes
 templates/app/www/res/icon/cordova_android_36.png  |  Bin 0 -> 3096 bytes
 templates/app/www/res/icon/cordova_android_48.png  |  Bin 0 -> 4090 bytes
 templates/app/www/res/icon/cordova_android_72.png  |  Bin 0 -> 6080 bytes
 templates/app/www/res/icon/cordova_android_96.png  |  Bin 0 -> 7685 bytes
 templates/app/www/res/icon/cordova_bb_80.png       |  Bin 0 -> 7287 bytes
 templates/app/www/res/icon/cordova_ios_114.png     |  Bin 0 -> 7869 bytes
 templates/app/www/res/icon/cordova_ios_144.png     |  Bin 0 -> 11706 bytes
 templates/app/www/res/icon/cordova_ios_57.png      |  Bin 0 -> 3908 bytes
 templates/app/www/res/icon/cordova_ios_72.png      |  Bin 0 -> 4944 bytes
 .../app/www/res/screen/android_hdpi_landscape.png  |  Bin 0 -> 218302 bytes
 .../app/www/res/screen/android_hdpi_portrait.png   |  Bin 0 -> 222148 bytes
 .../app/www/res/screen/android_ldpi_landscape.png  |  Bin 0 -> 42616 bytes
 .../app/www/res/screen/android_ldpi_portrait.png   |  Bin 0 -> 42034 bytes
 .../app/www/res/screen/android_mdpi_landscape.png  |  Bin 0 -> 92347 bytes
 .../app/www/res/screen/android_mdpi_portrait.png   |  Bin 0 -> 90555 bytes
 .../app/www/res/screen/android_xhdpi_landscape.png |  Bin 0 -> 489604 bytes
 .../app/www/res/screen/android_xhdpi_portrait.png  |  Bin 0 -> 504508 bytes
 .../www/res/screen/blackberry_transparent_300.png  |  Bin 0 -> 15823 bytes
 .../www/res/screen/blackberry_transparent_400.png  |  Bin 0 -> 11001 bytes
 templates/app/www/res/screen/ipad_landscape.png    |  Bin 0 -> 407370 bytes
 templates/app/www/res/screen/ipad_portrait.png     |  Bin 0 -> 422441 bytes
 .../app/www/res/screen/ipad_retina_landscape.png   |  Bin 0 -> 1534088 bytes
 .../app/www/res/screen/ipad_retina_portrait.png    |  Bin 0 -> 1610434 bytes
 templates/app/www/res/screen/iphone_landscape.png  |  Bin 0 -> 92301 bytes
 templates/app/www/res/screen/iphone_portrait.png   |  Bin 0 -> 93897 bytes
 .../app/www/res/screen/iphone_retina_landscape.png |  Bin 0 -> 339639 bytes
 .../app/www/res/screen/iphone_retina_portrait.png  |  Bin 0 -> 350593 bytes
 .../app/www/res/screen/windows_phone_portrait.jpg  |  Bin 0 -> 11483 bytes
 templates/app/www/spec.html                        |   50 +
 templates/app/www/spec/helper.js                   |   11 +
 templates/app/www/spec/index.js                    |   49 +
 .../app/www/spec/lib/jasmine-1.2.0/MIT.LICENSE     |   20 +
 .../app/www/spec/lib/jasmine-1.2.0/jasmine-html.js |  616 ++++
 .../app/www/spec/lib/jasmine-1.2.0/jasmine.css     |   81 +
 .../app/www/spec/lib/jasmine-1.2.0/jasmine.js      | 2529 +++++++++++++++
 templates/www/config.xml                           |   49 -
 templates/www/css/index.css                        |  100 -
 templates/www/img/cordova.png                      |  Bin 19932 -> 0 bytes
 templates/www/index.html                           |   24 -
 templates/www/js/index.js                          |   20 -
 templates/www/res/icon/cordova_128.png             |  Bin 11401 -> 0 bytes
 templates/www/res/icon/cordova_16.png              |  Bin 1699 -> 0 bytes
 templates/www/res/icon/cordova_24.png              |  Bin 2215 -> 0 bytes
 templates/www/res/icon/cordova_256.png             |  Bin 27408 -> 0 bytes
 templates/www/res/icon/cordova_32.png              |  Bin 2843 -> 0 bytes
 templates/www/res/icon/cordova_48.png              |  Bin 4111 -> 0 bytes
 templates/www/res/icon/cordova_512.png             |  Bin 39830 -> 0 bytes
 templates/www/res/icon/cordova_64.png              |  Bin 5463 -> 0 bytes
 templates/www/res/icon/cordova_android_36.png      |  Bin 3096 -> 0 bytes
 templates/www/res/icon/cordova_android_48.png      |  Bin 4090 -> 0 bytes
 templates/www/res/icon/cordova_android_72.png      |  Bin 6080 -> 0 bytes
 templates/www/res/icon/cordova_android_96.png      |  Bin 7685 -> 0 bytes
 templates/www/res/icon/cordova_bb_80.png           |  Bin 7287 -> 0 bytes
 templates/www/res/icon/cordova_ios_114.png         |  Bin 7869 -> 0 bytes
 templates/www/res/icon/cordova_ios_144.png         |  Bin 11706 -> 0 bytes
 templates/www/res/icon/cordova_ios_57.png          |  Bin 3908 -> 0 bytes
 templates/www/res/icon/cordova_ios_72.png          |  Bin 4944 -> 0 bytes
 .../www/res/screen/android_hdpi_landscape.png      |  Bin 218302 -> 0 bytes
 templates/www/res/screen/android_hdpi_portrait.png |  Bin 222148 -> 0 bytes
 .../www/res/screen/android_ldpi_landscape.png      |  Bin 42616 -> 0 bytes
 templates/www/res/screen/android_ldpi_portrait.png |  Bin 42034 -> 0 bytes
 .../www/res/screen/android_mdpi_landscape.png      |  Bin 92347 -> 0 bytes
 templates/www/res/screen/android_mdpi_portrait.png |  Bin 90555 -> 0 bytes
 .../www/res/screen/android_xhdpi_landscape.png     |  Bin 489604 -> 0 bytes
 .../www/res/screen/android_xhdpi_portrait.png      |  Bin 504508 -> 0 bytes
 .../www/res/screen/blackberry_transparent_300.png  |  Bin 15823 -> 0 bytes
 .../www/res/screen/blackberry_transparent_400.png  |  Bin 11001 -> 0 bytes
 templates/www/res/screen/ipad_landscape.png        |  Bin 407370 -> 0 bytes
 templates/www/res/screen/ipad_portrait.png         |  Bin 422441 -> 0 bytes
 templates/www/res/screen/ipad_retina_landscape.png |  Bin 1534088 -> 0 bytes
 templates/www/res/screen/ipad_retina_portrait.png  |  Bin 1610434 -> 0 bytes
 templates/www/res/screen/iphone_landscape.png      |  Bin 92301 -> 0 bytes
 templates/www/res/screen/iphone_portrait.png       |  Bin 93897 -> 0 bytes
 .../www/res/screen/iphone_retina_landscape.png     |  Bin 339639 -> 0 bytes
 .../www/res/screen/iphone_retina_portrait.png      |  Bin 350593 -> 0 bytes
 .../www/res/screen/windows_phone_portrait.jpg      |  Bin 11483 -> 0 bytes
 templates/www/spec.html                            |   50 -
 templates/www/spec/helper.js                       |   11 -
 templates/www/spec/index.js                        |   49 -
 templates/www/spec/lib/jasmine-1.2.0/MIT.LICENSE   |   20 -
 .../www/spec/lib/jasmine-1.2.0/jasmine-html.js     |  616 ----
 templates/www/spec/lib/jasmine-1.2.0/jasmine.css   |   81 -
 templates/www/spec/lib/jasmine-1.2.0/jasmine.js    | 2529 ---------------
 117 files changed, 3860 insertions(+), 3922 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/README.md
----------------------------------------------------------------------
diff --cc README.md
index 48517a2,291c037..db232ce
--- a/README.md
+++ b/README.md
@@@ -100,11 -102,15 +102,17 @@@ This file is what you should be editin
  - The whitelist can be modified using the `<access>` elements. Make sure the `origin` attribute of your `<access>` element points to a valid URL (you can use `*` as wildcard). For more information on the whitelisting syntax, see the [docs.phonegap.com](http://docs.phonegap.com/en/2.2.0/guide_whitelist_index.md.html#Domain%20Whitelist%20Guide). You can use either attribute `uri` ([BlackBerry-proprietary](https://developer.blackberry.com/html5/documentation/access_element_834677_11.html)) or `origin` ([standards-compliant](http://www.w3.org/TR/widgets-access/#attributes)) to denote the domain.
  - Platform-specific preferences can be customized via `<preference>` tags. See [docs.phonegap.com](http://docs.phonegap.com/en/2.3.0/guide_project-settings_index.md.html#Project%20Settings) for a list of preferences you can use.
  
+ ## platforms/
+ Platforms added to your application will have the native application project structures laid out within this directory.
+ 
+ ## plugins/
+ Any added plugins will be extracted or copied into this directory.
+ 
  # Hooks
  
 -Projects created by cordova-cli have `before` and `after` hooks for each [project command](#project_commands). There are two types of hooks: project-specific ones and module-level ones.
 +Projects created by cordova-cli have `before` and `after` hooks for each [project command](#project_commands).
 +
 +There are two types of hooks: project-specific ones and module-level ones. Both of these types of hooks receive the project root folder as a parameter.
  
  ## Project-specific Hooks
  

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/bootstrap.js
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/package.json
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/spec/cordova-cli/platform.spec.js
----------------------------------------------------------------------
diff --cc spec/cordova-cli/platform.spec.js
index bbf3959,0000000..698582c
mode 100644,000000..100644
--- a/spec/cordova-cli/platform.spec.js
+++ b/spec/cordova-cli/platform.spec.js
@@@ -1,256 -1,0 +1,345 @@@
 +/**
 +    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 cordova = require('../../cordova'),
 +    path = require('path'),
 +    shell = require('shelljs'),
 +    request = require('request'),
 +    fs = require('fs'),
 +    et = require('elementtree'),
 +    config_parser = require('../../src/config_parser'),
 +    helper = require('./helper'),
 +    util = require('../../src/util'),
 +    hooker = require('../../src/hooker'),
 +    platforms = require('../../platforms'),
 +    tempDir = path.join(__dirname, '..', '..', 'temp');
 +    android_parser = require('../../src/metadata/android_parser'),
 +    blackberry_parser = require('../../src/metadata/blackberry_parser'),
 +    cordova_project = path.join(__dirname, '..', 'fixtures', 'projects', 'cordova');
 +
 +var cwd = process.cwd();
 +
 +describe('platform command', function() {
 +    beforeEach(function() {
 +        // Make a temp directory
 +        shell.rm('-rf', tempDir);
 +        shell.mkdir('-p', tempDir);
 +    });
 +    it('should run inside a Cordova-based project', function() {
 +        this.after(function() {
 +            process.chdir(cwd);
 +        });
 +
 +        cordova.create(tempDir);
 +
 +        process.chdir(tempDir);
 +
 +        expect(function() {
 +            cordova.platform();
 +        }).not.toThrow();
 +    });
 +    it('should not run outside of a Cordova-based project', function() {
 +        this.after(function() {
 +            process.chdir(cwd);
 +        });
 +
 +        process.chdir(tempDir);
 +
 +        expect(function() {
 +            cordova.platform();
 +        }).toThrow();
 +    });
 +
 +    describe('`ls`', function() { 
 +        beforeEach(function() {
 +            process.chdir(cordova_project);
 +        });
 +
 +        afterEach(function() {
 +            process.chdir(cwd);
 +        });
 +
 +        it('should list out no platforms for a fresh project', function() {
 +            shell.mv('-f', path.join(cordova_project, 'platforms', '*'), tempDir);
 +            this.after(function() {
 +                shell.mv('-f', path.join(tempDir, '*'), path.join(cordova_project, 'platforms'));
 +            });
 +            expect(cordova.platform('list').length).toEqual(0);
 +        });
 +
 +        it('should list out added platforms in a project', function() {
 +            process.chdir(tempDir);
 +            cordova.create(tempDir);
 +            shell.cp('-Rf', path.join(cordova_project, 'platforms', 'android'), path.join(tempDir, 'platforms'));
 +            shell.cp('-Rf', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir, 'platforms'));
 +            expect(cordova.platform('list').length).toEqual(2);
 +        });
 +    });
 +
 +    describe('`add`', function() {
 +        beforeEach(function() {
 +            cordova.create(tempDir);
 +            process.chdir(tempDir);
 +        });
 +
 +        afterEach(function() {
 +            process.chdir(cwd);
 +        });
-         
++
++        describe('android', function() {
++            var sh, cr;
++            var fake_reqs_check = function() {
++                cr.mostRecentCall.args[0](false);
++            };
++            var fake_create = function(a_path) {
++                shell.mkdir('-p', a_path);
++                fs.writeFileSync(path.join(a_path, 'AndroidManifest.xml'), 'hi', 'utf-8');
++                sh.mostRecentCall.args[2](0, '');
++            };
++            beforeEach(function() {
++                sh = spyOn(shell, 'exec');
++                cr = spyOn(android_parser, 'check_requirements');
++            });
++
++            it('should shell out to android ./bin/create', function() {
++                cordova.platform('add', 'android');
++                fake_reqs_check();
++                var shell_cmd = sh.mostRecentCall.args[0];
++                expect(shell_cmd).toMatch(/android\/bin\/create/);
++            });
++            it('should call android_parser\'s update_project', function() {
++                var s = spyOn(android_parser.prototype, 'update_project');
++                cordova.platform('add', 'android');
++                fake_reqs_check();
++                fake_create(path.join(tempDir, 'platforms', 'android'));
++                expect(s).toHaveBeenCalled();
++            });
++        });
++        describe('ios', function() {
++            var sh, cr;
++            var fake_reqs_check = function() {
++                cr.mostRecentCall.args[0](false);
++            };
++            var fake_create = function(a_path) {
++                shell.mkdir('-p', a_path);
++                fs.writeFileSync(path.join(a_path, 'poo.xcodeproj'), 'hi', 'utf-8');
++                shell.mkdir('-p', path.join(a_path, 'poo'));
++                shell.cp(util.projectConfig(cordova_project), path.join(a_path, 'poo', 'config.xml'));
++                sh.mostRecentCall.args[2](0, '');
++            };
++            beforeEach(function() {
++                sh = spyOn(shell, 'exec');
++                cr = spyOn(ios_parser, 'check_requirements');
++            });
++            it('should shell out to ios ./bin/create', function() {
++                cordova.platform('add', 'ios');
++                fake_reqs_check();
++                var shell_cmd = sh.mostRecentCall.args[0];
++                expect(shell_cmd).toMatch(/ios\/bin\/create/);
++            });
++            it('should call ios_parser\'s update_project', function() {
++                var s = spyOn(ios_parser.prototype, 'update_project');
++                cordova.platform('add', 'ios');
++                fake_reqs_check();
++                fake_create(path.join(tempDir, 'platforms', 'ios'));
++                expect(s).toHaveBeenCalled();
++            });
++        });
++        describe('blackberry', function() {
++            var sh, cr;
++            var fake_reqs_check = function() {
++                cr.mostRecentCall.args[0](false);
++            };
++            var fake_create = function(a_path) {
++                shell.mkdir('-p', path.join(a_path, 'www'));
++                fs.writeFileSync(path.join(a_path, 'project.properties'), 'hi', 'utf-8');
++                fs.writeFileSync(path.join(a_path, 'build.xml'), 'hi', 'utf-8');
++                shell.cp(util.projectConfig(cordova_project), path.join(a_path, 'www', 'config.xml'));
++                sh.mostRecentCall.args[2](0, '');
++            };
++            beforeEach(function() {
++                sh = spyOn(shell, 'exec');
++                cr = spyOn(blackberry_parser, 'check_requirements');
++            });
++            it('should shell out to blackberry bin/create', function() {
++                cordova.platform('add', 'blackberry');
++                fake_reqs_check();
++                var shell_cmd = sh.mostRecentCall.args[0];
++                expect(shell_cmd).toMatch(/blackberry\/bin\/create/);
++            });
++            it('should call blackberry_parser\'s update_project', function() {
++                var s = spyOn(blackberry_parser.prototype, 'update_project');
++                cordova.platform('add', 'blackberry');
++                fake_reqs_check();
++                fake_create(path.join(tempDir, 'platforms', 'blackberry'));
++                expect(s).toHaveBeenCalled();
++            });
++        });
 +        it('should handle multiple platforms', function() {
 +            var arc = spyOn(android_parser, 'check_requirements');
 +            var brc = spyOn(blackberry_parser, 'check_requirements');
 +            var sh = spyOn(shell, 'exec');
 +            cordova.platform('add', ['android', 'blackberry']);
 +            arc.mostRecentCall.args[0](false);
 +            brc.mostRecentCall.args[0](false);
 +            var android_create = path.join('android', 'bin', 'create');
 +            var bb_create      = path.join('blackberry', 'bin', 'create');
 +            expect(sh.argsForCall[0][0]).toContain(android_create);
 +            expect(sh.argsForCall[1][0]).toContain(bb_create);
 +        });
 +    });
 +
 +    describe('`remove`',function() {
 +        var num_platforms = fs.readdirSync(path.join(cordova_project, 'platforms')).length; 
 +        beforeEach(function() {
 +            process.chdir(cordova_project);
 +            shell.cp('-rf', path.join(cordova_project, 'platforms' ,'*'), tempDir);
 +        });
 +
 +        afterEach(function() {
 +            process.chdir(cwd);
 +            shell.cp('-rf', path.join(tempDir, '*'), path.join(cordova_project, 'platforms')); 
 +        });
 +
 +        it('should remove a supported and added platform', function() {
 +            cordova.platform('remove', 'android');
 +            expect(cordova.platform('ls').length).toEqual(num_platforms - 1);
 +        });
 +        it('should be able to remove multiple platforms', function() {
 +            cordova.platform('remove', ['android','blackberry']);
 +            expect(cordova.platform('ls').length).toEqual(num_platforms - 2);
 +        });
 +    });
 +
 +    describe('hooks', function() {
 +        var s;
 +        beforeEach(function() {
 +            cordova.create(tempDir);
 +            process.chdir(tempDir);
 +            s = spyOn(hooker.prototype, 'fire').andReturn(true);
 +        });
 +        afterEach(function() {
 +            process.chdir(cwd);
 +            shell.rm('-rf', tempDir);
 +        });
 +
 +        describe('list (ls) hooks', function() {
 +            it('should fire before hooks through the hooker module', function() {
 +                cordova.platform();
 +                expect(s).toHaveBeenCalledWith('before_platform_ls');
 +            });
 +            it('should fire after hooks through the hooker module', function() {
 +                cordova.platform();
 +                expect(s).toHaveBeenCalledWith('after_platform_ls');
 +            });
 +        });
 +        describe('remove (rm) hooks', function() {
 +            it('should fire before hooks through the hooker module', function() {
 +                cordova.platform('rm', 'android');
 +                expect(s).toHaveBeenCalledWith('before_platform_rm');
 +            });
 +            it('should fire after hooks through the hooker module', function() {
 +                cordova.platform('rm', 'android');
 +                expect(s).toHaveBeenCalledWith('after_platform_rm');
 +            });
 +        });
 +        describe('add hooks', function() {
 +            var sh, cr;
 +            var fake_reqs_check = function() {
 +                cr.mostRecentCall.args[0](false);
 +            };
 +            var fake_create = function(a_path) {
 +                shell.mkdir('-p', a_path);
 +                fs.writeFileSync(path.join(a_path, 'AndroidManifest.xml'), 'hi', 'utf-8');
 +                sh.mostRecentCall.args[2](0, '');
 +            };
 +            beforeEach(function() {
 +                sh = spyOn(shell, 'exec');
 +                cr = spyOn(android_parser, 'check_requirements');
 +            });
 +            it('should fire before and after hooks through the hooker module', function() {
 +                var ap = spyOn(android_parser.prototype, 'update_project');
 +                cordova.platform('add', 'android');
 +                fake_reqs_check();
 +                fake_create(path.join(tempDir, 'platforms', 'android'));
 +                ap.mostRecentCall.args[1](); // fake out update_project
 +                expect(s).toHaveBeenCalledWith('before_platform_add');
 +                expect(s).toHaveBeenCalledWith('after_platform_add');
 +            });
 +        });
 +    });
 +});
 +
 +describe('platform.supports(name, callback)', function() {
 +    var androidParser = require('../../src/metadata/android_parser');
 +
 +    beforeEach(function() {
 +        spyOn(androidParser, 'check_requirements');
 +    });
 +
 +    it('should require a platform name', function() {
 +        expect(function() {
 +            cordova.platform.supports(undefined, function(e){});
 +        }).toThrow();
 +    });
 +
 +    it('should require a callback function', function() {
 +        expect(function() {
 +            cordova.platform.supports('android', undefined);
 +        }).toThrow();
 +    });
 +
 +    describe('when platform is unknown', function() {
 +        it('should trigger callback with false', function(done) {
 +            cordova.platform.supports('windows-3.1', function(e) {
 +                expect(e).toEqual(jasmine.any(Error));
 +                done();
 +            });
 +        });
 +    });
 +
 +    describe('when platform is supported', function() {
 +        beforeEach(function() {
 +            androidParser.check_requirements.andCallFake(function(callback) {
 +                callback(null);
 +            });
 +        });
 +
 +        it('should trigger callback without error', function(done) {
 +            cordova.platform.supports('android', function(e) {
 +                expect(e).toBeNull();
 +                done();
 +            });
 +        });
 +    });
 +
 +    describe('when platform is unsupported', function() {
 +        beforeEach(function() {
 +            androidParser.check_requirements.andCallFake(function(callback) {
 +                callback(new Error('could not find the android sdk'));
 +            });
 +        });
 +
 +        it('should trigger callback with error', function(done) {
 +            cordova.platform.supports('android', function(e) {
 +                expect(e).toEqual(jasmine.any(Error));
 +                done();
 +            });
 +        });
 +    });
 +});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/spec/cordova-cli/plugin.spec.js
----------------------------------------------------------------------
diff --cc spec/cordova-cli/plugin.spec.js
index 061738f,0000000..bf97b58
mode 100644,000000..100644
--- a/spec/cordova-cli/plugin.spec.js
+++ b/spec/cordova-cli/plugin.spec.js
@@@ -1,164 -1,0 +1,146 @@@
 +/**
 +    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 cordova = require('../../cordova'),
 +    path = require('path'),
 +    shell = require('shelljs'),
 +    fs = require('fs'),
 +    hooker = require('../../src/hooker'),
 +    tempDir = path.join(__dirname, '..', '..', 'temp'),
 +    fixturesDir = path.join(__dirname, '..', 'fixtures'),
 +    testPlugin = path.join(fixturesDir, 'plugins', 'test'),
 +    cordova_project = path.join(fixturesDir, 'projects', 'cordova'),
 +    androidPlugin = path.join(fixturesDir, 'plugins', 'android');
 +
 +var cwd = process.cwd();
 +
 +describe('plugin command', function() {
 +    beforeEach(function() {
 +        // Make a temp directory
 +        shell.rm('-rf', tempDir);
 +        shell.mkdir('-p', tempDir);
 +    });
 +
 +    it('should run inside a Cordova-based project', function() {
 +        this.after(function() {
 +            process.chdir(cwd);
 +        });
 +
 +        cordova.create(tempDir);
 +
 +        process.chdir(tempDir);
 +
 +        expect(function() {
 +            cordova.plugin();
 +        }).not.toThrow();
 +    });
 +    it('should not run outside of a Cordova-based project', function() {
 +        this.after(function() {
 +            process.chdir(cwd);
 +        });
 +
 +        process.chdir(tempDir);
 +
 +        expect(function() {
 +            cordova.plugin();
 +        }).toThrow();
 +    });
 +
 +    describe('edge cases', function() {
 +       beforeEach(function() {
 +           cordova.create(tempDir);
 +           process.chdir(tempDir);
 +       });
 +
 +       afterEach(function() {
 +           process.chdir(cwd);
 +       });
 +
 +       it('should not fail when the plugins directory is missing', function() {
 +           fs.rmdirSync('plugins');
 +
 +           expect(function() {
 +               cordova.plugin();
 +           }).not.toThrow();
 +       });
 +
 +       it('should ignore files, like .gitignore, in the plugins directory', function() {
 +           var someFile = path.join(tempDir, 'plugins', '.gitignore');
 +           fs.writeFileSync(someFile, 'not a plugin');
 +
 +           expect(cordova.plugin('list')).toEqual('No plugins added. Use `cordova plugin add <plugin>`.');
 +       });
 +    });
 +
 +    describe('`ls`', function() {
 +        beforeEach(function() {
 +            cordova.create(tempDir);
 +        });
 +
 +        afterEach(function() {
 +            process.chdir(cwd);
 +        });
 +
 +        it('should list out no plugins for a fresh project', function() {
 +            process.chdir(tempDir);
 +
 +            expect(cordova.plugin('list')).toEqual('No plugins added. Use `cordova plugin add <plugin>`.');
 +        });
 +    });
 +
 +    describe('`add`', function() {
 +        beforeEach(function() {
 +            cordova.create(tempDir);
 +            process.chdir(tempDir);
 +        });
 +
 +        afterEach(function() {
 +            process.chdir(cwd);
 +        });
 +        describe('failure', function() {
-             it('should throw if your app has no platforms added', function() {
-                 expect(function() {
-                     cordova.plugin('add', testPlugin);
-                 }).toThrow('You need at least one platform added to your app. Use `cordova platform add <platform>`.');
-             });
-             it('should throw if plugin does not support any app platforms', function() {
-                 process.chdir(cordova_project);
-                 shell.mv('-f', path.join(cordova_project, 'platforms', 'android'), tempDir);
-                 shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), tempDir);
-                 this.after(function() {
-                     process.chdir(cwd);
-                     shell.mv('-f', path.join(tempDir, 'android'), path.join(cordova_project, 'platforms'));
-                     shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms'));
-                 });
-                 expect(function() {
-                     cordova.plugin('add', androidPlugin);
-                 }).toThrow('Plugin "android" does not support any of your application\'s platforms. Plugin platforms: android; your application\'s platforms: ios');
-             });
 +            it('should throw if plugin is already added to project', function() {
 +                process.chdir(cordova_project);
 +                var cb = jasmine.createSpy();
 +                this.after(function() {
 +                    process.chdir(cordova_project);
 +                    cordova.plugin('rm', "test");
 +                    process.chdir(cwd);
 +                });
 +                runs(function() {
 +                    cordova.plugin('add', testPlugin, cb);
 +                });
 +                waitsFor(function() { return cb.wasCalled; }, 'frst add plugin');
 +                runs(function(){
 +                    expect(function() {
 +                        cordova.plugin('add', testPlugin);
-                     }).toThrow('Plugin "test" already added to project.');
++                    }).toThrow();
 +                });
 +            });
 +            it('should throw if plugin does not have a plugin.xml', function() {
 +                process.chdir(cordova_project);
 +                this.after(function() {
 +                    process.chdir(cwd);
 +                });
 +                expect(function() {
 +                    cordova.plugin('add', fixturesDir);
-                 }).toThrow('Plugin "fixtures" does not have a plugin.xml in the root. Plugin must support the Cordova Plugin Specification: https://github.com/alunny/cordova-plugin-spec');
++                }).toThrow();
 +            });
 +        });
 +    });
 +});
 +

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/spec/cordova-cli/serve.spec.js
----------------------------------------------------------------------
diff --cc spec/cordova-cli/serve.spec.js
index 2e61212,0000000..0139da8
mode 100644,000000..100644
--- a/spec/cordova-cli/serve.spec.js
+++ b/spec/cordova-cli/serve.spec.js
@@@ -1,134 -1,0 +1,134 @@@
 +
 +/**
 +    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 cordova = require('../../cordova'),
 +    path = require('path'),
 +    shell = require('shelljs'),
 +    request = require('request'),
 +    fs = require('fs'),
 +    util = require('../../src/util'),
 +    hooker = require('../../src/hooker'),
 +    tempDir = path.join(__dirname, '..', '..', 'temp'),
 +    http = require('http'),
 +    android_parser = require('../../src/metadata/android_parser'),
 +    ios_parser = require('../../src/metadata/ios_parser'),
 +    blackberry_parser = require('../../src/metadata/blackberry_parser'),
 +    wp7_parser        = require('../../src/metadata/wp7_parser'),
 +    wp8_parser        = require('../../src/metadata/wp8_parser');
 +
 +var cwd = process.cwd();
 +
 +xdescribe('serve command', function() {
 +    beforeEach(function() {
 +        // Make a temp directory
 +        shell.rm('-rf', tempDir);
 +        shell.mkdir('-p', tempDir);
 +    });
 +    it('should not run outside of a Cordova-based project', function() {
 +        this.after(function() {
 +            process.chdir(cwd);
 +        });
 +
 +        process.chdir(tempDir);
 +
 +        expect(function() {
 +            cordova.serve('android');
 +        }).toThrow();
 +    });
 +
 +
 +    describe('`serve`', function() {
 +        var payloads = {
 +            android: 'This is the Android test file.',
 +            ios: 'This is the iOS test file.'
 +        };
 +
 +        beforeEach(function() {
 +            cordova.create(tempDir);
 +            process.chdir(tempDir);
 +            cordova.platform('add', 'android');
 +            cordova.platform('add', 'ios');
 +
 +            // Write testing HTML files into the directory.
 +            fs.writeFileSync(path.join(tempDir, 'platforms', 'android', 'assets', 'www', 'test.html'), payloads.android);
 +            fs.writeFileSync(path.join(tempDir, 'platforms', 'ios', 'www', 'test.html'), payloads.ios);
 +        });
 +
 +        afterEach(function() {
 +            process.chdir(cwd);
 +        });
 +
 +        function test_serve(platform, path, expectedContents, port) {
 +            return function() {
 +                var ret;
 +                runs(function() {
 +                    ret = port ? cordova.serve(platform, port) : cordova.serve(platform);
 +                });
 +
 +                waitsFor(function() {
 +                    return ret.server;
 +                }, 'the server should start', 1000);
 +
 +                var done, errorCB;
 +                runs(function() {
 +                    expect(ret.server).toBeDefined();
 +                    errorCB = jasmine.createSpy();
 +                    http.get({
 +                        host: 'localhost',
 +                        port: port || 8000,
 +                        path: path
 +                    }).on('response', function(res) {
 +                        var response = '';
 +                        res.on('data', function(data) {
 +                            response += data;
 +                        });
 +                        res.on('end', function() {
 +                            expect(res.statusCode).toEqual(200);
 +                            expect(response).toEqual(expectedContents);
 +                            done = true;
 +                        });
 +                    }).on('error', errorCB);
 +                });
 +
 +                waitsFor(function() {
 +                    return done;
 +                }, 'the HTTP request should complete', 1000);
 +
 +                runs(function() {
 +                    expect(done).toBeTruthy();
 +                    expect(errorCB).not.toHaveBeenCalled();
 +
 +                    ret.server.close();
 +                });
 +            };
 +        };
 +
 +        it('should serve from top-level www if the file exists there', function() {
 +            var payload = 'This is test file.';
-             fs.writeFileSync(path.join(tempDir, 'www', 'test.html'), payload);
++            fs.writeFileSync(path.join(util.projectWww(tempDir), 'test.html'), payload);
 +            test_serve('android', '/test.html', payload)();
 +        });
 +
 +        it('should fall back to assets/www on Android', test_serve('android', '/test.html', payloads.android));
 +        it('should fall back to www on iOS', test_serve('ios', '/test.html', payloads.ios));
 +
 +        it('should honour a custom port setting', test_serve('android', '/test.html', payloads.android, 9001));
 +    });
 +});
 +

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/spec/platform-script/android/android_parser.spec.js
----------------------------------------------------------------------
diff --cc spec/platform-script/android/android_parser.spec.js
index 03bf1a1,0000000..83b13dd
mode 100644,000000..100644
--- a/spec/platform-script/android/android_parser.spec.js
+++ b/spec/platform-script/android/android_parser.spec.js
@@@ -1,237 -1,0 +1,237 @@@
 +
 +/**
 +    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 android_parser = require('../../../src/metadata/android_parser'),
 +    config_parser = require('../../../src/config_parser'),
 +    util = require('../../../src/util'),
 +    path = require('path'),
 +    shell = require('shelljs'),
 +    fs = require('fs'),
 +    et = require('elementtree'),
 +    cordova = require('../../../cordova'),
 +    projects_path = path.join(__dirname, '..', '..', 'fixtures', 'projects'),
 +    android_path = path.join(projects_path, 'native', 'android_fixture'),
 +    project_path = path.join(projects_path, 'cordova'),
 +    android_project_path = path.join(project_path, 'platforms', 'android');
 +
- var www_config = path.join(project_path, 'www', 'config.xml');
++var www_config = util.projectConfig(project_path);
 +var original_www_config = fs.readFileSync(www_config, 'utf-8');
 +
 +describe('android project parser', function() {
 +    it('should throw an exception with a path that is not a native android project', function() {
 +        expect(function() {
 +            var project = new android_parser(process.cwd());
 +        }).toThrow();
 +    });
 +    it('should accept a proper native android project path as construction parameter', function() {
 +        expect(function() {
 +            var project = new android_parser(android_path);
 +            expect(project).toBeDefined();
 +        }).not.toThrow();
 +    });
 +
 +    describe('update_from_config method', function() {
 +        var project, config;
 +        
 +        var android_strings = path.join(android_path, 'res', 'values', 'strings.xml');
 +        var android_manifest = path.join(android_path, 'AndroidManifest.xml');
 +        var android_config = path.join(android_path, 'res', 'xml', 'config.xml');
 +
 +        var original_strings = fs.readFileSync(android_strings, 'utf-8');
 +        var original_manifest = fs.readFileSync(android_manifest, 'utf-8');
 +        var original_android_config = fs.readFileSync(android_config, 'utf-8');
 +
 +        beforeEach(function() {
 +            project = new android_parser(android_path);
 +            config = new config_parser(www_config);
 +        });
 +        afterEach(function() {
 +            fs.writeFileSync(android_strings, original_strings, 'utf-8');
 +            fs.writeFileSync(android_manifest, original_manifest, 'utf-8');
 +            fs.writeFileSync(www_config, original_www_config, 'utf-8');
 +            fs.writeFileSync(android_config, original_android_config, 'utf-8');
 +        });
 +        it('should throw an exception if a non config_parser object is passed into it', function() {
 +            expect(function() {
 +                project.update_from_config({});
 +            }).toThrow();
 +        });
 +        it('should update the application name properly', function() {
 +            config.name('bond. james bond.');
 +            project.update_from_config(config);
 +
 +            var strings = new et.ElementTree(et.XML(fs.readFileSync(android_strings, 'utf-8')));
 +            var app_name = strings.find('string[@name="app_name"]').text;
 +
 +            expect(app_name).toBe('bond. james bond.');
 +        });
 +        it('should update the application package name properly', function() {
 +            var javs = path.join(android_path, 'src', 'ca', 'filmaj', 'dewd', 'cordovaExample.java');
 +            var orig_javs = path.join(android_path, 'src', 'org', 'apache', 'cordova', 'cordovaExample', 'cordovaExample.java');
 +            var orig_contents = fs.readFileSync(orig_javs, 'utf-8');
 +            this.after(function() {
 +                fs.writeFileSync(orig_javs, orig_contents, 'utf-8');
 +                shell.rm('-rf', path.join(android_path, 'src', 'ca'));
 +            });
 +            config.packageName('ca.filmaj.dewd');
 +            project.update_from_config(config);
 +
 +            var manifest = new et.ElementTree(et.XML(fs.readFileSync(android_manifest, 'utf-8')));
 +            expect(manifest.getroot().attrib.package).toEqual('ca.filmaj.dewd');
 +
 +            expect(fs.existsSync(javs)).toBe(true);
 +            expect(fs.readFileSync(javs, 'utf-8')).toMatch(/package ca.filmaj.dewd/i);
 +        });
 +        it('should handle unsupported "-" in the application package name', function() {
 +            var javs = path.join(android_path, 'src', 'ca', 'filmaj', 'the_dewd', 'cordovaExample.java');
 +            var orig_javs = path.join(android_path, 'src', 'org', 'apache', 'cordova', 'cordovaExample', 'cordovaExample.java');
 +            var orig_contents = fs.readFileSync(orig_javs, 'utf-8');
 +            this.after(function() {
 +                fs.writeFileSync(orig_javs, orig_contents, 'utf-8');
 +                shell.rm('-rf', path.join(android_path, 'src', 'ca'));
 +            });
 +            config.packageName('ca.filmaj.the-dewd');
 +            project.update_from_config(config);
 +
 +            var manifest = new et.ElementTree(et.XML(fs.readFileSync(android_manifest, 'utf-8')));
 +            expect(manifest.getroot().attrib.package).toEqual('ca.filmaj.the_dewd');
 +
 +            expect(fs.existsSync(javs)).toBe(true);
 +            expect(fs.readFileSync(javs, 'utf-8')).toMatch(/package ca.filmaj.the_dewd/i);
 +        });
 +        it('should update the whitelist properly', function() {
 +            config.access.remove('*');
 +            config.access.add('http://apache.org');
 +            config.access.add('http://github.com');
 +            project.update_from_config(config);
 +
 +            var native_config = new et.ElementTree(et.XML(fs.readFileSync(android_config, 'utf-8')));
 +            var as = native_config.findall('access');
 +            expect(as.length).toEqual(2);
 +            expect(as[0].attrib.origin).toEqual('http://apache.org');
 +            expect(as[1].attrib.origin).toEqual('http://github.com');
 +        });
 +        describe('preferences', function() {
 +            it('should not change default project preferences and copy over additional project preferences to platform-level config.xml', function() {
 +                config.preference.add({name:'henrik',value:'sedin'});
 +                project.update_from_config(config);
 +
 +                var native_config = new et.ElementTree(et.XML(fs.readFileSync(android_config, 'utf-8')));
 +                var ps = native_config.findall('preference');
 +                expect(ps.length).toEqual(7);
 +                expect(ps[0].attrib.name).toEqual('useBrowserHistory');
 +                expect(ps[0].attrib.value).toEqual('true');
 +                expect(ps[6].attrib.name).toEqual('henrik');
 +                expect(ps[6].attrib.value).toEqual('sedin');
 +            });
 +            it('should override a default project preference if applicable', function() {
 +                config.preference.add({name:'useBrowserHistory',value:'false'});
 +                project.update_from_config(config);
 +
 +                var native_config = new et.ElementTree(et.XML(fs.readFileSync(android_config, 'utf-8')));
 +                var ps = native_config.findall('preference');
 +                expect(ps.length).toEqual(6);
 +                expect(ps[0].attrib.name).toEqual('useBrowserHistory');
 +                expect(ps[0].attrib.value).toEqual('false');
 +            });
 +        });
 +    });
 +
 +    describe('cross-platform project level methods', function() {
 +        var parser, config;
 +        var android_strings = path.join(android_project_path, 'res', 'values', 'strings.xml');
 +        var android_manifest = path.join(android_project_path, 'AndroidManifest.xml');
 +        var android_config = path.join(android_project_path, 'res', 'xml', 'config.xml');
 +
 +        var original_strings = fs.readFileSync(android_strings, 'utf-8');
 +        var original_manifest = fs.readFileSync(android_manifest, 'utf-8');
 +        var original_android_config = fs.readFileSync(android_config, 'utf-8');
 +
 +        beforeEach(function() {
 +            parser = new android_parser(android_project_path);
 +            config = new config_parser(www_config);
 +        });
 +        afterEach(function() {
 +            fs.writeFileSync(android_strings, original_strings, 'utf-8');
 +            fs.writeFileSync(android_manifest, original_manifest, 'utf-8');
 +            fs.writeFileSync(www_config, original_www_config, 'utf-8');
 +            fs.writeFileSync(android_config, original_android_config, 'utf-8');
 +        });
 +        describe('update_www method', function() {
 +            it('should update all www assets', function() {
-                 var newFile = path.join(project_path, 'www', 'somescript.js');
++                var newFile = path.join(util.projectWww(project_path), 'somescript.js');
 +                this.after(function() {
 +                    shell.rm('-f', newFile);
 +                });
 +                fs.writeFileSync(newFile, 'alert("sup");', 'utf-8');
 +                parser.update_www();
 +                expect(fs.existsSync(path.join(android_project_path, 'assets', 'www', 'somescript.js'))).toBe(true);
 +            });
 +            it('should write out android js to cordova.js', function() {
 +                parser.update_www();
 +                expect(fs.readFileSync(path.join(android_project_path, 'assets', 'www', 'cordova.js'),'utf-8')).toBe(fs.readFileSync(path.join(util.libDirectory, 'cordova-android', 'framework', 'assets', 'js', 'cordova.android.js'), 'utf-8'));
 +            });
 +        });
 +
 +        describe('update_overrides method',function() {
-             var mergesPath = path.join(project_path, 'merges', 'android');
++            var mergesPath = path.join(util.appDir(project_path), 'merges', 'android');
 +            var newFile = path.join(mergesPath, 'merge.js');
 +            beforeEach(function() {
 +                shell.mkdir('-p', mergesPath);
 +                fs.writeFileSync(newFile, 'alert("sup");', 'utf-8');
 +            });
 +            afterEach(function() {
 +                shell.rm('-rf', mergesPath);
 +            });
 +            it('should copy a new file from merges into www', function() {
 +                parser.update_overrides();
 +                expect(fs.existsSync(path.join(android_project_path, 'assets', 'www', 'merge.js'))).toBe(true);
 +            });
 +
 +            it('should copy a file from merges over a file in www', function() {
-                 var newFileWWW = path.join(project_path, 'www','merge.js');
++                var newFileWWW = path.join(util.projectWww(project_path), 'merge.js');
 +                fs.writeFileSync(newFileWWW, 'var foo=1;', 'utf-8');
 +                this.after(function() {
 +                    shell.rm('-rf', newFileWWW);
 +                });
 +                parser.update_overrides();
 +                expect(fs.existsSync(path.join(android_project_path, 'assets', 'www', 'merge.js'))).toBe(true);
 +                expect(fs.readFileSync(path.join(android_project_path, 'assets', 'www', 'merge.js'),'utf-8')).toEqual('alert("sup");');
 +            });
 +        });
 +
 +        describe('update_project method', function() {
 +            it('should invoke update_www', function() {
 +                var spyWww = spyOn(parser, 'update_www');
 +                parser.update_project(config);
 +                expect(spyWww).toHaveBeenCalled();
 +            });
 +            it('should invoke update_from_config', function() {
 +                var spyConfig = spyOn(parser, 'update_from_config');
 +                parser.update_project(config);
 +                expect(spyConfig).toHaveBeenCalled();
 +            });
 +            it('should call out to util.deleteSvnFolders', function() {
 +                var spy = spyOn(util, 'deleteSvnFolders');
 +                parser.update_project(config);
 +                expect(spy).toHaveBeenCalled();
 +            });
 +        });
 +    });
 +});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/spec/platform-script/blackberry/blackberry_parser.spec.js
----------------------------------------------------------------------
diff --cc spec/platform-script/blackberry/blackberry_parser.spec.js
index d183f7f,0000000..0ed2f2c
mode 100644,000000..100644
--- a/spec/platform-script/blackberry/blackberry_parser.spec.js
+++ b/spec/platform-script/blackberry/blackberry_parser.spec.js
@@@ -1,249 -1,0 +1,249 @@@
 +
 +/**
 +    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 blackberry_parser = require('../../../src/metadata/blackberry_parser'),
 +    config_parser = require('../../../src/config_parser'),
 +    path = require('path'),
 +    util = require('../../../src/util'),
 +    et = require('elementtree'),
 +    shell = require('shelljs'),
 +    cordova = require('../../../cordova'),
 +    fs = require('fs'),
 +    projects_path = path.join(__dirname, '..', '..', 'fixtures', 'projects'),
 +    blackberry_path = path.join(projects_path, 'native', 'blackberry_fixture'),
 +    project_path = path.join(projects_path, 'cordova'),
 +    blackberry_project_path = path.join(project_path, 'platforms', 'blackberry');
 +
- var www_config = path.join(project_path, 'www', 'config.xml');
++var www_config = util.projectConfig(project_path);
 +var original_www_config = fs.readFileSync(www_config, 'utf-8');
 +
 +describe('blackberry project parser', function() {
 +    beforeEach(function() {
 +        spyOn(process.stdout, 'write'); // silence console output
 +    });
 +
 +    it('should throw an exception with a path that is not a native blackberry project', function() {
 +        expect(function() {
 +            var project = new blackberry_parser(process.cwd());
 +        }).toThrow();
 +    });
 +    it('should accept a proper native blackberry project path as construction parameter', function() {
 +        var project;
 +        expect(function() {
 +            project = new blackberry_parser(blackberry_path);
 +        }).not.toThrow();
 +        expect(project).toBeDefined();
 +    });
 +
 +    describe('update_from_config method', function() {
 +        var project, config;
 +
 +        var blackberry_config = path.join(blackberry_path, 'www', 'config.xml');
 +        var original_blackberry_config = fs.readFileSync(blackberry_config, 'utf-8');
 +
 +        beforeEach(function() {
 +            project = new blackberry_parser(blackberry_path);
 +            config = new config_parser(www_config);
 +        });
 +        afterEach(function() {
 +            fs.writeFileSync(blackberry_config, original_blackberry_config, 'utf-8');
 +            fs.writeFileSync(www_config, original_www_config, 'utf-8');
 +        });
 +        it('should throw an exception if a non config_parser object is passed into it', function() {
 +            expect(function() {
 +                project.update_from_config({});
 +            }).toThrow();
 +        });
 +        it('should update the application name properly', function() {
 +            config.name('bond. james bond.');
 +            project.update_from_config(config);
 +
 +            var bb_cfg = new config_parser(blackberry_config);
 +
 +            expect(bb_cfg.name()).toBe('bond. james bond.');
 +        });
 +        it('should update the application package name properly', function() {
 +            config.packageName('sofa.king.awesome');
 +            project.update_from_config(config);
 +
 +            var bb_cfg = new config_parser(blackberry_config);
 +            expect(bb_cfg.packageName()).toBe('sofa.king.awesome');
 +        });
 +        describe('whitelist', function() {
 +            it('should update the whitelist when using access elements with origin attribute', function() {
 +                config.access.remove('*');
 +                config.access.add('http://blackberry.com');
 +                config.access.add('http://rim.com');
 +                project.update_from_config(config);
 +
 +                var bb_cfg = new et.ElementTree(et.XML(fs.readFileSync(blackberry_config, 'utf-8')));
 +                var as = bb_cfg.getroot().findall('access');
 +                expect(as.length).toEqual(2);
 +                expect(as[0].attrib.uri).toEqual('http://blackberry.com');
 +                expect(as[1].attrib.uri).toEqual('http://rim.com');
 +            });
 +            it('should update the whitelist when using access elements with uri attributes', function() {
 +                fs.writeFileSync(www_config, fs.readFileSync(www_config, 'utf-8').replace(/origin="\*/,'uri="http://rim.com'), 'utf-8');
 +                config = new config_parser(www_config);
 +                project.update_from_config(config);
 +
 +                var bb_cfg = new et.ElementTree(et.XML(fs.readFileSync(blackberry_config, 'utf-8')));
 +                var as = bb_cfg.getroot().findall('access');
 +                expect(as.length).toEqual(1);
 +                expect(as[0].attrib.uri).toEqual('http://rim.com');
 +            });
 +        });
 +    });
 +
 +    describe('cross-platform project level methods', function() {
 +        var parser, config;
 +
 +        var blackberry_config = path.join(blackberry_project_path, 'www', 'config.xml');
 +        var original_blackberry_config = fs.readFileSync(blackberry_config, 'utf-8');
 +
 +        beforeEach(function() {
 +            parser = new blackberry_parser(blackberry_project_path);
 +            config = new config_parser(www_config);
 +        });
 +        afterEach(function() {
 +            fs.writeFileSync(blackberry_config, original_blackberry_config, 'utf-8');
 +            fs.writeFileSync(www_config, original_www_config, 'utf-8');
 +        });
 +
 +        describe('update_www method', function() {
 +            it('should update all www assets', function() {
-                 var newFile = path.join(project_path, 'www', 'somescript.js');
++                var newFile = path.join(util.projectWww(project_path), 'somescript.js');
 +                this.after(function() {
 +                    shell.rm('-f', newFile);
 +                });
 +                fs.writeFileSync(newFile, 'alert("sup");', 'utf-8');
 +                parser.update_www();
 +                expect(fs.existsSync(path.join(blackberry_project_path, 'www', 'somescript.js'))).toBe(true);
 +            });
 +            it('should not overwrite the blackberry-specific config.xml', function() {
-                 var www_cfg = fs.readFileSync(path.join(project_path, 'www', 'config.xml'), 'utf-8');
++                var www_cfg = fs.readFileSync(util.projectConfig(project_path), 'utf-8');
 +                parser.update_www();
 +                var bb_cfg = fs.readFileSync(blackberry_config, 'utf-8');
 +                expect(bb_cfg).not.toBe(www_cfg);
 +            });
 +        });
 +
 +        describe('update_overrides method',function() {
-             var mergesPath = path.join(project_path, 'merges', 'blackberry');
++            var mergesPath = path.join(util.appDir(project_path), 'merges', 'blackberry');
 +            var newFile = path.join(mergesPath, 'merge.js');
 +            beforeEach(function() {
 +                shell.mkdir('-p', mergesPath);
 +                fs.writeFileSync(newFile, 'alert("sup");', 'utf-8');
 +            });
 +            afterEach(function() {
 +                shell.rm('-rf', mergesPath);
 +            });
 +
 +            it('should copy a new file from merges into www', function() {
 +                parser.update_overrides();
 +                expect(fs.existsSync(path.join(blackberry_project_path, 'www', 'merge.js'))).toBe(true);
 +            });
 +
 +            it('should copy a file from merges over a file in www', function() {
-                 var newFileWWW = path.join(project_path, 'www','merge.js');
++                var newFileWWW = path.join(util.projectWww(project_path), 'merge.js');
 +                fs.writeFileSync(newFileWWW, 'var foo=1;', 'utf-8');
 +                this.after(function() {
 +                    shell.rm('-rf', newFileWWW);
 +                });
 +                parser.update_overrides();
 +                expect(fs.existsSync(path.join(blackberry_project_path, 'www', 'merge.js'))).toBe(true);
 +                expect(fs.readFileSync(path.join(blackberry_project_path, 'www', 'merge.js'),'utf-8')).toEqual('alert("sup");');
 +            });
 +        });
 +
 +        describe('update_project method', function() {
 +            var cordova_config_path = path.join(project_path, '.cordova', 'config.json');
 +            var original_config_json = fs.readFileSync(cordova_config_path, 'utf-8');
 +
 +            describe('with stubbed out config for BlackBerry SDKs', function() {
 +                beforeEach(function() {
 +                    fs.writeFileSync(cordova_config_path, JSON.stringify({
 +                        blackberry:{
 +                            qnx:{
 +                            }
 +                        }
 +                    }), 'utf-8');
 +                });
 +                afterEach(function() {
 +                    fs.writeFileSync(cordova_config_path, original_config_json, 'utf-8');
 +                });
 +                it('should invoke update_www', function() {
 +                    var spyWww = spyOn(parser, 'update_www');
 +                    parser.update_project(config);
 +                    expect(spyWww).toHaveBeenCalled();
 +                });
 +                it('should invoke update_from_config', function() {
 +                    var spyConfig = spyOn(parser, 'update_from_config');
 +                    parser.update_project(config);
 +                    expect(spyConfig).toHaveBeenCalled();
 +                });
 +                it('should not invoke get_blackberry_environment', function() {
 +                    var spyEnv = spyOn(parser, 'get_blackberry_environment');
 +                    parser.update_project(config);
 +                    expect(spyEnv).not.toHaveBeenCalled();
 +                });
 +                it('should write out project properties', function(done) {
 +                    var spyProps = spyOn(parser, 'write_project_properties');
 +                    parser.update_project(config, function() { 
 +                        expect(spyProps).toHaveBeenCalled();
 +                        done();
 +                    });
 +                });
 +                it('should call out to util.deleteSvnFolders', function(done) {
 +                    var spy = spyOn(util, 'deleteSvnFolders');
 +                    parser.update_project(config, function() {
 +                        expect(spy).toHaveBeenCalled();
 +                        done();
 +                    });
 +                });
 +            });
 +            describe('with empty BlackBerry SDKs in config', function() {
 +                afterEach(function() {
 +                    fs.writeFileSync(cordova_config_path, original_config_json, 'utf-8');
 +                });
 +                it('should invoke get_blackberry_environment', function() {
 +                    var spyEnv = spyOn(parser, 'get_blackberry_environment');
 +                    var promptSpy = spyOn(require('prompt'), 'get');
 +                    parser.update_project(config);
 +                    expect(spyEnv).toHaveBeenCalled();
 +                });
 +                it('should write out project properties', function(done) {
 +                    var spyProps = spyOn(parser, 'write_project_properties');
 +                    var promptSpy = spyOn(require('prompt'), 'get');
 +                    parser.update_project(config, function() {
 +                        expect(spyProps).toHaveBeenCalled();
 +                        done();
 +                    });
 +                    promptSpy.mostRecentCall.args[1](null, {});
 +                });
 +            });
 +        });
 +    });
 +
 +    describe('write_project_properties method', function() {
 +    });
 +
 +    describe('get_blackberry_environment method', function() {
 +    });
 +});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/spec/platform-script/ios/ios_parser.spec.js
----------------------------------------------------------------------
diff --cc spec/platform-script/ios/ios_parser.spec.js
index b0c2aeb,0000000..9112929
mode 100644,000000..100644
--- a/spec/platform-script/ios/ios_parser.spec.js
+++ b/spec/platform-script/ios/ios_parser.spec.js
@@@ -1,219 -1,0 +1,219 @@@
 +/**
 + 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 ios_parser = require('../../../src/metadata/ios_parser'),
 +    config_parser = require('../../../src/config_parser'),
 +    cordova = require('../../../cordova'),
 +    util = require('../../../src/util'),
 +    path = require('path'),
 +    shell = require('shelljs'),
 +    fs = require('fs'),
 +    os = require('os'),
 +    et = require('elementtree'),
 +    projects_path = path.join(__dirname, '..', '..', 'fixtures', 'projects')
 +    ios_path = path.join(projects_path, 'native', 'ios_fixture'),
 +    project_path = path.join(projects_path, 'cordova'),
 +    ios_project_path = path.join(project_path, 'platforms', 'ios');
 +
- var www_config = path.join(project_path, 'www', 'config.xml');
++var www_config = util.projectConfig(project_path);
 +var original_www_config = fs.readFileSync(www_config, 'utf-8');
 +
 +describe('ios project parser', function () {
 +    it('should throw an exception with a path that is not a native ios project', function () {
 +        expect(function () {
 +            var project = new ios_parser(process.cwd());
 +        }).toThrow();
 +    });
 +    it('should accept a proper native ios project path as construction parameter', function () {
 +        var project;
 +        expect(function () {
 +            project = new ios_parser(ios_path);
 +        }).not.toThrow();
 +        expect(project).toBeDefined();
 +    });
 +
 +    describe('update_from_config method', function () {
 +        var project, config;
 +
 +        var ios_plist = path.join(ios_path, 'cordovaExample', 'cordovaExample-Info.plist'),
 +            ios_pbx = path.join(ios_path, 'cordovaExample.xcodeproj', 'project.pbxproj'),
 +            ios_config_xml = path.join(ios_path, 'cordovaExample', 'config.xml');
 +
 +        var original_pbx = fs.readFileSync(ios_pbx, 'utf-8');
 +        var original_plist = fs.readFileSync(ios_plist, 'utf-8');
 +        var original_ios_config = fs.readFileSync(ios_config_xml, 'utf-8');
 +
 +        beforeEach(function () {
 +            project = new ios_parser(ios_path);
 +            config = new config_parser(www_config);
 +        });
 +        afterEach(function () {
 +            fs.writeFileSync(ios_pbx, original_pbx, 'utf-8');
 +            fs.writeFileSync(ios_config_xml, original_ios_config, 'utf-8');
 +            fs.writeFileSync(ios_plist, original_plist, 'utf-8');
 +            fs.writeFileSync(www_config, original_www_config, 'utf-8');
 +        });
 +        it('should throw an exception if a non config_parser object is passed into it', function () {
 +            expect(function () {
 +                project.update_from_config({});
 +            }).toThrow();
 +        });
 +        it('should update the application name properly', function (done) {
 +            config.name('bond. james bond.');
 +            project.update_from_config(config, function () {
 +                var pbx_contents = fs.readFileSync(ios_pbx, 'utf-8');
 +                expect(pbx_contents.match(/PRODUCT_NAME\s*=\s*"bond. james bond."/)[0]).toBe('PRODUCT_NAME = "bond. james bond."');
 +                done();
 +            });
 +        });
 +        it('should update the application package name (bundle identifier) properly', function (done) {
 +            config.packageName('ca.filmaj.dewd');
 +            project.update_from_config(config, function () {
 +                var plist_contents = fs.readFileSync(ios_plist, 'utf-8');
 +                expect(plist_contents).toMatch(/<string>ca.filmaj.dewd/);
 +                done();
 +            });
 +        });
 +        it('should update the whitelist in the project config.xml', function (done) {
 +            project.update_from_config(config, function () {
 +                var config_contents = fs.readFileSync(ios_config_xml, 'utf-8');
 +                expect(config_contents).toMatch(/<access origin="\*" \/>/);
 +                done();
 +            });
 +        });
 +        describe('preferences', function () {
 +            it('should not change default project preferences and copy over additional project preferences to platform-level config.xml', function (done) {
 +                config.preference.add({name:'henrik', value:'sedin'});
 +                project.update_from_config(config, function () {
 +                    var native_config = new et.ElementTree(et.XML(fs.readFileSync(ios_config_xml, 'utf-8')));
 +                    var ps = native_config.findall('preference');
 +                    expect(ps.length).toEqual(17);
 +                    expect(ps[0].attrib.name).toEqual('KeyboardDisplayRequiresUserAction');
 +                    expect(ps[0].attrib.value).toEqual('true');
 +                    expect(ps[16].attrib.name).toEqual('henrik');
 +                    expect(ps[16].attrib.value).toEqual('sedin');
 +                    done();
 +                });
 +            });
 +            it('should override a default project preference if applicable', function (done) {
 +                config.preference.add({name:'UIWebViewBounce', value:'false'});
 +                project.update_from_config(config, function () {
 +                    var native_config = new et.ElementTree(et.XML(fs.readFileSync(ios_config_xml, 'utf-8')));
 +                    var ps = native_config.findall('preference');
 +                    expect(ps.length).toEqual(16);
 +                    expect(ps[2].attrib.name).toEqual('UIWebViewBounce');
 +                    expect(ps[2].attrib.value).toEqual('false');
 +                    done();
 +                });
 +            });
 +        });
 +    });
 +
 +    describe('cross-platform project level methods', function () {
 +        var parser, config;
 +        var ios_plist = path.join(ios_project_path, 'cordovaExample', 'cordovaExample-Info.plist'),
 +            ios_pbx = path.join(ios_project_path, 'cordovaExample.xcodeproj', 'project.pbxproj'),
 +            ios_config_xml = path.join(ios_project_path, 'cordovaExample', 'config.xml');
 +
 +        var original_pbx = fs.readFileSync(ios_pbx, 'utf-8');
 +        var original_plist = fs.readFileSync(ios_plist, 'utf-8');
 +        var original_ios_config = fs.readFileSync(ios_config_xml, 'utf-8');
 +
 +        beforeEach(function () {
 +            parser = new ios_parser(ios_project_path);
 +            config = new config_parser(www_config);
 +        });
 +        afterEach(function () {
 +            fs.writeFileSync(ios_pbx, original_pbx, 'utf-8');
 +            fs.writeFileSync(ios_config_xml, original_ios_config, 'utf-8');
 +            fs.writeFileSync(ios_plist, original_plist, 'utf-8');
 +            fs.writeFileSync(www_config, original_www_config, 'utf-8');
 +        });
 +
 +        describe('update_www method', function () {
 +            it('should update all www assets', function () {
-                 var newFile = path.join(project_path, 'www', 'somescript.js');
++                var newFile = path.join(util.projectWww(project_path), 'somescript.js');
 +                this.after(function () {
 +                    shell.rm('-f', newFile);
 +                });
 +                fs.writeFileSync(newFile, 'alert("sup");', 'utf-8');
 +                parser.update_www();
 +                expect(fs.existsSync(path.join(ios_project_path, 'www', 'somescript.js'))).toBe(true);
 +            });
 +            it('should write out ios js to cordova.js', function () {
 +                parser.update_www();
 +                expect(fs.readFileSync(path.join(ios_project_path, 'www', 'cordova.js'), 'utf-8')).toBe(fs.readFileSync(path.join(util.libDirectory, 'cordova-ios', 'CordovaLib', 'cordova.ios.js'), 'utf-8'));
 +            });
 +        });
 +
 +        describe('update_overrides method', function () {
-             var mergesPath = path.join(project_path, 'merges', 'ios');
++            var mergesPath = path.join(util.appDir(project_path), 'merges', 'ios');
 +            var newFile = path.join(mergesPath, 'merge.js');
 +            beforeEach(function() {
 +                shell.mkdir('-p', mergesPath);
 +                fs.writeFileSync(newFile, 'alert("sup");', 'utf-8');
 +            });
 +            afterEach(function() {
 +                shell.rm('-rf', mergesPath);
 +            });
 +
 +            it('should copy a new file from merges into www', function () {
 +                parser.update_overrides();
 +                expect(fs.existsSync(path.join(ios_project_path, 'www', 'merge.js'))).toBe(true);
 +            });
 +
 +            it('should copy a file from merges over a file in www', function () {
-                 var newFileWWW = path.join(project_path, 'www', 'merge.js');
++                var newFileWWW = path.join(util.projectWww(project_path), 'merge.js');
 +                fs.writeFileSync(newFileWWW, 'var foo=1;', 'utf-8');
 +                this.after(function () {
 +                    shell.rm('-rf', newFileWWW);
 +                });
 +
 +                parser.update_overrides();
 +                expect(fs.existsSync(path.join(ios_project_path, 'www', 'merge.js'))).toBe(true);
 +                expect(fs.readFileSync(path.join(ios_project_path, 'www', 'merge.js'), 'utf-8')).toEqual('alert("sup");');
 +            });
 +        });
 +
 +        describe('update_project method', function () {
 +            it('should invoke update_www', function (done) {
 +                var spyWww = spyOn(parser, 'update_www');
 +                parser.update_project(config, function () {
 +                    expect(spyWww).toHaveBeenCalled();
 +                    done();
 +                });
 +            });
 +            it('should invoke update_from_config', function (done) {
 +                var spyConfig = spyOn(parser, 'update_from_config').andCallThrough();
 +                parser.update_project(config, function () {
 +                    expect(spyConfig).toHaveBeenCalled();
 +                    done();
 +                });
 +            });
 +            it('should call out to util.deleteSvnFolders', function(done) {
 +                var spy = spyOn(util, 'deleteSvnFolders');
 +                var spyConfig = spyOn(parser, 'update_from_config').andCallThrough();
 +                parser.update_project(config, function () {
 +                    expect(spy).toHaveBeenCalled();
 +                    done();
 +                });
 +            });
 +        });
 +    });
 +});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/src/compile.js
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/src/emulate.js
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/src/metadata/android_parser.js
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/src/metadata/blackberry_parser.js
----------------------------------------------------------------------
diff --cc src/metadata/blackberry_parser.js
index 3d8b578,97cdd59..7ad69ba
--- a/src/metadata/blackberry_parser.js
+++ b/src/metadata/blackberry_parser.js
@@@ -89,30 -90,42 +90,38 @@@ module.exports.prototype = 
          return path.join(this.path, 'www');
      },
  
+     staging_dir: function() {
+         return path.join(this.path, '.staging', 'www');
+     },
+ 
+     config_xml:function(){
+         return this.config_path;
+     },
+ 
      update_www:function() {
          var projectRoot = util.isCordova(this.path);
-         var www = path.join(projectRoot, 'www');
+         var www = util.projectWww(projectRoot);
          var platformWww = this.www_dir();
  
 -        var finalWww = path.join(this.path, 'finalwww');
 -        shell.mkdir('-p', finalWww);
 -
 -        // replace stock bb app contents with app contents. 
 -        // to keep:
 -        //        - config.xml
 -        //        - cordova.js
 -        //        - ext*
 -        //        - plugins.xml
 -        //        - res
 -        shell.cp('-f', path.join(platformWww, 'config.xml'), finalWww);
 -        shell.cp('-f', path.join(platformWww, 'cordova-*.js'), finalWww);
 -        shell.cp('-f', path.join(platformWww, 'plugins.xml'), finalWww);
 -        shell.cp('-rf', path.join(platformWww, 'ext*'), finalWww);
 -        shell.cp('-rf', path.join(platformWww, 'res'), finalWww);
 -
 -        // Copy everything over from platform-agnostic www.
 -        shell.cp('-rf', path.join(www, '*'), finalWww);
 -
 -        // Delete the old platform www, and move the final project over
 -        shell.rm('-rf', platformWww);
 -        shell.mv(finalWww, platformWww);
 +        // remove the stock www folder
 +        shell.rm('-rf', this.www_dir());
  
 +        // copy over project www assets
 +        shell.cp('-rf', www, this.path);
 +
 +        // add cordova.js
 +        shell.cp('-f', path.join(util.libDirectory, 'cordova-blackberry', 'javascript', 'cordova.blackberry.js'), path.join(this.www_dir(), 'cordova.js'));
 +
 +        // add webworks ext directories
 +        shell.cp('-rf', path.join(util.libDirectory, 'cordova-blackberry', 'framework', 'ext*'), this.www_dir());
 +
 +        // add config.xml
 +        // @TODO should use project www/config.xml but it must use BBWP elements
 +        shell.cp('-f', path.join(util.libDirectory, 'cordova-blackberry', 'bin', 'templates', 'project', 'www', 'config.xml'), this.www_dir());
 +
 +        // add res/
 +        // @TODO remove this when config.xml is generalized
 +        shell.cp('-rf', path.join(util.libDirectory, 'cordova-blackberry', 'bin', 'templates', 'project', 'www', 'res'), this.www_dir());
      },
  
      // update the overrides folder into the www folder

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/src/platform.js
----------------------------------------------------------------------
diff --cc src/platform.js
index 15285fb,1e51c6c..c215a71
--- a/src/platform.js
+++ b/src/platform.js
@@@ -26,8 -26,7 +26,9 @@@ var config_parser     = require('./conf
      android_parser    = require('./metadata/android_parser'),
      ios_parser        = require('./metadata/ios_parser'),
      blackberry_parser = require('./metadata/blackberry_parser'),
 +    wp7_parser      = require('./metadata/wp7_parser'),
 +    wp8_parser      = require('./metadata/wp8_parser'),
+     plugman           = require('plugman'),
      shell             = require('shelljs');
  
  var parsers = {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/src/plugin.js
----------------------------------------------------------------------
diff --cc src/plugin.js
index b6d9911,706cc82..c275a55
--- a/src/plugin.js
+++ b/src/plugin.js
@@@ -47,16 -50,12 +50,11 @@@ module.exports = function plugin(comman
      var platforms = cordova_util.listPlatforms(projectRoot);
  
      // Massage plugin name(s) / path(s)
-     var pluginPath, plugins, names = [];
+     var pluginPath, plugins;
      pluginPath = path.join(projectRoot, 'plugins');
 -    plugins = ls(pluginPath);
 -
 +    plugins = cordova_util.findPlugins(pluginPath);
-     if (targets) { 
-         if (!(targets instanceof Array)) targets = [targets];
-         targets.forEach(function(target) {
-             var targetName = target.substr(target.lastIndexOf('/') + 1);
-             if (targetName[targetName.length-1] == '/') targetName = targetName.substr(0, targetName.length-1);
-             names.push(targetName);
-         });
+     if (targets && !(targets instanceof Array)) {
+         targets = [targets];
      }
  
      switch(command) {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a79365d8/templates/app/config.xml
----------------------------------------------------------------------
diff --cc templates/app/config.xml
index 0000000,206bc56..aca4d5e
mode 000000,100644..100644
--- a/templates/app/config.xml
+++ b/templates/app/config.xml
@@@ -1,0 -1,49 +1,49 @@@
+ <?xml version="1.0" encoding="UTF-8"?>
+ <widget xmlns     = "http://www.w3.org/ns/widgets"
 -        xmlns:gap = "http://phonegap.com/ns/1.0"
++        xmlns:cdv = "http://cordova.apache.org/ns/1.0"
+         id        = "io.cordova.hello-cordova"
+         version   = "2.0.0">
+     <name>Hello Cordova</name>
+ 
+     <description>
+         A sample Apache Cordova application that responds to the deviceready event.
+     </description>
+ 
+     <author href="http://cordova.io" email="callback-dev@incubator.apache.org">
+         Apache Cordova Team
+     </author>
+ 
+     <icon src="res/icon/cordova_512.png"        width="512" height="512" />
 -    <icon src="res/icon/cordova_android_96.png" width="96"  height="96"  gap:platform="android" />
 -    <icon src="res/icon/cordova_bb_80.png"      width="80"  height="80"  gap:platform="blackberry" />
 -    <icon src="res/icon/cordova_ios_144.png"    width="144" height="144" gap:platform="ios" />
++    <icon src="res/icon/cordova_android_96.png" width="96"  height="96"  cdv:platform="android" />
++    <icon src="res/icon/cordova_bb_80.png"      width="80"  height="80"  cdv:platform="blackberry" />
++    <icon src="res/icon/cordova_ios_144.png"    width="144" height="144" cdv:platform="ios" />
+ 
 -    <gap:splash src="res/screen/android_hdpi_landscape.png"      width="800"  height="480"  gap:platform="android" />
 -    <gap:splash src="res/screen/android_hdpi_portrait.png"       width="480"  height="800"  gap:platform="android" />
 -    <gap:splash src="res/screen/android_ldpi_landscape.png"      width="320"  height="200"  gap:platform="android" />
 -    <gap:splash src="res/screen/android_ldpi_portrait.png"       width="200"  height="320"  gap:platform="android" />
 -    <gap:splash src="res/screen/android_mdpi_landscape.png"      width="480"  height="320"  gap:platform="android" />
 -    <gap:splash src="res/screen/android_mdpi_portrait.png"       width="320"  height="480"  gap:platform="android" />
 -    <gap:splash src="res/screen/android_xhdpi_landscape.png"     width="1280" height="720"  gap:platform="android" />
 -    <gap:splash src="res/screen/android_xhdpi_portrait.png"      width="720"  height="1280" gap:platform="android" />
 -    <gap:splash src="res/screen/blackberry_transparent_300.png"  width="300"  height="300"  gap:platform="blackberry" />
 -    <gap:splash src="res/screen/blackberry_transparent_400.png"  width="200"  height="200"  gap:platform="blackberry" />
 -    <gap:splash src="res/screen/ipad_landscape.png"              width="1024" height="748"  gap:platform="ios" />
 -    <gap:splash src="res/screen/ipad_portrait.png"               width="768"  height="1004" gap:platform="ios" />
 -    <gap:splash src="res/screen/ipad_retina_landscape.png"       width="2048" height="1496" gap:platform="ios" />
 -    <gap:splash src="res/screen/ipad_retina_portrait.png"        width="1536" height="2008" gap:platform="ios" />
 -    <gap:splash src="res/screen/iphone_landscape.png"            width="480"  height="320"  gap:platform="ios" />
 -    <gap:splash src="res/screen/iphone_portrait.png"             width="320"  height="480"  gap:platform="ios" />
 -    <gap:splash src="res/screen/iphone_retina_landscape.png"     width="960"  height="640"  gap:platform="ios" />
 -    <gap:splash src="res/screen/iphone_retina_portrait.png"      width="640"  height="960"  gap:platform="ios" />
 -    <gap:splash src="res/screen/windows_phone_portrait.jpg"      width="480"  height="800"  gap:platform="winphone" />
++    <cdv:splash src="res/screen/android_hdpi_landscape.png"      width="800"  height="480"  cdv:platform="android" />
++    <cdv:splash src="res/screen/android_hdpi_portrait.png"       width="480"  height="800"  cdv:platform="android" />
++    <cdv:splash src="res/screen/android_ldpi_landscape.png"      width="320"  height="200"  cdv:platform="android" />
++    <cdv:splash src="res/screen/android_ldpi_portrait.png"       width="200"  height="320"  cdv:platform="android" />
++    <cdv:splash src="res/screen/android_mdpi_landscape.png"      width="480"  height="320"  cdv:platform="android" />
++    <cdv:splash src="res/screen/android_mdpi_portrait.png"       width="320"  height="480"  cdv:platform="android" />
++    <cdv:splash src="res/screen/android_xhdpi_landscape.png"     width="1280" height="720"  cdv:platform="android" />
++    <cdv:splash src="res/screen/android_xhdpi_portrait.png"      width="720"  height="1280" cdv:platform="android" />
++    <cdv:splash src="res/screen/blackberry_transparent_300.png"  width="300"  height="300"  cdv:platform="blackberry" />
++    <cdv:splash src="res/screen/blackberry_transparent_400.png"  width="200"  height="200"  cdv:platform="blackberry" />
++    <cdv:splash src="res/screen/ipad_landscape.png"              width="1024" height="748"  cdv:platform="ios" />
++    <cdv:splash src="res/screen/ipad_portrait.png"               width="768"  height="1004" cdv:platform="ios" />
++    <cdv:splash src="res/screen/ipad_retina_landscape.png"       width="2048" height="1496" cdv:platform="ios" />
++    <cdv:splash src="res/screen/ipad_retina_portrait.png"        width="1536" height="2008" cdv:platform="ios" />
++    <cdv:splash src="res/screen/iphone_landscape.png"            width="480"  height="320"  cdv:platform="ios" />
++    <cdv:splash src="res/screen/iphone_portrait.png"             width="320"  height="480"  cdv:platform="ios" />
++    <cdv:splash src="res/screen/iphone_retina_landscape.png"     width="960"  height="640"  cdv:platform="ios" />
++    <cdv:splash src="res/screen/iphone_retina_portrait.png"      width="640"  height="960"  cdv:platform="ios" />
++    <cdv:splash src="res/screen/windows_phone_portrait.jpg"      width="480"  height="800"  cdv:platform="winphone" />
+ 
+     <feature name="http://api.phonegap.com/1.0/device" />
+ 
+     <preference name="phonegap-version" value="1.9.0" />
+     <preference name="orientation"      value="default" />
+     <preference name="target-device"    value="universal" />
+     <preference name="fullscreen"       value="false" />
+ 
+     <access origin="*" />
+ </widget>