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:57:58 UTC

[01/50] Reorganize specs into cordova-cli/ and platform-script/

Updated Branches:
  refs/heads/master2 [created] f7965d15a


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/platform.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform.spec.js b/spec/platform.spec.js
deleted file mode 100644
index 7138c7e..0000000
--- a/spec/platform.spec.js
+++ /dev/null
@@ -1,339 +0,0 @@
-/**
-    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'),
-    ios_parser = require('../src/metadata/ios_parser'),
-    cordova_project = path.join(__dirname, 'fixtures', 'projects', 'cordova'),
-    blackberry_parser = require('../src/metadata/blackberry_parser');
-
-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() {
-            expect(cordova.platform('list').length).toEqual(3);
-        });
-    });
-
-    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(path.join(cordova_project, 'www', 'config.xml'), 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(path.join(cordova_project, 'www', 'config.xml'), 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 irc = spyOn(ios_parser, 'check_requirements');
-            var sh = spyOn(shell, 'exec');
-            cordova.platform('add', ['android', 'ios']);
-            arc.mostRecentCall.args[0](false);
-            irc.mostRecentCall.args[0](false);
-            expect(sh.argsForCall[0][0]).toMatch(/android\/bin\/create/);
-            expect(sh.argsForCall[1][0]).toMatch(/ios\/bin\/create/);
-        });
-    });
-
-    describe('`remove`',function() { 
-        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(2);
-        });
-        it('should be able to remove multiple platforms', function() {
-            cordova.platform('remove', ['android','ios']);
-            expect(cordova.platform('ls').length).toEqual(1);
-        });
-    });
-
-    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/693521da/spec/plugin.spec.js
----------------------------------------------------------------------
diff --git a/spec/plugin.spec.js b/spec/plugin.spec.js
deleted file mode 100644
index 3d370e7..0000000
--- a/spec/plugin.spec.js
+++ /dev/null
@@ -1,164 +0,0 @@
-/**
-    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.');
-                });
-            });
-            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');
-            });
-        });
-    });
-});
-

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/plugin_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/plugin_parser.spec.js b/spec/plugin_parser.spec.js
deleted file mode 100644
index 050ee8d..0000000
--- a/spec/plugin_parser.spec.js
+++ /dev/null
@@ -1,42 +0,0 @@
-
-/**
-    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'),
-    fs = require('fs'),
-    plugin_parser = require('../src/plugin_parser'),
-    et = require('elementtree'),
-    xml = path.join(__dirname, 'fixtures', 'plugins', 'test', 'plugin.xml');
-
-describe('plugin.xml parser', function () {
-    it('should read a proper plugin.xml file', function() {
-        var cfg;
-        expect(function () {
-            cfg = new plugin_parser(xml);
-        }).not.toThrow();
-        expect(cfg).toBeDefined();
-        expect(cfg.doc).toBeDefined();
-    });
-    it('should be able to figure out which platforms the plugin supports', function() {
-        var cfg = new plugin_parser(xml);
-        expect(cfg.platforms.length).toBe(1);
-        expect(cfg.platforms.indexOf('ios') > -1).toBe(true);
-    });
-});
-

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/prepare.spec.js
----------------------------------------------------------------------
diff --git a/spec/prepare.spec.js b/spec/prepare.spec.js
deleted file mode 100644
index f232b3e..0000000
--- a/spec/prepare.spec.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/**
-    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'),
-    et = require('elementtree'),
-    shell = require('shelljs'),
-    path = require('path'),
-    fs = require('fs'),
-    config_parser = require('../src/config_parser'),
-    android_parser = require('../src/metadata/android_parser'),
-    ios_parser = require('../src/metadata/ios_parser'),
-    blackberry_parser = require('../src/metadata/blackberry_parser'),
-    hooker = require('../src/hooker'),
-    fixtures = path.join(__dirname, 'fixtures'),
-    hooks = path.join(fixtures, 'hooks'),
-    tempDir = path.join(__dirname, '..', 'temp'),
-    cordova_project = path.join(fixtures, 'projects', 'cordova');
-
-var cwd = process.cwd();
-
-describe('prepare command', function() {
-    beforeEach(function() {
-        shell.rm('-rf', tempDir);
-        shell.mkdir('-p', tempDir);
-    });
-
-    it('should not run inside a Cordova-based project with no added platforms', function() {
-        this.after(function() {
-            process.chdir(cwd);
-        });
-
-        cordova.create(tempDir);
-        process.chdir(tempDir);
-        expect(function() {
-            cordova.prepare();
-        }).toThrow();
-    });
-    
-    it('should run inside a Cordova-based project with at least one added platform', function() {
-        // move platform project fixtures over to fake cordova into thinking platforms were added
-        // TODO: possibly add this to helper?
-        shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
-        this.after(function() {
-            process.chdir(cwd);
-            shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
-        });
-
-        process.chdir(cordova_project);
-
-        var a_parser_spy = spyOn(android_parser.prototype, 'update_project');
-        var i_parser_spy = spyOn(ios_parser.prototype, 'update_project');
-        expect(function() {
-            cordova.prepare();
-            expect(a_parser_spy).toHaveBeenCalled();
-            expect(i_parser_spy).toHaveBeenCalled();
-        }).not.toThrow();
-    });
-    it('should not run outside of a Cordova-based project', function() {
-        this.after(function() {
-            process.chdir(cwd);
-        });
-
-        shell.mkdir('-p', tempDir);
-        process.chdir(tempDir);
-
-        expect(function() {
-            cordova.prepare();
-        }).toThrow();
-    });
-
-    describe('hooks', function() {
-        var s;
-        beforeEach(function() {
-            s = spyOn(hooker.prototype, 'fire').andReturn(true);
-        });
-
-        describe('when platforms are added', function() {
-            beforeEach(function() {
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
-                process.chdir(cordova_project);
-            });
-            afterEach(function() {
-                shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
-                shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
-                process.chdir(cwd);
-            });
-
-            it('should fire before hooks through the hooker module', function() {
-                cordova.prepare();
-                expect(s).toHaveBeenCalledWith('before_prepare');
-            });
-            it('should fire after hooks through the hooker module', function() {
-                var parser_spy = spyOn(android_parser.prototype, 'update_project');
-                cordova.prepare();
-                parser_spy.mostRecentCall.args[1](); // parser cb
-                expect(s).toHaveBeenCalledWith('after_prepare');
-            });
-        });
-
-        describe('with no platforms added', function() {
-            beforeEach(function() {
-                cordova.create(tempDir);
-                process.chdir(tempDir);
-            });
-            afterEach(function() {
-                process.chdir(cwd);
-            });
-            it('should not fire the hooker', function() {
-                expect(function() {
-                    cordova.prepare();
-                }).toThrow();
-                expect(s).not.toHaveBeenCalledWith('before_prepare');
-                expect(s).not.toHaveBeenCalledWith('after_prepare');
-            });
-        });
-    });
-});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/serve.spec.js
----------------------------------------------------------------------
diff --git a/spec/serve.spec.js b/spec/serve.spec.js
deleted file mode 100644
index 8d00cab..0000000
--- a/spec/serve.spec.js
+++ /dev/null
@@ -1,132 +0,0 @@
-
-/**
-    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');
-
-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);
-            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));
-    });
-});
-


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

Posted by br...@apache.org.
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>


[35/50] Add Windows support to Android platform-scripts.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/framework/build.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/build.xml b/lib/cordova-android/framework/build.xml
index 9e7b127..989dbb2 100644
--- a/lib/cordova-android/framework/build.xml
+++ b/lib/cordova-android/framework/build.xml
@@ -26,8 +26,7 @@
         </filterchain>
     </loadfile>
 
-    <!-- check that the version of ant is at least 1.8.0, as is needed
-         for the dblQuote property -->
+    <!-- check that the version of ant is at least 1.8.0 -->
     <antversion property="thisantversion" atleast="1.8.0" />
     <fail message="The required minimum version of ant is 1.8.0, you have ${ant.version}"
           unless="thisantversion" />
@@ -80,9 +79,6 @@
          -->
     <property file="ant.properties" />
 
-    <!-- We need to setup the double quote. -->
-    <property name="dblQuote">"</property>
-
     <!-- The project.properties file is created and updated by the 'android'
          tool, as well as ADT.
 
@@ -136,33 +132,10 @@
     -->
     <import file="${sdk.dir}/tools/ant/build.xml" />
 
-    <!-- Combine JavaScript files into one cordova-uncompressed.js file. -->
-    <target name="build-javascript" depends="clean">
-
-      <!-- Clean up existing files -->
-      <!--<delete file="assets/www/cordova_${version}.js"/>-->
-
-      <!-- Create uncompressed JS file -->
-      <concat destfile="assets/www/cordova-${version}.js">
-        <filelist dir="assets/js" files="cordova.android.js"/>
-      </concat>
-
-      <!-- update project files to reference cordova-x.x.x.min.js -->
-      <replaceregexp match="cordova(.*)\.js" replace="cordova-${version}.js" byline="true">
-         <fileset file="assets/www/index.html" />
-         <fileset file="../bin/templates/project/assets/www/index.html" />
-      </replaceregexp>
-
-      <!-- This is sketchy, but it works, ${dblQuote} does not -->
-      <replaceregexp match="cordovaVersion = [\u0022].*[\u0022];" replace='cordovaVersion = ${dblQuote}${version}${dblQuote};' byline="true">
-        <fileset file="src/org/apache/cordova/Device.java" />
-      </replaceregexp>
-    </target>
-
     <!-- Build Cordova jar file that includes all native code, and Cordova JS file
          that includes all JavaScript code.
     -->
-    <target name="jar" depends="build-javascript, -compile">
+    <target name="jar" depends="-compile">
       <jar jarfile="cordova-${version}.jar" basedir="bin/classes" excludes="org/apache/cordova/R.class,org/apache/cordova/R$*.class"/>
     </target>
 
@@ -195,10 +168,10 @@
         </junit>
     </target>
 
-    <target name="cordova_debug" depends="build-javascript, debug">
+    <target name="cordova_debug" depends="debug">
     </target>
 
-    <target name="cordova_release" depends="build-javascript, release">
+    <target name="cordova_release" depends="release">
     </target>
 
 </project>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/src/metadata/android_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/android_parser.js b/src/metadata/android_parser.js
index 3dfaf22..09b379f 100644
--- a/src/metadata/android_parser.js
+++ b/src/metadata/android_parser.js
@@ -136,7 +136,7 @@ module.exports.prototype = {
         shell.cp('-rf', www, platformWww);
 
         // write out android lib's cordova.js
-        var jsPath = path.join(util.libDirectory, 'cordova-android', 'framework', 'assets', 'js', 'cordova.android.js');
+        var jsPath = path.join(util.libDirectory, 'cordova-android', 'framework', 'assets', 'www', 'cordova.js');
         fs.writeFileSync(path.join(this.www_dir(), 'cordova.js'), fs.readFileSync(jsPath, 'utf-8'), 'utf-8');
 
     },


[36/50] Add Windows support to Android platform-scripts.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/framework/assets/www/cordova.js
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/assets/www/cordova.js b/lib/cordova-android/framework/assets/www/cordova.js
new file mode 100644
index 0000000..2941307
--- /dev/null
+++ b/lib/cordova-android/framework/assets/www/cordova.js
@@ -0,0 +1,6836 @@
+// Platform: android
+
+// commit d0ffb852378ff018bac2f3b12c38098a19b8ce00
+
+// File generated at :: Thu Apr 18 2013 15:10:54 GMT-0400 (EDT)
+
+/*
+ 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.
+*/
+
+;(function() {
+
+// file: lib/scripts/require.js
+
+var require,
+    define;
+
+(function () {
+    var modules = {};
+    // Stack of moduleIds currently being built.
+    var requireStack = [];
+    // Map of module ID -> index into requireStack of modules currently being built.
+    var inProgressModules = {};
+
+    function build(module) {
+        var factory = module.factory;
+        module.exports = {};
+        delete module.factory;
+        factory(require, module.exports, module);
+        return module.exports;
+    }
+
+    require = function (id) {
+        if (!modules[id]) {
+            throw "module " + id + " not found";
+        } else if (id in inProgressModules) {
+            var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;
+            throw "Cycle in require graph: " + cycle;
+        }
+        if (modules[id].factory) {
+            try {
+                inProgressModules[id] = requireStack.length;
+                requireStack.push(id);
+                return build(modules[id]);
+            } finally {
+                delete inProgressModules[id];
+                requireStack.pop();
+            }
+        }
+        return modules[id].exports;
+    };
+
+    define = function (id, factory) {
+        if (modules[id]) {
+            throw "module " + id + " already defined";
+        }
+
+        modules[id] = {
+            id: id,
+            factory: factory
+        };
+    };
+
+    define.remove = function (id) {
+        delete modules[id];
+    };
+
+    define.moduleMap = modules;
+})();
+
+//Export for use in node
+if (typeof module === "object" && typeof require === "function") {
+    module.exports.require = require;
+    module.exports.define = define;
+}
+
+// file: lib/cordova.js
+define("cordova", function(require, exports, module) {
+
+
+var channel = require('cordova/channel');
+
+/**
+ * Listen for DOMContentLoaded and notify our channel subscribers.
+ */
+document.addEventListener('DOMContentLoaded', function() {
+    channel.onDOMContentLoaded.fire();
+}, false);
+if (document.readyState == 'complete' || document.readyState == 'interactive') {
+    channel.onDOMContentLoaded.fire();
+}
+
+/**
+ * Intercept calls to addEventListener + removeEventListener and handle deviceready,
+ * resume, and pause events.
+ */
+var m_document_addEventListener = document.addEventListener;
+var m_document_removeEventListener = document.removeEventListener;
+var m_window_addEventListener = window.addEventListener;
+var m_window_removeEventListener = window.removeEventListener;
+
+/**
+ * Houses custom event handlers to intercept on document + window event listeners.
+ */
+var documentEventHandlers = {},
+    windowEventHandlers = {};
+
+document.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    if (typeof documentEventHandlers[e] != 'undefined') {
+        documentEventHandlers[e].subscribe(handler);
+    } else {
+        m_document_addEventListener.call(document, evt, handler, capture);
+    }
+};
+
+window.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    if (typeof windowEventHandlers[e] != 'undefined') {
+        windowEventHandlers[e].subscribe(handler);
+    } else {
+        m_window_addEventListener.call(window, evt, handler, capture);
+    }
+};
+
+document.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    // If unsubscribing from an event that is handled by a plugin
+    if (typeof documentEventHandlers[e] != "undefined") {
+        documentEventHandlers[e].unsubscribe(handler);
+    } else {
+        m_document_removeEventListener.call(document, evt, handler, capture);
+    }
+};
+
+window.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    // If unsubscribing from an event that is handled by a plugin
+    if (typeof windowEventHandlers[e] != "undefined") {
+        windowEventHandlers[e].unsubscribe(handler);
+    } else {
+        m_window_removeEventListener.call(window, evt, handler, capture);
+    }
+};
+
+function createEvent(type, data) {
+    var event = document.createEvent('Events');
+    event.initEvent(type, false, false);
+    if (data) {
+        for (var i in data) {
+            if (data.hasOwnProperty(i)) {
+                event[i] = data[i];
+            }
+        }
+    }
+    return event;
+}
+
+if(typeof window.console === "undefined") {
+    window.console = {
+        log:function(){}
+    };
+}
+
+var cordova = {
+    define:define,
+    require:require,
+    /**
+     * Methods to add/remove your own addEventListener hijacking on document + window.
+     */
+    addWindowEventHandler:function(event) {
+        return (windowEventHandlers[event] = channel.create(event));
+    },
+    addStickyDocumentEventHandler:function(event) {
+        return (documentEventHandlers[event] = channel.createSticky(event));
+    },
+    addDocumentEventHandler:function(event) {
+        return (documentEventHandlers[event] = channel.create(event));
+    },
+    removeWindowEventHandler:function(event) {
+        delete windowEventHandlers[event];
+    },
+    removeDocumentEventHandler:function(event) {
+        delete documentEventHandlers[event];
+    },
+    /**
+     * Retrieve original event handlers that were replaced by Cordova
+     *
+     * @return object
+     */
+    getOriginalHandlers: function() {
+        return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
+        'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
+    },
+    /**
+     * Method to fire event from native code
+     * bNoDetach is required for events which cause an exception which needs to be caught in native code
+     */
+    fireDocumentEvent: function(type, data, bNoDetach) {
+        var evt = createEvent(type, data);
+        if (typeof documentEventHandlers[type] != 'undefined') {
+            if( bNoDetach ) {
+              documentEventHandlers[type].fire(evt);
+            }
+            else {
+              setTimeout(function() {
+                  // Fire deviceready on listeners that were registered before cordova.js was loaded.
+                  if (type == 'deviceready') {
+                      document.dispatchEvent(evt);
+                  }
+                  documentEventHandlers[type].fire(evt);
+              }, 0);
+            }
+        } else {
+            document.dispatchEvent(evt);
+        }
+    },
+    fireWindowEvent: function(type, data) {
+        var evt = createEvent(type,data);
+        if (typeof windowEventHandlers[type] != 'undefined') {
+            setTimeout(function() {
+                windowEventHandlers[type].fire(evt);
+            }, 0);
+        } else {
+            window.dispatchEvent(evt);
+        }
+    },
+
+    /**
+     * Plugin callback mechanism.
+     */
+    // Randomize the starting callbackId to avoid collisions after refreshing or navigating.
+    // This way, it's very unlikely that any new callback would get the same callbackId as an old callback.
+    callbackId: Math.floor(Math.random() * 2000000000),
+    callbacks:  {},
+    callbackStatus: {
+        NO_RESULT: 0,
+        OK: 1,
+        CLASS_NOT_FOUND_EXCEPTION: 2,
+        ILLEGAL_ACCESS_EXCEPTION: 3,
+        INSTANTIATION_EXCEPTION: 4,
+        MALFORMED_URL_EXCEPTION: 5,
+        IO_EXCEPTION: 6,
+        INVALID_ACTION: 7,
+        JSON_EXCEPTION: 8,
+        ERROR: 9
+    },
+
+    /**
+     * Called by native code when returning successful result from an action.
+     */
+    callbackSuccess: function(callbackId, args) {
+        try {
+            cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
+        } catch (e) {
+            console.log("Error in error callback: " + callbackId + " = "+e);
+        }
+    },
+
+    /**
+     * Called by native code when returning error result from an action.
+     */
+    callbackError: function(callbackId, args) {
+        // TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative.
+        // Derive success from status.
+        try {
+            cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
+        } catch (e) {
+            console.log("Error in error callback: " + callbackId + " = "+e);
+        }
+    },
+
+    /**
+     * Called by native code when returning the result from an action.
+     */
+    callbackFromNative: function(callbackId, success, status, args, keepCallback) {
+        var callback = cordova.callbacks[callbackId];
+        if (callback) {
+            if (success && status == cordova.callbackStatus.OK) {
+                callback.success && callback.success.apply(null, args);
+            } else if (!success) {
+                callback.fail && callback.fail.apply(null, args);
+            }
+
+            // Clear callback if not expecting any more results
+            if (!keepCallback) {
+                delete cordova.callbacks[callbackId];
+            }
+        }
+    },
+    addConstructor: function(func) {
+        channel.onCordovaReady.subscribe(function() {
+            try {
+                func();
+            } catch(e) {
+                console.log("Failed to run constructor: " + e);
+            }
+        });
+    }
+};
+
+// Register pause, resume and deviceready channels as events on document.
+channel.onPause = cordova.addDocumentEventHandler('pause');
+channel.onResume = cordova.addDocumentEventHandler('resume');
+channel.onDeviceReady = cordova.addStickyDocumentEventHandler('deviceready');
+
+module.exports = cordova;
+
+});
+
+// file: lib/common/argscheck.js
+define("cordova/argscheck", function(require, exports, module) {
+
+var exec = require('cordova/exec');
+var utils = require('cordova/utils');
+
+var moduleExports = module.exports;
+
+var typeMap = {
+    'A': 'Array',
+    'D': 'Date',
+    'N': 'Number',
+    'S': 'String',
+    'F': 'Function',
+    'O': 'Object'
+};
+
+function extractParamName(callee, argIndex) {
+  return (/.*?\((.*?)\)/).exec(callee)[1].split(', ')[argIndex];
+}
+
+function checkArgs(spec, functionName, args, opt_callee) {
+    if (!moduleExports.enableChecks) {
+        return;
+    }
+    var errMsg = null;
+    var typeName;
+    for (var i = 0; i < spec.length; ++i) {
+        var c = spec.charAt(i),
+            cUpper = c.toUpperCase(),
+            arg = args[i];
+        // Asterix means allow anything.
+        if (c == '*') {
+            continue;
+        }
+        typeName = utils.typeName(arg);
+        if ((arg === null || arg === undefined) && c == cUpper) {
+            continue;
+        }
+        if (typeName != typeMap[cUpper]) {
+            errMsg = 'Expected ' + typeMap[cUpper];
+            break;
+        }
+    }
+    if (errMsg) {
+        errMsg += ', but got ' + typeName + '.';
+        errMsg = 'Wrong type for parameter "' + extractParamName(opt_callee || args.callee, i) + '" of ' + functionName + ': ' + errMsg;
+        // Don't log when running jake test.
+        if (typeof jasmine == 'undefined') {
+            console.error(errMsg);
+        }
+        throw TypeError(errMsg);
+    }
+}
+
+function getValue(value, defaultValue) {
+    return value === undefined ? defaultValue : value;
+}
+
+moduleExports.checkArgs = checkArgs;
+moduleExports.getValue = getValue;
+moduleExports.enableChecks = true;
+
+
+});
+
+// file: lib/common/builder.js
+define("cordova/builder", function(require, exports, module) {
+
+var utils = require('cordova/utils');
+
+function each(objects, func, context) {
+    for (var prop in objects) {
+        if (objects.hasOwnProperty(prop)) {
+            func.apply(context, [objects[prop], prop]);
+        }
+    }
+}
+
+function clobber(obj, key, value) {
+    exports.replaceHookForTesting(obj, key);
+    obj[key] = value;
+    // Getters can only be overridden by getters.
+    if (obj[key] !== value) {
+        utils.defineGetter(obj, key, function() {
+            return value;
+        });
+    }
+}
+
+function assignOrWrapInDeprecateGetter(obj, key, value, message) {
+    if (message) {
+        utils.defineGetter(obj, key, function() {
+            console.log(message);
+            delete obj[key];
+            clobber(obj, key, value);
+            return value;
+        });
+    } else {
+        clobber(obj, key, value);
+    }
+}
+
+function include(parent, objects, clobber, merge) {
+    each(objects, function (obj, key) {
+        try {
+          var result = obj.path ? require(obj.path) : {};
+
+          if (clobber) {
+              // Clobber if it doesn't exist.
+              if (typeof parent[key] === 'undefined') {
+                  assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+              } else if (typeof obj.path !== 'undefined') {
+                  // If merging, merge properties onto parent, otherwise, clobber.
+                  if (merge) {
+                      recursiveMerge(parent[key], result);
+                  } else {
+                      assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+                  }
+              }
+              result = parent[key];
+          } else {
+            // Overwrite if not currently defined.
+            if (typeof parent[key] == 'undefined') {
+              assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+            } else {
+              // Set result to what already exists, so we can build children into it if they exist.
+              result = parent[key];
+            }
+          }
+
+          if (obj.children) {
+            include(result, obj.children, clobber, merge);
+          }
+        } catch(e) {
+          utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"');
+        }
+    });
+}
+
+/**
+ * Merge properties from one object onto another recursively.  Properties from
+ * the src object will overwrite existing target property.
+ *
+ * @param target Object to merge properties into.
+ * @param src Object to merge properties from.
+ */
+function recursiveMerge(target, src) {
+    for (var prop in src) {
+        if (src.hasOwnProperty(prop)) {
+            if (target.prototype && target.prototype.constructor === target) {
+                // If the target object is a constructor override off prototype.
+                clobber(target.prototype, prop, src[prop]);
+            } else {
+                if (typeof src[prop] === 'object' && typeof target[prop] === 'object') {
+                    recursiveMerge(target[prop], src[prop]);
+                } else {
+                    clobber(target, prop, src[prop]);
+                }
+            }
+        }
+    }
+}
+
+exports.buildIntoButDoNotClobber = function(objects, target) {
+    include(target, objects, false, false);
+};
+exports.buildIntoAndClobber = function(objects, target) {
+    include(target, objects, true, false);
+};
+exports.buildIntoAndMerge = function(objects, target) {
+    include(target, objects, true, true);
+};
+exports.recursiveMerge = recursiveMerge;
+exports.assignOrWrapInDeprecateGetter = assignOrWrapInDeprecateGetter;
+exports.replaceHookForTesting = function() {};
+
+});
+
+// file: lib/common/channel.js
+define("cordova/channel", function(require, exports, module) {
+
+var utils = require('cordova/utils'),
+    nextGuid = 1;
+
+/**
+ * Custom pub-sub "channel" that can have functions subscribed to it
+ * This object is used to define and control firing of events for
+ * cordova initialization, as well as for custom events thereafter.
+ *
+ * The order of events during page load and Cordova startup is as follows:
+ *
+ * onDOMContentLoaded*         Internal event that is received when the web page is loaded and parsed.
+ * onNativeReady*              Internal event that indicates the Cordova native side is ready.
+ * onCordovaReady*             Internal event fired when all Cordova JavaScript objects have been created.
+ * onCordovaInfoReady*         Internal event fired when device properties are available.
+ * onCordovaConnectionReady*   Internal event fired when the connection property has been set.
+ * onDeviceReady*              User event fired to indicate that Cordova is ready
+ * onResume                    User event fired to indicate a start/resume lifecycle event
+ * onPause                     User event fired to indicate a pause lifecycle event
+ * onDestroy*                  Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
+ *
+ * The events marked with an * are sticky. Once they have fired, they will stay in the fired state.
+ * All listeners that subscribe after the event is fired will be executed right away.
+ *
+ * The only Cordova events that user code should register for are:
+ *      deviceready           Cordova native code is initialized and Cordova APIs can be called from JavaScript
+ *      pause                 App has moved to background
+ *      resume                App has returned to foreground
+ *
+ * Listeners can be registered as:
+ *      document.addEventListener("deviceready", myDeviceReadyListener, false);
+ *      document.addEventListener("resume", myResumeListener, false);
+ *      document.addEventListener("pause", myPauseListener, false);
+ *
+ * The DOM lifecycle events should be used for saving and restoring state
+ *      window.onload
+ *      window.onunload
+ *
+ */
+
+/**
+ * Channel
+ * @constructor
+ * @param type  String the channel name
+ */
+var Channel = function(type, sticky) {
+    this.type = type;
+    // Map of guid -> function.
+    this.handlers = {};
+    // 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
+    this.state = sticky ? 1 : 0;
+    // Used in sticky mode to remember args passed to fire().
+    this.fireArgs = null;
+    // Used by onHasSubscribersChange to know if there are any listeners.
+    this.numHandlers = 0;
+    // Function that is called when the first listener is subscribed, or when
+    // the last listener is unsubscribed.
+    this.onHasSubscribersChange = null;
+},
+    channel = {
+        /**
+         * Calls the provided function only after all of the channels specified
+         * have been fired. All channels must be sticky channels.
+         */
+        join: function(h, c) {
+            var len = c.length,
+                i = len,
+                f = function() {
+                    if (!(--i)) h();
+                };
+            for (var j=0; j<len; j++) {
+                if (c[j].state === 0) {
+                    throw Error('Can only use join with sticky channels.');
+                }
+                c[j].subscribe(f);
+            }
+            if (!len) h();
+        },
+        create: function(type) {
+            return channel[type] = new Channel(type, false);
+        },
+        createSticky: function(type) {
+            return channel[type] = new Channel(type, true);
+        },
+
+        /**
+         * cordova Channels that must fire before "deviceready" is fired.
+         */
+        deviceReadyChannelsArray: [],
+        deviceReadyChannelsMap: {},
+
+        /**
+         * Indicate that a feature needs to be initialized before it is ready to be used.
+         * This holds up Cordova's "deviceready" event until the feature has been initialized
+         * and Cordova.initComplete(feature) is called.
+         *
+         * @param feature {String}     The unique feature name
+         */
+        waitForInitialization: function(feature) {
+            if (feature) {
+                var c = channel[feature] || this.createSticky(feature);
+                this.deviceReadyChannelsMap[feature] = c;
+                this.deviceReadyChannelsArray.push(c);
+            }
+        },
+
+        /**
+         * Indicate that initialization code has completed and the feature is ready to be used.
+         *
+         * @param feature {String}     The unique feature name
+         */
+        initializationComplete: function(feature) {
+            var c = this.deviceReadyChannelsMap[feature];
+            if (c) {
+                c.fire();
+            }
+        }
+    };
+
+function forceFunction(f) {
+    if (typeof f != 'function') throw "Function required as first argument!";
+}
+
+/**
+ * Subscribes the given function to the channel. Any time that
+ * Channel.fire is called so too will the function.
+ * Optionally specify an execution context for the function
+ * and a guid that can be used to stop subscribing to the channel.
+ * Returns the guid.
+ */
+Channel.prototype.subscribe = function(f, c) {
+    // need a function to call
+    forceFunction(f);
+    if (this.state == 2) {
+        f.apply(c || this, this.fireArgs);
+        return;
+    }
+
+    var func = f,
+        guid = f.observer_guid;
+    if (typeof c == "object") { func = utils.close(c, f); }
+
+    if (!guid) {
+        // first time any channel has seen this subscriber
+        guid = '' + nextGuid++;
+    }
+    func.observer_guid = guid;
+    f.observer_guid = guid;
+
+    // Don't add the same handler more than once.
+    if (!this.handlers[guid]) {
+        this.handlers[guid] = func;
+        this.numHandlers++;
+        if (this.numHandlers == 1) {
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+/**
+ * Unsubscribes the function with the given guid from the channel.
+ */
+Channel.prototype.unsubscribe = function(f) {
+    // need a function to unsubscribe
+    forceFunction(f);
+
+    var guid = f.observer_guid,
+        handler = this.handlers[guid];
+    if (handler) {
+        delete this.handlers[guid];
+        this.numHandlers--;
+        if (this.numHandlers === 0) {
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+/**
+ * Calls all functions subscribed to this channel.
+ */
+Channel.prototype.fire = function(e) {
+    var fail = false,
+        fireArgs = Array.prototype.slice.call(arguments);
+    // Apply stickiness.
+    if (this.state == 1) {
+        this.state = 2;
+        this.fireArgs = fireArgs;
+    }
+    if (this.numHandlers) {
+        // Copy the values first so that it is safe to modify it from within
+        // callbacks.
+        var toCall = [];
+        for (var item in this.handlers) {
+            toCall.push(this.handlers[item]);
+        }
+        for (var i = 0; i < toCall.length; ++i) {
+            toCall[i].apply(this, fireArgs);
+        }
+        if (this.state == 2 && this.numHandlers) {
+            this.numHandlers = 0;
+            this.handlers = {};
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+
+// defining them here so they are ready super fast!
+// DOM event that is received when the web page is loaded and parsed.
+channel.createSticky('onDOMContentLoaded');
+
+// Event to indicate the Cordova native side is ready.
+channel.createSticky('onNativeReady');
+
+// Event to indicate that all Cordova JavaScript objects have been created
+// and it's time to run plugin constructors.
+channel.createSticky('onCordovaReady');
+
+// Event to indicate that device properties are available
+channel.createSticky('onCordovaInfoReady');
+
+// Event to indicate that the connection property has been set.
+channel.createSticky('onCordovaConnectionReady');
+
+// Event to indicate that all automatically loaded JS plugins are loaded and ready.
+channel.createSticky('onPluginsReady');
+
+// Event to indicate that Cordova is ready
+channel.createSticky('onDeviceReady');
+
+// Event to indicate a resume lifecycle event
+channel.create('onResume');
+
+// Event to indicate a pause lifecycle event
+channel.create('onPause');
+
+// Event to indicate a destroy lifecycle event
+channel.createSticky('onDestroy');
+
+// Channels that must fire before "deviceready" is fired.
+channel.waitForInitialization('onCordovaReady');
+channel.waitForInitialization('onCordovaConnectionReady');
+channel.waitForInitialization('onDOMContentLoaded');
+
+module.exports = channel;
+
+});
+
+// file: lib/common/commandProxy.js
+define("cordova/commandProxy", function(require, exports, module) {
+
+
+// internal map of proxy function
+var CommandProxyMap = {};
+
+module.exports = {
+
+    // example: cordova.commandProxy.add("Accelerometer",{getCurrentAcceleration: function(successCallback, errorCallback, options) {...},...);
+    add:function(id,proxyObj) {
+        console.log("adding proxy for " + id);
+        CommandProxyMap[id] = proxyObj;
+        return proxyObj;
+    },
+
+    // cordova.commandProxy.remove("Accelerometer");
+    remove:function(id) {
+        var proxy = CommandProxyMap[id];
+        delete CommandProxyMap[id];
+        CommandProxyMap[id] = null;
+        return proxy;
+    },
+
+    get:function(service,action) {
+        return ( CommandProxyMap[service] ? CommandProxyMap[service][action] : null );
+    }
+};
+});
+
+// file: lib/android/exec.js
+define("cordova/exec", function(require, exports, module) {
+
+/**
+ * Execute a cordova command.  It is up to the native side whether this action
+ * is synchronous or asynchronous.  The native side can return:
+ *      Synchronous: PluginResult object as a JSON string
+ *      Asynchronous: Empty string ""
+ * If async, the native side will cordova.callbackSuccess or cordova.callbackError,
+ * depending upon the result of the action.
+ *
+ * @param {Function} success    The success callback
+ * @param {Function} fail       The fail callback
+ * @param {String} service      The name of the service to use
+ * @param {String} action       Action to be run in cordova
+ * @param {String[]} [args]     Zero or more arguments to pass to the method
+ */
+var cordova = require('cordova'),
+    nativeApiProvider = require('cordova/plugin/android/nativeapiprovider'),
+    utils = require('cordova/utils'),
+    jsToNativeModes = {
+        PROMPT: 0,
+        JS_OBJECT: 1,
+        // This mode is currently for benchmarking purposes only. It must be enabled
+        // on the native side through the ENABLE_LOCATION_CHANGE_EXEC_MODE
+        // constant within CordovaWebViewClient.java before it will work.
+        LOCATION_CHANGE: 2
+    },
+    nativeToJsModes = {
+        // Polls for messages using the JS->Native bridge.
+        POLLING: 0,
+        // For LOAD_URL to be viable, it would need to have a work-around for
+        // the bug where the soft-keyboard gets dismissed when a message is sent.
+        LOAD_URL: 1,
+        // For the ONLINE_EVENT to be viable, it would need to intercept all event
+        // listeners (both through addEventListener and window.ononline) as well
+        // as set the navigator property itself.
+        ONLINE_EVENT: 2,
+        // Uses reflection to access private APIs of the WebView that can send JS
+        // to be executed.
+        // Requires Android 3.2.4 or above.
+        PRIVATE_API: 3
+    },
+    jsToNativeBridgeMode,  // Set lazily.
+    nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT,
+    pollEnabled = false,
+    messagesFromNative = [];
+
+function androidExec(success, fail, service, action, args) {
+    // Set default bridge modes if they have not already been set.
+    // By default, we use the failsafe, since addJavascriptInterface breaks too often
+    if (jsToNativeBridgeMode === undefined) {
+        androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
+    }
+
+    // Process any ArrayBuffers in the args into a string.
+    for (var i = 0; i < args.length; i++) {
+        if (utils.typeName(args[i]) == 'ArrayBuffer') {
+            args[i] = window.btoa(String.fromCharCode.apply(null, new Uint8Array(args[i])));
+        }
+    }
+
+    var callbackId = service + cordova.callbackId++,
+        argsJson = JSON.stringify(args);
+
+    if (success || fail) {
+        cordova.callbacks[callbackId] = {success:success, fail:fail};
+    }
+
+    if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) {
+        window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;
+    } else {
+        var messages = nativeApiProvider.get().exec(service, action, callbackId, argsJson);
+        // If argsJson was received by Java as null, try again with the PROMPT bridge mode.
+        // This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2.  See CB-2666.
+        if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && messages === "@Null arguments.") {
+            androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
+            androidExec(success, fail, service, action, args);
+            androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
+            return;
+        } else {
+            androidExec.processMessages(messages);
+        }
+    }
+}
+
+function pollOnce() {
+    var msg = nativeApiProvider.get().retrieveJsMessages();
+    androidExec.processMessages(msg);
+}
+
+function pollingTimerFunc() {
+    if (pollEnabled) {
+        pollOnce();
+        setTimeout(pollingTimerFunc, 50);
+    }
+}
+
+function hookOnlineApis() {
+    function proxyEvent(e) {
+        cordova.fireWindowEvent(e.type);
+    }
+    // The network module takes care of firing online and offline events.
+    // It currently fires them only on document though, so we bridge them
+    // to window here (while first listening for exec()-releated online/offline
+    // events).
+    window.addEventListener('online', pollOnce, false);
+    window.addEventListener('offline', pollOnce, false);
+    cordova.addWindowEventHandler('online');
+    cordova.addWindowEventHandler('offline');
+    document.addEventListener('online', proxyEvent, false);
+    document.addEventListener('offline', proxyEvent, false);
+}
+
+hookOnlineApis();
+
+androidExec.jsToNativeModes = jsToNativeModes;
+androidExec.nativeToJsModes = nativeToJsModes;
+
+androidExec.setJsToNativeBridgeMode = function(mode) {
+    if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaNative) {
+        console.log('Falling back on PROMPT mode since _cordovaNative is missing. Expected for Android 3.2 and lower only.');
+        mode = jsToNativeModes.PROMPT;
+    }
+    nativeApiProvider.setPreferPrompt(mode == jsToNativeModes.PROMPT);
+    jsToNativeBridgeMode = mode;
+};
+
+androidExec.setNativeToJsBridgeMode = function(mode) {
+    if (mode == nativeToJsBridgeMode) {
+        return;
+    }
+    if (nativeToJsBridgeMode == nativeToJsModes.POLLING) {
+        pollEnabled = false;
+    }
+
+    nativeToJsBridgeMode = mode;
+    // Tell the native side to switch modes.
+    nativeApiProvider.get().setNativeToJsBridgeMode(mode);
+
+    if (mode == nativeToJsModes.POLLING) {
+        pollEnabled = true;
+        setTimeout(pollingTimerFunc, 1);
+    }
+};
+
+// Processes a single message, as encoded by NativeToJsMessageQueue.java.
+function processMessage(message) {
+    try {
+        var firstChar = message.charAt(0);
+        if (firstChar == 'J') {
+            eval(message.slice(1));
+        } else if (firstChar == 'S' || firstChar == 'F') {
+            var success = firstChar == 'S';
+            var keepCallback = message.charAt(1) == '1';
+            var spaceIdx = message.indexOf(' ', 2);
+            var status = +message.slice(2, spaceIdx);
+            var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1);
+            var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx);
+            var payloadKind = message.charAt(nextSpaceIdx + 1);
+            var payload;
+            if (payloadKind == 's') {
+                payload = message.slice(nextSpaceIdx + 2);
+            } else if (payloadKind == 't') {
+                payload = true;
+            } else if (payloadKind == 'f') {
+                payload = false;
+            } else if (payloadKind == 'N') {
+                payload = null;
+            } else if (payloadKind == 'n') {
+                payload = +message.slice(nextSpaceIdx + 2);
+            } else if (payloadKind == 'A') {
+                var data = message.slice(nextSpaceIdx + 2);
+                var bytes = window.atob(data);
+                var arraybuffer = new Uint8Array(bytes.length);
+                for (var i = 0; i < bytes.length; i++) {
+                    arraybuffer[i] = bytes.charCodeAt(i);
+                }
+                payload = arraybuffer.buffer;
+            } else if (payloadKind == 'S') {
+                payload = window.atob(message.slice(nextSpaceIdx + 2));
+            } else {
+                payload = JSON.parse(message.slice(nextSpaceIdx + 1));
+            }
+            cordova.callbackFromNative(callbackId, success, status, [payload], keepCallback);
+        } else {
+            console.log("processMessage failed: invalid message:" + message);
+        }
+    } catch (e) {
+        console.log("processMessage failed: Message: " + message);
+        console.log("processMessage failed: Error: " + e);
+        console.log("processMessage failed: Stack: " + e.stack);
+    }
+}
+
+// This is called from the NativeToJsMessageQueue.java.
+androidExec.processMessages = function(messages) {
+    if (messages) {
+        messagesFromNative.push(messages);
+        // Check for the reentrant case, and enqueue the message if that's the case.
+        if (messagesFromNative.length > 1) {
+            return;
+        }
+        while (messagesFromNative.length) {
+            // Don't unshift until the end so that reentrancy can be detected.
+            messages = messagesFromNative[0];
+            // The Java side can send a * message to indicate that it
+            // still has messages waiting to be retrieved.
+            if (messages == '*') {
+                messagesFromNative.shift();
+                window.setTimeout(pollOnce, 0);
+                return;
+            }
+
+            var spaceIdx = messages.indexOf(' ');
+            var msgLen = +messages.slice(0, spaceIdx);
+            var message = messages.substr(spaceIdx + 1, msgLen);
+            messages = messages.slice(spaceIdx + msgLen + 1);
+            processMessage(message);
+            if (messages) {
+                messagesFromNative[0] = messages;
+            } else {
+                messagesFromNative.shift();
+            }
+        }
+    }
+};
+
+module.exports = androidExec;
+
+});
+
+// file: lib/common/modulemapper.js
+define("cordova/modulemapper", function(require, exports, module) {
+
+var builder = require('cordova/builder'),
+    moduleMap = define.moduleMap,
+    symbolList,
+    deprecationMap;
+
+exports.reset = function() {
+    symbolList = [];
+    deprecationMap = {};
+};
+
+function addEntry(strategy, moduleName, symbolPath, opt_deprecationMessage) {
+    if (!(moduleName in moduleMap)) {
+        throw new Error('Module ' + moduleName + ' does not exist.');
+    }
+    symbolList.push(strategy, moduleName, symbolPath);
+    if (opt_deprecationMessage) {
+        deprecationMap[symbolPath] = opt_deprecationMessage;
+    }
+}
+
+// Note: Android 2.3 does have Function.bind().
+exports.clobbers = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('c', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+exports.merges = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('m', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+exports.defaults = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('d', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+function prepareNamespace(symbolPath, context) {
+    if (!symbolPath) {
+        return context;
+    }
+    var parts = symbolPath.split('.');
+    var cur = context;
+    for (var i = 0, part; part = parts[i]; ++i) {
+        cur = cur[part] = cur[part] || {};
+    }
+    return cur;
+}
+
+exports.mapModules = function(context) {
+    var origSymbols = {};
+    context.CDV_origSymbols = origSymbols;
+    for (var i = 0, len = symbolList.length; i < len; i += 3) {
+        var strategy = symbolList[i];
+        var moduleName = symbolList[i + 1];
+        var symbolPath = symbolList[i + 2];
+        var lastDot = symbolPath.lastIndexOf('.');
+        var namespace = symbolPath.substr(0, lastDot);
+        var lastName = symbolPath.substr(lastDot + 1);
+
+        var module = require(moduleName);
+        var deprecationMsg = symbolPath in deprecationMap ? 'Access made to deprecated symbol: ' + symbolPath + '. ' + deprecationMsg : null;
+        var parentObj = prepareNamespace(namespace, context);
+        var target = parentObj[lastName];
+
+        if (strategy == 'm' && target) {
+            builder.recursiveMerge(target, module);
+        } else if ((strategy == 'd' && !target) || (strategy != 'd')) {
+            if (!(symbolPath in origSymbols)) {
+                origSymbols[symbolPath] = target;
+            }
+            builder.assignOrWrapInDeprecateGetter(parentObj, lastName, module, deprecationMsg);
+        }
+    }
+};
+
+exports.getOriginalSymbol = function(context, symbolPath) {
+    var origSymbols = context.CDV_origSymbols;
+    if (origSymbols && (symbolPath in origSymbols)) {
+        return origSymbols[symbolPath];
+    }
+    var parts = symbolPath.split('.');
+    var obj = context;
+    for (var i = 0; i < parts.length; ++i) {
+        obj = obj && obj[parts[i]];
+    }
+    return obj;
+};
+
+exports.loadMatchingModules = function(matchingRegExp) {
+    for (var k in moduleMap) {
+        if (matchingRegExp.exec(k)) {
+            require(k);
+        }
+    }
+};
+
+exports.reset();
+
+
+});
+
+// file: lib/android/platform.js
+define("cordova/platform", function(require, exports, module) {
+
+module.exports = {
+    id: "android",
+    initialize:function() {
+        var channel = require("cordova/channel"),
+            cordova = require('cordova'),
+            exec = require('cordova/exec'),
+            modulemapper = require('cordova/modulemapper');
+
+        modulemapper.loadMatchingModules(/cordova.*\/symbols$/);
+        modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app');
+
+        modulemapper.mapModules(window);
+
+        // Inject a listener for the backbutton on the document.
+        var backButtonChannel = cordova.addDocumentEventHandler('backbutton');
+        backButtonChannel.onHasSubscribersChange = function() {
+            // If we just attached the first handler or detached the last handler,
+            // let native know we need to override the back button.
+            exec(null, null, "App", "overrideBackbutton", [this.numHandlers == 1]);
+        };
+
+        // Add hardware MENU and SEARCH button handlers
+        cordova.addDocumentEventHandler('menubutton');
+        cordova.addDocumentEventHandler('searchbutton');
+
+        // Let native code know we are all done on the JS side.
+        // Native code will then un-hide the WebView.
+        channel.join(function() {
+            exec(null, null, "App", "show", []);
+        }, [channel.onCordovaReady]);
+    }
+};
+
+});
+
+// file: lib/common/plugin/Acceleration.js
+define("cordova/plugin/Acceleration", function(require, exports, module) {
+
+var Acceleration = function(x, y, z, timestamp) {
+    this.x = x;
+    this.y = y;
+    this.z = z;
+    this.timestamp = timestamp || (new Date()).getTime();
+};
+
+module.exports = Acceleration;
+
+});
+
+// file: lib/common/plugin/Camera.js
+define("cordova/plugin/Camera", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    Camera = require('cordova/plugin/CameraConstants'),
+    CameraPopoverHandle = require('cordova/plugin/CameraPopoverHandle');
+
+var cameraExport = {};
+
+// Tack on the Camera Constants to the base camera plugin.
+for (var key in Camera) {
+    cameraExport[key] = Camera[key];
+}
+
+/**
+ * Gets a picture from source defined by "options.sourceType", and returns the
+ * image as defined by the "options.destinationType" option.
+
+ * The defaults are sourceType=CAMERA and destinationType=FILE_URI.
+ *
+ * @param {Function} successCallback
+ * @param {Function} errorCallback
+ * @param {Object} options
+ */
+cameraExport.getPicture = function(successCallback, errorCallback, options) {
+    argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
+    options = options || {};
+    var getValue = argscheck.getValue;
+
+    var quality = getValue(options.quality, 50);
+    var destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
+    var sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
+    var targetWidth = getValue(options.targetWidth, -1);
+    var targetHeight = getValue(options.targetHeight, -1);
+    var encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG);
+    var mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE);
+    var allowEdit = !!options.allowEdit;
+    var correctOrientation = !!options.correctOrientation;
+    var saveToPhotoAlbum = !!options.saveToPhotoAlbum;
+    var popoverOptions = getValue(options.popoverOptions, null);
+    var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
+
+    var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
+                mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
+
+    exec(successCallback, errorCallback, "Camera", "takePicture", args);
+    return new CameraPopoverHandle();
+};
+
+cameraExport.cleanup = function(successCallback, errorCallback) {
+    exec(successCallback, errorCallback, "Camera", "cleanup", []);
+};
+
+module.exports = cameraExport;
+
+});
+
+// file: lib/common/plugin/CameraConstants.js
+define("cordova/plugin/CameraConstants", function(require, exports, module) {
+
+module.exports = {
+  DestinationType:{
+    DATA_URL: 0,         // Return base64 encoded string
+    FILE_URI: 1,         // Return file uri (content://media/external/images/media/2 for Android)
+    NATIVE_URI: 2        // Return native uri (eg. asset-library://... for iOS)
+  },
+  EncodingType:{
+    JPEG: 0,             // Return JPEG encoded image
+    PNG: 1               // Return PNG encoded image
+  },
+  MediaType:{
+    PICTURE: 0,          // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
+    VIDEO: 1,            // allow selection of video only, ONLY RETURNS URL
+    ALLMEDIA : 2         // allow selection from all media types
+  },
+  PictureSourceType:{
+    PHOTOLIBRARY : 0,    // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
+    CAMERA : 1,          // Take picture from camera
+    SAVEDPHOTOALBUM : 2  // Choose image from picture library (same as PHOTOLIBRARY for Android)
+  },
+  PopoverArrowDirection:{
+      ARROW_UP : 1,        // matches iOS UIPopoverArrowDirection constants to specify arrow location on popover
+      ARROW_DOWN : 2,
+      ARROW_LEFT : 4,
+      ARROW_RIGHT : 8,
+      ARROW_ANY : 15
+  },
+  Direction:{
+      BACK: 0,
+      FRONT: 1
+  }
+};
+
+});
+
+// file: lib/common/plugin/CameraPopoverHandle.js
+define("cordova/plugin/CameraPopoverHandle", function(require, exports, module) {
+
+var exec = require('cordova/exec');
+
+/**
+ * A handle to an image picker popover.
+ */
+var CameraPopoverHandle = function() {
+    this.setPosition = function(popoverOptions) {
+        console.log('CameraPopoverHandle.setPosition is only supported on iOS.');
+    };
+};
+
+module.exports = CameraPopoverHandle;
+
+});
+
+// file: lib/common/plugin/CameraPopoverOptions.js
+define("cordova/plugin/CameraPopoverOptions", function(require, exports, module) {
+
+var Camera = require('cordova/plugin/CameraConstants');
+
+/**
+ * Encapsulates options for iOS Popover image picker
+ */
+var CameraPopoverOptions = function(x,y,width,height,arrowDir){
+    // information of rectangle that popover should be anchored to
+    this.x = x || 0;
+    this.y = y || 32;
+    this.width = width || 320;
+    this.height = height || 480;
+    // The direction of the popover arrow
+    this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY;
+};
+
+module.exports = CameraPopoverOptions;
+
+});
+
+// file: lib/common/plugin/CaptureAudioOptions.js
+define("cordova/plugin/CaptureAudioOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all audio capture operation configuration options.
+ */
+var CaptureAudioOptions = function(){
+    // Upper limit of sound clips user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single sound clip in seconds.
+    this.duration = 0;
+    // The selected audio mode. Must match with one of the elements in supportedAudioModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureAudioOptions;
+
+});
+
+// file: lib/common/plugin/CaptureError.js
+define("cordova/plugin/CaptureError", function(require, exports, module) {
+
+/**
+ * The CaptureError interface encapsulates all errors in the Capture API.
+ */
+var CaptureError = function(c) {
+   this.code = c || null;
+};
+
+// Camera or microphone failed to capture image or sound.
+CaptureError.CAPTURE_INTERNAL_ERR = 0;
+// Camera application or audio capture application is currently serving other capture request.
+CaptureError.CAPTURE_APPLICATION_BUSY = 1;
+// Invalid use of the API (e.g. limit parameter has value less than one).
+CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
+// User exited camera application or audio capture application before capturing anything.
+CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
+// The requested capture operation is not supported.
+CaptureError.CAPTURE_NOT_SUPPORTED = 20;
+
+module.exports = CaptureError;
+
+});
+
+// file: lib/common/plugin/CaptureImageOptions.js
+define("cordova/plugin/CaptureImageOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all image capture operation configuration options.
+ */
+var CaptureImageOptions = function(){
+    // Upper limit of images user can take. Value must be equal or greater than 1.
+    this.limit = 1;
+    // The selected image mode. Must match with one of the elements in supportedImageModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureImageOptions;
+
+});
+
+// file: lib/common/plugin/CaptureVideoOptions.js
+define("cordova/plugin/CaptureVideoOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all video capture operation configuration options.
+ */
+var CaptureVideoOptions = function(){
+    // Upper limit of videos user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single video clip in seconds.
+    this.duration = 0;
+    // The selected video mode. Must match with one of the elements in supportedVideoModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureVideoOptions;
+
+});
+
+// file: lib/common/plugin/CompassError.js
+define("cordova/plugin/CompassError", function(require, exports, module) {
+
+/**
+ *  CompassError.
+ *  An error code assigned by an implementation when an error has occurred
+ * @constructor
+ */
+var CompassError = function(err) {
+    this.code = (err !== undefined ? err : null);
+};
+
+CompassError.COMPASS_INTERNAL_ERR = 0;
+CompassError.COMPASS_NOT_SUPPORTED = 20;
+
+module.exports = CompassError;
+
+});
+
+// file: lib/common/plugin/CompassHeading.js
+define("cordova/plugin/CompassHeading", function(require, exports, module) {
+
+var CompassHeading = function(magneticHeading, trueHeading, headingAccuracy, timestamp) {
+  this.magneticHeading = magneticHeading;
+  this.trueHeading = trueHeading;
+  this.headingAccuracy = headingAccuracy;
+  this.timestamp = timestamp || new Date().getTime();
+};
+
+module.exports = CompassHeading;
+
+});
+
+// file: lib/common/plugin/ConfigurationData.js
+define("cordova/plugin/ConfigurationData", function(require, exports, module) {
+
+/**
+ * Encapsulates a set of parameters that the capture device supports.
+ */
+function ConfigurationData() {
+    // The ASCII-encoded string in lower case representing the media type.
+    this.type = null;
+    // The height attribute represents height of the image or video in pixels.
+    // In the case of a sound clip this attribute has value 0.
+    this.height = 0;
+    // The width attribute represents width of the image or video in pixels.
+    // In the case of a sound clip this attribute has value 0
+    this.width = 0;
+}
+
+module.exports = ConfigurationData;
+
+});
+
+// file: lib/common/plugin/Connection.js
+define("cordova/plugin/Connection", function(require, exports, module) {
+
+/**
+ * Network status
+ */
+module.exports = {
+        UNKNOWN: "unknown",
+        ETHERNET: "ethernet",
+        WIFI: "wifi",
+        CELL_2G: "2g",
+        CELL_3G: "3g",
+        CELL_4G: "4g",
+        CELL:"cellular",
+        NONE: "none"
+};
+
+});
+
+// file: lib/common/plugin/Contact.js
+define("cordova/plugin/Contact", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    ContactError = require('cordova/plugin/ContactError'),
+    utils = require('cordova/utils');
+
+/**
+* Converts primitives into Complex Object
+* Currently only used for Date fields
+*/
+function convertIn(contact) {
+    var value = contact.birthday;
+    try {
+      contact.birthday = new Date(parseFloat(value));
+    } catch (exception){
+      console.log("Cordova Contact convertIn error: exception creating date.");
+    }
+    return contact;
+}
+
+/**
+* Converts Complex objects into primitives
+* Only conversion at present is for Dates.
+**/
+
+function convertOut(contact) {
+    var value = contact.birthday;
+    if (value !== null) {
+        // try to make it a Date object if it is not already
+        if (!utils.isDate(value)){
+            try {
+                value = new Date(value);
+            } catch(exception){
+                value = null;
+            }
+        }
+        if (utils.isDate(value)){
+            value = value.valueOf(); // convert to milliseconds
+        }
+        contact.birthday = value;
+    }
+    return contact;
+}
+
+/**
+* Contains information about a single contact.
+* @constructor
+* @param {DOMString} id unique identifier
+* @param {DOMString} displayName
+* @param {ContactName} name
+* @param {DOMString} nickname
+* @param {Array.<ContactField>} phoneNumbers array of phone numbers
+* @param {Array.<ContactField>} emails array of email addresses
+* @param {Array.<ContactAddress>} addresses array of addresses
+* @param {Array.<ContactField>} ims instant messaging user ids
+* @param {Array.<ContactOrganization>} organizations
+* @param {DOMString} birthday contact's birthday
+* @param {DOMString} note user notes about contact
+* @param {Array.<ContactField>} photos
+* @param {Array.<ContactField>} categories
+* @param {Array.<ContactField>} urls contact's web sites
+*/
+var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
+    ims, organizations, birthday, note, photos, categories, urls) {
+    this.id = id || null;
+    this.rawId = null;
+    this.displayName = displayName || null;
+    this.name = name || null; // ContactName
+    this.nickname = nickname || null;
+    this.phoneNumbers = phoneNumbers || null; // ContactField[]
+    this.emails = emails || null; // ContactField[]
+    this.addresses = addresses || null; // ContactAddress[]
+    this.ims = ims || null; // ContactField[]
+    this.organizations = organizations || null; // ContactOrganization[]
+    this.birthday = birthday || null;
+    this.note = note || null;
+    this.photos = photos || null; // ContactField[]
+    this.categories = categories || null; // ContactField[]
+    this.urls = urls || null; // ContactField[]
+};
+
+/**
+* Removes contact from device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.remove = function(successCB, errorCB) {
+    argscheck.checkArgs('FF', 'Contact.remove', arguments);
+    var fail = errorCB && function(code) {
+        errorCB(new ContactError(code));
+    };
+    if (this.id === null) {
+        fail(ContactError.UNKNOWN_ERROR);
+    }
+    else {
+        exec(successCB, fail, "Contacts", "remove", [this.id]);
+    }
+};
+
+/**
+* Creates a deep copy of this Contact.
+* With the contact ID set to null.
+* @return copy of this Contact
+*/
+Contact.prototype.clone = function() {
+    var clonedContact = utils.clone(this);
+    clonedContact.id = null;
+    clonedContact.rawId = null;
+
+    function nullIds(arr) {
+        if (arr) {
+            for (var i = 0; i < arr.length; ++i) {
+                arr[i].id = null;
+            }
+        }
+    }
+
+    // Loop through and clear out any id's in phones, emails, etc.
+    nullIds(clonedContact.phoneNumbers);
+    nullIds(clonedContact.emails);
+    nullIds(clonedContact.addresses);
+    nullIds(clonedContact.ims);
+    nullIds(clonedContact.organizations);
+    nullIds(clonedContact.categories);
+    nullIds(clonedContact.photos);
+    nullIds(clonedContact.urls);
+    return clonedContact;
+};
+
+/**
+* Persists contact to device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.save = function(successCB, errorCB) {
+    argscheck.checkArgs('FFO', 'Contact.save', arguments);
+    var fail = errorCB && function(code) {
+        errorCB(new ContactError(code));
+    };
+    var success = function(result) {
+        if (result) {
+            if (successCB) {
+                var fullContact = require('cordova/plugin/contacts').create(result);
+                successCB(convertIn(fullContact));
+            }
+        }
+        else {
+            // no Entry object returned
+            fail(ContactError.UNKNOWN_ERROR);
+        }
+    };
+    var dupContact = convertOut(utils.clone(this));
+    exec(success, fail, "Contacts", "save", [dupContact]);
+};
+
+
+module.exports = Contact;
+
+});
+
+// file: lib/common/plugin/ContactAddress.js
+define("cordova/plugin/ContactAddress", function(require, exports, module) {
+
+/**
+* Contact address.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code
+* @param formatted // NOTE: not a W3C standard
+* @param streetAddress
+* @param locality
+* @param region
+* @param postalCode
+* @param country
+*/
+
+var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
+    this.id = null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+    this.type = type || null;
+    this.formatted = formatted || null;
+    this.streetAddress = streetAddress || null;
+    this.locality = locality || null;
+    this.region = region || null;
+    this.postalCode = postalCode || null;
+    this.country = country || null;
+};
+
+module.exports = ContactAddress;
+
+});
+
+// file: lib/common/plugin/ContactError.js
+define("cordova/plugin/ContactError", function(require, exports, module) {
+
+/**
+ *  ContactError.
+ *  An error code assigned by an implementation when an error has occurred
+ * @constructor
+ */
+var ContactError = function(err) {
+    this.code = (typeof err != 'undefined' ? err : null);
+};
+
+/**
+ * Error codes
+ */
+ContactError.UNKNOWN_ERROR = 0;
+ContactError.INVALID_ARGUMENT_ERROR = 1;
+ContactError.TIMEOUT_ERROR = 2;
+ContactError.PENDING_OPERATION_ERROR = 3;
+ContactError.IO_ERROR = 4;
+ContactError.NOT_SUPPORTED_ERROR = 5;
+ContactError.PERMISSION_DENIED_ERROR = 20;
+
+module.exports = ContactError;
+
+});
+
+// file: lib/common/plugin/ContactField.js
+define("cordova/plugin/ContactField", function(require, exports, module) {
+
+/**
+* Generic contact field.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param type
+* @param value
+* @param pref
+*/
+var ContactField = function(type, value, pref) {
+    this.id = null;
+    this.type = (type && type.toString()) || null;
+    this.value = (value && value.toString()) || null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+};
+
+module.exports = ContactField;
+
+});
+
+// file: lib/common/plugin/ContactFindOptions.js
+define("cordova/plugin/ContactFindOptions", function(require, exports, module) {
+
+/**
+ * ContactFindOptions.
+ * @constructor
+ * @param filter used to match contacts against
+ * @param multiple boolean used to determine if more than one contact should be returned
+ */
+
+var ContactFindOptions = function(filter, multiple) {
+    this.filter = filter || '';
+    this.multiple = (typeof multiple != 'undefined' ? multiple : false);
+};
+
+module.exports = ContactFindOptions;
+
+});
+
+// file: lib/common/plugin/ContactName.js
+define("cordova/plugin/ContactName", function(require, exports, module) {
+
+/**
+* Contact name.
+* @constructor
+* @param formatted // NOTE: not part of W3C standard
+* @param familyName
+* @param givenName
+* @param middle
+* @param prefix
+* @param suffix
+*/
+var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
+    this.formatted = formatted || null;
+    this.familyName = familyName || null;
+    this.givenName = givenName || null;
+    this.middleName = middle || null;
+    this.honorificPrefix = prefix || null;
+    this.honorificSuffix = suffix || null;
+};
+
+module.exports = ContactName;
+
+});
+
+// file: lib/common/plugin/ContactOrganization.js
+define("cordova/plugin/ContactOrganization", function(require, exports, module) {
+
+/**
+* Contact organization.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param name
+* @param dept
+* @param title
+* @param startDate
+* @param endDate
+* @param location
+* @param desc
+*/
+
+var ContactOrganization = function(pref, type, name, dept, title) {
+    this.id = null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+    this.type = type || null;
+    this.name = name || null;
+    this.department = dept || null;
+    this.title = title || null;
+};
+
+module.exports = ContactOrganization;
+
+});
+
+// file: lib/common/plugin/Coordinates.js
+define("cordova/plugin/Coordinates", function(require, exports, module) {
+
+/**
+ * This class contains position information.
+ * @param {Object} lat
+ * @param {Object} lng
+ * @param {Object} alt
+ * @param {Object} acc
+ * @param {Object} head
+ * @param {Object} vel
+ * @param {Object} altacc
+ * @constructor
+ */
+var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
+    /**
+     * The latitude of the position.
+     */
+    this.latitude = lat;
+    /**
+     * The longitude of the position,
+     */
+    this.longitude = lng;
+    /**
+     * The accuracy of the position.
+     */
+    this.accuracy = acc;
+    /**
+     * The altitude of the position.
+     */
+    this.altitude = (alt !== undefined ? alt : null);
+    /**
+     * The direction the device is moving at the position.
+     */
+    this.heading = (head !== undefined ? head : null);
+    /**
+     * The velocity with which the device is moving at the position.
+     */
+    this.speed = (vel !== undefined ? vel : null);
+
+    if (this.speed === 0 || this.speed === null) {
+        this.heading = NaN;
+    }
+
+    /**
+     * The altitude accuracy of the position.
+     */
+    this.altitudeAccuracy = (altacc !== undefined) ? altacc : null;
+};
+
+module.exports = Coordinates;
+
+});
+
+// file: lib/common/plugin/DirectoryEntry.js
+define("cordova/plugin/DirectoryEntry", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    Entry = require('cordova/plugin/Entry'),
+    FileError = require('cordova/plugin/FileError'),
+    DirectoryReader = require('cordova/plugin/DirectoryReader');
+
+/**
+ * An interface representing a directory on the file system.
+ *
+ * {boolean} isFile always false (readonly)
+ * {boolean} isDirectory always true (readonly)
+ * {DOMString} name of the directory, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the directory (readonly)
+ * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly)
+ */
+var DirectoryEntry = function(name, fullPath) {
+     DirectoryEntry.__super__.constructor.call(this, false, true, name, fullPath);
+};
+
+utils.extend(DirectoryEntry, Entry);
+
+/**
+ * Creates a new DirectoryReader to read entries from this directory
+ */
+DirectoryEntry.prototype.createReader = function() {
+    return new DirectoryReader(this.fullPath);
+};
+
+/**
+ * Creates or looks up a directory
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
+ * @param {Flags} options to create or exclusively create the directory
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
+    argscheck.checkArgs('sOFF', 'DirectoryEntry.getDirectory', arguments);
+    var win = successCallback && function(result) {
+        var entry = new DirectoryEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]);
+};
+
+/**
+ * Deletes a directory and all of it's contents
+ *
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'DirectoryEntry.removeRecursively', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]);
+};
+
+/**
+ * Creates or looks up a file
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
+ * @param {Flags} options to create or exclusively create the file
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
+    argscheck.checkArgs('sOFF', 'DirectoryEntry.getFile', arguments);
+    var win = successCallback && function(result) {
+        var FileEntry = require('cordova/plugin/FileEntry');
+        var entry = new FileEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getFile", [this.fullPath, path, options]);
+};
+
+module.exports = DirectoryEntry;
+
+});
+
+// file: lib/common/plugin/DirectoryReader.js
+define("cordova/plugin/DirectoryReader", function(require, exports, module) {
+
+var exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError') ;
+
+/**
+ * An interface that lists the files and directories in a directory.
+ */
+function DirectoryReader(path) {
+    this.path = path || null;
+}
+
+/**
+ * Returns a list of entries from a directory.
+ *
+ * @param {Function} successCallback is called with a list of entries
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
+    var win = typeof successCallback !== 'function' ? null : function(result) {
+        var retVal = [];
+        for (var i=0; i<result.length; i++) {
+            var entry = null;
+            if (result[i].isDirectory) {
+                entry = new (require('cordova/plugin/DirectoryEntry'))();
+            }
+            else if (result[i].isFile) {
+                entry = new (require('cordova/plugin/FileEntry'))();
+            }
+            entry.isDirectory = result[i].isDirectory;
+            entry.isFile = result[i].isFile;
+            entry.name = result[i].name;
+            entry.fullPath = result[i].fullPath;
+            retVal.push(entry);
+        }
+        successCallback(retVal);
+    };
+    var fail = typeof errorCallback !== 'function' ? null : function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "readEntries", [this.path]);
+};
+
+module.exports = DirectoryReader;
+
+});
+
+// file: lib/common/plugin/Entry.js
+define("cordova/plugin/Entry", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError'),
+    Metadata = require('cordova/plugin/Metadata');
+
+/**
+ * Represents a file or directory on the local file system.
+ *
+ * @param isFile
+ *            {boolean} true if Entry is a file (readonly)
+ * @param isDirectory
+ *            {boolean} true if Entry is a directory (readonly)
+ * @param name
+ *            {DOMString} name of the file or directory, excluding the path
+ *            leading to it (readonly)
+ * @param fullPath
+ *            {DOMString} the absolute full path to the file or directory
+ *            (readonly)
+ */
+function Entry(isFile, isDirectory, name, fullPath, fileSystem) {
+    this.isFile = !!isFile;
+    this.isDirectory = !!isDirectory;
+    this.name = name || '';
+    this.fullPath = fullPath || '';
+    this.filesystem = fileSystem || null;
+}
+
+/**
+ * Look up the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ */
+Entry.prototype.getMetadata = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.getMetadata', arguments);
+    var success = successCallback && function(lastModified) {
+        var metadata = new Metadata(lastModified);
+        successCallback(metadata);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+
+    exec(success, fail, "File", "getMetadata", [this.fullPath]);
+};
+
+/**
+ * Set the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ * @param metadataObject
+ *            {Object} keys and values to set
+ */
+Entry.prototype.setMetadata = function(successCallback, errorCallback, metadataObject) {
+    argscheck.checkArgs('FFO', 'Entry.setMetadata', arguments);
+    exec(successCallback, errorCallback, "File", "setMetadata", [this.fullPath, metadataObject]);
+};
+
+/**
+ * Move a file or directory to a new location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to move this entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new DirectoryEntry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
+    argscheck.checkArgs('oSFF', 'Entry.moveTo', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    // source path
+    var srcPath = this.fullPath,
+        // entry name
+        name = newName || this.name,
+        success = function(entry) {
+            if (entry) {
+                if (successCallback) {
+                    // create appropriate Entry object
+                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
+                    successCallback(result);
+                }
+            }
+            else {
+                // no Entry object returned
+                fail && fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+
+    // copy
+    exec(success, fail, "File", "moveTo", [srcPath, parent.fullPath, name]);
+};
+
+/**
+ * Copy a directory to a different location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to copy the entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new Entry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
+    argscheck.checkArgs('oSFF', 'Entry.copyTo', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+
+        // source path
+    var srcPath = this.fullPath,
+        // entry name
+        name = newName || this.name,
+        // success callback
+        success = function(entry) {
+            if (entry) {
+                if (successCallback) {
+                    // create appropriate Entry object
+                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
+                    successCallback(result);
+                }
+            }
+            else {
+                // no Entry object returned
+                fail && fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+
+    // copy
+    exec(success, fail, "File", "copyTo", [srcPath, parent.fullPath, name]);
+};
+
+/**
+ * Return a URL that can be used to identify this entry.
+ */
+Entry.prototype.toURL = function() {
+    // fullPath attribute contains the full URL
+    return this.fullPath;
+};
+
+/**
+ * Returns a URI that can be used to identify this entry.
+ *
+ * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
+ * @return uri
+ */
+Entry.prototype.toURI = function(mimeType) {
+    console.log("DEPRECATED: Update your code to use 'toURL'");
+    // fullPath attribute contains the full URI
+    return this.toURL();
+};
+
+/**
+ * Remove a file or directory. It is an error to attempt to delete a
+ * directory that is not empty. It is an error to attempt to delete a
+ * root directory of a file system.
+ *
+ * @param successCallback {Function} called with no parameters
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.remove = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.remove', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, "File", "remove", [this.fullPath]);
+};
+
+/**
+ * Look up the parent DirectoryEntry of this entry.
+ *
+ * @param successCallback {Function} called with the parent DirectoryEntry object
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.getParent = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.getParent', arguments);
+    var win = successCallback && function(result) {
+        var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
+        var entry = new DirectoryEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getParent", [this.fullPath]);
+};
+
+module.exports = Entry;
+
+});
+
+// file: lib/common/plugin/File.js
+define("cordova/plugin/File", function(require, exports, module) {
+
+/**
+ * Constructor.
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+
+var File = function(name, fullPath, type, lastModifiedDate, size){
+    this.name = name || '';
+    this.fullPath = fullPath || null;
+    this.type = type || null;
+    this.lastModifiedDate = lastModifiedDate || null;
+    this.size = size || 0;
+
+    // These store the absolute start and end for slicing the file.
+    this.start = 0;
+    this.end = this.size;
+};
+
+/**
+ * Returns a "slice" of the file. Since Cordova Files don't contain the actual
+ * content, this really returns a File with adjusted start and end.
+ * Slices of slices are supported.
+ * start {Number} The index at which to start the slice (inclusive).
+ * end {Number} The index at which to end the slice (exclusive).
+ */
+File.prototype.slice = function(start, end) {
+    var size = this.end - this.start;
+    var newStart = 0;
+    var newEnd = size;
+    if (arguments.length) {
+        if (start < 0) {
+            newStart = Math.max(size + start, 0);
+        } else {
+            newStart = Math.min(size, start);
+        }
+    }
+
+    if (arguments.length >= 2) {
+        if (end < 0) {
+            newEnd = Math.max(size + end, 0);
+        } else {
+            newEnd = Math.min(end, size);
+        }
+    }
+
+    var newFile = new File(this.name, this.fullPath, this.type, this.lastModifiedData, this.size);
+    newFile.start = this.start + newStart;
+    newFile.end = this.start + newEnd;
+    return newFile;
+};
+
+
+module.exports = File;
+
+});
+
+// file: lib/common/plugin/FileEntry.js
+define("cordova/plugin/FileEntry", function(require, exports, module) {
+
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    Entry = require('cordova/plugin/Entry'),
+    FileWriter = require('cordova/plugin/FileWriter'),
+    File = require('cordova/plugin/File'),
+    FileError = require('cordova/plugin/FileError');
+
+/**
+ * An interface representing a file on the file system.
+ *
+ * {boolean} isFile always true (readonly)
+ * {boolean} isDirectory always false (readonly)
+ * {DOMString} name of the file, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the file (readonly)
+ * {FileSystem} filesystem on which the file resides (readonly)
+ */
+var FileEntry = function(name, fullPath) {
+     FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath]);
+};
+
+utils.extend(FileEntry, Entry);
+
+/**
+ * Creates a new FileWriter associated with the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new FileWriter
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.createWriter = function(successCallback, errorCallback) {
+    this.file(function(filePointer) {
+        var writer = new FileWriter(filePointer);
+
+        if (writer.fileName === null || writer.fileName === "") {
+            errorCallback && errorCallback(new FileError(FileError.INVALID_STATE_ERR));
+        } else {
+            successCallback && successCallback(writer);
+        }
+    }, errorCallback);
+};
+
+/**
+ * Returns a File that represents the current state of the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new File object
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.file = function(successCallback, errorCallback) {
+    var win = successCallback && function(f) {
+        var file = new File(f.name, f.fullPath, f.type, f.lastModifiedDate, f.size);
+        successCallback(file);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getFileMetadata", [this.fullPath]);
+};
+
+
+module.exports = FileEntry;
+
+});
+
+// file: lib/common/plugin/FileError.js
+define("cordova/plugin/FileError", function(require, exports, module) {
+
+/**
+ * FileError
+ */
+function FileError(error) {
+  this.code = error || null;
+}
+
+// File error codes
+// Found in DOMException
+FileError.NOT_FOUND_ERR = 1;
+FileError.SECURITY_ERR = 2;
+FileError.ABORT_ERR = 3;
+
+// Added by File API specification
+FileError.NOT_READABLE_ERR = 4;
+FileError.ENCODING_ERR = 5;
+FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
+FileError.INVALID_STATE_ERR = 7;
+FileError.SYNTAX_ERR = 8;
+FileError.INVALID_MODIFICATION_ERR = 9;
+FileError.QUOTA_EXCEEDED_ERR = 10;
+FileError.TYPE_MISMATCH_ERR = 11;
+FileError.PATH_EXISTS_ERR = 12;
+
+module.exports = FileError;
+
+});
+
+// file: lib/common/plugin/FileReader.js
+define("cordova/plugin/FileReader", function(require, exports, module) {
+
+var exec = require('cordova/exec'),
+    modulemapper = require('cordova/modulemapper'),
+    utils = require('cordova/utils'),
+    File = require('cordova/plugin/File'),
+    FileError = require('cordova/plugin/FileError'),
+    ProgressEvent = require('cordova/plugin/ProgressEvent'),
+    origFileReader = modulemapper.getOriginalSymbol(this, 'FileReader');
+
+/**
+ * This class reads the mobile device file system.
+ *
+ * For Android:
+ *      The root directory is the root of the file system.
+ *      To read from the SD card, the file name is "sdcard/my_file.txt"
+ * @constructor
+ */
+var FileReader = function() {
+    this._readyState = 0;
+    this._error = null;
+    this._result = null;
+    this._fileName = '';
+    this._realReader = origFileReader ? new origFileReader() : {};
+};
+
+// States
+FileReader.EMPTY = 0;
+FileReader.LOADING = 1;
+FileReader.DONE = 2;
+
+utils.defineGetter(FileReader.prototype, 'readyState', function() {
+    return this._fileName ? this._readyState : this._realReader.readyState;
+});
+
+utils.defineGetter(FileReader.prototype, 'error', function() {
+    return this._fileName ? this._error: this._realReader.error;
+});
+
+utils.defineGetter(FileReader.prototype, 'result', function() {
+    return this._fileName ? this._result: this._realReader.result;
+});
+
+function defineEvent(eventName) {
+    utils.defineGetterSetter(FileReader.prototype, eventName, function() {
+        return this._realReader[eventName] || null;
+    }, function(value) {
+        this._realReader[eventName] = value;
+    });
+}
+defineEvent('onloadstart');    // When the read starts.
+defineEvent('onprogress');     // While reading (and decoding) file or fileBlob data, and reporting partial file data (progress.loaded/progress.total)
+defineEvent('onload');         // When the read has successfully completed.
+defineEvent('onerror');        // When the read has failed (see errors).
+defineEvent('onloadend');      // When the request has completed (either in success or failure).
+defineEvent('onabort');        // When the read has been aborted. For instance, by invoking the abort() method.
+
+function initRead(reader, file) {
+    // Already loading something
+    if (reader.readyState == FileReader.LOADING) {
+      throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    reader._result = null;
+    reader._error = null;
+    reader._readyState = FileReader.LOADING;
+
+    if (typeof file == 'string') {
+        // Deprecated in Cordova 2.4.
+        console.warn('Using a string argument with FileReader.readAs functions is deprecated.');
+        reader._fileName = file;
+    } else if (typeof file.fullPath == 'string') {
+        reader._fileName = file.fullPath;
+    } else {
+        reader._fileName = '';
+        return true;
+    }
+
+    reader.onloadstart && reader.onloadstart(new ProgressEvent("loadstart", {target:reader}));
+}
+
+/**
+ * Abort reading file.
+ */
+FileReader.prototype.abort = function() {
+    if (origFileReader && !this._fileName) {
+        return this._realReader.abort();
+    }
+    this._result = null;
+
+    if (this._readyState == FileReader.DONE || this._readyState == FileReader.EMPTY) {
+      return;
+    }
+
+    this._readyState = FileReader.DONE;
+
+    // If abort callback
+    if (typeof this.onabort === 'function') {
+        this.onabort(new ProgressEvent('abort', {target:this}));
+    }
+    // If load end callback
+    if (typeof this.onloadend === 'function') {
+        this.onloadend(new ProgressEvent('loadend', {target:this}));
+    }
+};
+
+/**
+ * Read text file.
+ *
+ * @param file          {File} File object containing file properties
+ * @param encoding      [Optional] (see http://www.iana.org/assignments/character-sets)
+ */
+FileReader.prototype.readAsText = function(file, encoding) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsText(file, encoding);
+    }
+
+    // Default encoding is UTF-8
+    var enc = encoding ? encoding : "UTF-8";
+    var me = this;
+    var execArgs = [this._fileName, enc, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // Save result
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // null result
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsText", execArgs);
+};
+
+
+/**
+ * Read file and return data as a base64 encoded data url.
+ * A data url is of the form:
+ *      data:[<mediatype>][;base64],<data>
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsDataURL = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsDataURL(file);
+    }
+
+    var me = this;
+    var execArgs = [this._fileName, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // Save result
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsDataURL", execArgs);
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsBinaryString = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsBinaryString(file);
+    }
+
+    var me = this;
+    var execArgs = [this._fileName, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsBinaryString", execArgs);
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsArrayBuffer = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsArrayBuffer(file);
+    }
+
+    var me = this;
+    var execArgs = [this._fileName, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new Prog

<TRUNCATED>

[21/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/FileTransfer.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/FileTransfer.cs b/lib/cordova-wp8/templates/standalone/Plugins/FileTransfer.cs
new file mode 100644
index 0000000..e585895
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/FileTransfer.cs
@@ -0,0 +1,526 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Net;
+using System.Runtime.Serialization;
+using System.Windows;
+using System.Security;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public class FileTransfer : BaseCommand
+    {
+        public class DownloadRequestState
+        {
+            // This class stores the State of the request.
+            public HttpWebRequest request;
+            public DownloadOptions options;
+
+            public DownloadRequestState()
+            {
+                request = null;
+                options = null;
+            }
+        }
+
+        /// <summary>
+        /// Boundary symbol
+        /// </summary>       
+        private string Boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");
+
+        // Error codes
+        public const int FileNotFoundError = 1;
+        public const int InvalidUrlError = 2;
+        public const int ConnectionError = 3;
+
+        /// <summary>
+        /// Options for downloading file
+        /// </summary>
+        [DataContract]
+        public class DownloadOptions
+        {
+            /// <summary>
+            /// File path to download to
+            /// </summary>
+            [DataMember(Name = "filePath", IsRequired = true)]
+            public string FilePath { get; set; }
+
+            /// <summary>
+            /// Server address to the file to download
+            /// </summary>
+            [DataMember(Name = "url", IsRequired = true)]
+            public string Url { get; set; }
+        }
+
+        /// <summary>
+        /// Options for uploading file
+        /// </summary>
+        [DataContract]
+        public class UploadOptions
+        {
+            /// <summary>
+            /// File path to upload
+            /// </summary>
+            [DataMember(Name = "filePath", IsRequired = true)]
+            public string FilePath { get; set; }
+
+            /// <summary>
+            /// Server address
+            /// </summary>
+            [DataMember(Name = "server", IsRequired = true)]
+            public string Server { get; set; }
+
+            /// <summary>
+            /// File key
+            /// </summary>
+            [DataMember(Name = "fileKey")]
+            public string FileKey { get; set; }
+
+            /// <summary>
+            /// File name on the server
+            /// </summary>
+            [DataMember(Name = "fileName")]
+            public string FileName { get; set; }
+
+            /// <summary>
+            /// File Mime type
+            /// </summary>
+            [DataMember(Name = "mimeType")]
+            public string MimeType { get; set; }
+
+
+            /// <summary>
+            /// Additional options
+            /// </summary>
+            [DataMember(Name = "params")]
+            public string Params { get; set; }
+
+            /// <summary>
+            /// Flag to recognize if we should trust every host (only in debug environments)
+            /// </summary>
+            [DataMember(Name = "debug")]
+            public bool Debug { get; set; }
+
+            /// <summary>
+            /// Creates options object with default parameters
+            /// </summary>
+            public UploadOptions()
+            {
+                this.SetDefaultValues(new StreamingContext());
+            }
+
+            /// <summary>
+            /// Initializes default values for class fields.
+            /// Implemented in separate method because default constructor is not invoked during deserialization.
+            /// </summary>
+            /// <param name="context"></param>
+            [OnDeserializing()]
+            public void SetDefaultValues(StreamingContext context)
+            {
+                this.FileKey = "file";
+                this.FileName = "image.jpg";
+                this.MimeType = "image/jpeg";
+            }
+
+        }
+
+        /// <summary>
+        /// Uploading response info
+        /// </summary>
+        [DataContract]
+        public class FileUploadResult
+        {
+            /// <summary>
+            /// Amount of sent bytes
+            /// </summary>
+            [DataMember(Name = "bytesSent")]
+            public long BytesSent { get; set; }
+
+            /// <summary>
+            /// Server response code
+            /// </summary>
+            [DataMember(Name = "responseCode")]
+            public long ResponseCode { get; set; }
+
+            /// <summary>
+            /// Server response
+            /// </summary>
+            [DataMember(Name = "response", EmitDefaultValue = false)]
+            public string Response { get; set; }
+
+            /// <summary>
+            /// Creates FileUploadResult object with response values
+            /// </summary>
+            /// <param name="bytesSent">Amount of sent bytes</param>
+            /// <param name="responseCode">Server response code</param>
+            /// <param name="response">Server response</param>
+            public FileUploadResult(long bytesSent, long responseCode, string response)
+            {
+                this.BytesSent = bytesSent;
+                this.ResponseCode = responseCode;
+                this.Response = response;
+            }
+        }
+
+        /// <summary>
+        /// Represents transfer error codes for callback
+        /// </summary>
+        [DataContract]
+        public class FileTransferError
+        {
+            /// <summary>
+            /// Error code
+            /// </summary>
+            [DataMember(Name = "code", IsRequired = true)]
+            public int Code { get; set; }
+
+            /// <summary>
+            /// The source URI
+            /// </summary>
+            [DataMember(Name = "source", IsRequired = true)]
+            public string Source { get; set; }
+
+            /// <summary>
+            /// The target URI
+            /// </summary>
+            [DataMember(Name = "target", IsRequired = true)]
+            public string Target { get; set; }
+
+            /// <summary>
+            /// The http status code response from the remote URI
+            /// </summary>
+            [DataMember(Name = "http_status", IsRequired = true)]
+            public int HttpStatus { get; set; }
+
+            /// <summary>
+            /// Creates FileTransferError object
+            /// </summary>
+            /// <param name="errorCode">Error code</param>
+            public FileTransferError(int errorCode)
+            {
+                this.Code = errorCode;
+                this.Source = null;
+                this.Target = null;
+                this.HttpStatus = 0;
+            }
+            public FileTransferError(int errorCode, string source, string target, int status)
+            {
+                this.Code = errorCode;
+                this.Source = source;
+                this.Target = target;
+                this.HttpStatus = status;
+            }
+        }
+
+        /// <summary>
+        /// Upload options
+        /// </summary>
+        private UploadOptions uploadOptions;
+
+        /// <summary>
+        /// Bytes sent
+        /// </summary>
+        private long bytesSent;
+
+        /// <summary>
+        /// sends a file to a server
+        /// </summary>
+        /// <param name="options">Upload options</param>
+        public void upload(string options)
+        {
+            Debug.WriteLine("options = " + options);
+            options = options.Replace("{}", "null");
+
+            try 
+            {
+                try 
+                {
+                    string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                    uploadOptions = JSON.JsonHelper.Deserialize<UploadOptions>(args[0]);
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                Uri serverUri;
+                try
+                {
+                    serverUri = new Uri(uploadOptions.Server);
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(InvalidUrlError, uploadOptions.Server, null, 0)));
+                    return;
+                }
+                HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(serverUri);
+                webRequest.ContentType = "multipart/form-data;boundary=" + Boundary;
+                webRequest.Method = "POST";
+                webRequest.BeginGetRequestStream(WriteCallback, webRequest);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)));
+            }
+        }
+
+        public void download(string options)
+        {
+            DownloadOptions downloadOptions = null;
+            HttpWebRequest webRequest = null;
+
+            try
+            {
+                string[] optionStrings = JSON.JsonHelper.Deserialize<string[]>(options);
+
+                downloadOptions = new DownloadOptions();// JSON.JsonHelper.Deserialize<DownloadOptions>(options);
+                downloadOptions.Url = optionStrings[0];
+                downloadOptions.FilePath = optionStrings[1];
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                webRequest = (HttpWebRequest)WebRequest.Create(downloadOptions.Url);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(InvalidUrlError, downloadOptions.Url, null, 0)));
+                return;
+            }
+
+            if (downloadOptions != null && webRequest != null)
+            {
+                DownloadRequestState state = new DownloadRequestState();
+                state.options = downloadOptions;
+                state.request = webRequest;
+                webRequest.BeginGetResponse(new AsyncCallback(downloadCallback), state);
+            }
+
+
+
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="asynchronousResult"></param>
+        private void downloadCallback(IAsyncResult asynchronousResult)
+        {
+            DownloadRequestState reqState = (DownloadRequestState)asynchronousResult.AsyncState;
+            HttpWebRequest request = reqState.request;
+
+            try
+            {
+                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    // create the file if not exists
+                    if (!isoFile.FileExists(reqState.options.FilePath))
+                    {
+                        var file = isoFile.CreateFile(reqState.options.FilePath);
+                        file.Close();
+                    }
+
+                    using (FileStream fileStream = new IsolatedStorageFileStream(reqState.options.FilePath, FileMode.Open, FileAccess.Write, isoFile))
+                    {
+                        long totalBytes = response.ContentLength;
+                        int bytesRead = 0;
+                        using (BinaryReader reader = new BinaryReader(response.GetResponseStream()))
+                        {
+
+                            using (BinaryWriter writer = new BinaryWriter(fileStream))
+                            {
+                                int BUFFER_SIZE = 1024;
+                                byte[] buffer;
+
+                                while (true)
+                                {
+                                    buffer = reader.ReadBytes(BUFFER_SIZE);
+                                    // fire a progress event ?
+                                    bytesRead += buffer.Length;
+                                    if (buffer.Length > 0)
+                                    {
+                                        writer.Write(buffer);
+                                    }
+                                    else
+                                    {
+                                        writer.Close();
+                                        reader.Close();
+                                        fileStream.Close();
+                                        break;
+                                    }
+                                }
+                            }
+
+                        }
+
+
+                    }
+                }
+                WPCordovaClassLib.Cordova.Commands.File.FileEntry entry = new WPCordovaClassLib.Cordova.Commands.File.FileEntry(reqState.options.FilePath);
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry));
+            }
+            catch (IsolatedStorageException)
+            {
+                // Trying to write the file somewhere within the IsoStorage.
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)));
+            }
+            catch (SecurityException)
+            {
+                // Trying to write the file somewhere not allowed.
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)));
+            }
+            catch (WebException webex)
+            {
+                // TODO: probably need better work here to properly respond with all http status codes back to JS
+                // Right now am jumping through hoops just to detect 404.
+                if ((webex.Status == WebExceptionStatus.ProtocolError && ((HttpWebResponse)webex.Response).StatusCode == HttpStatusCode.NotFound) || webex.Status == WebExceptionStatus.UnknownError)
+                {
+                    // Weird MSFT detection of 404... seriously... just give us the f(*&#$@ status code as a number ffs!!!
+                    // "Numbers for HTTP status codes? Nah.... let's create our own set of enums/structs to abstract that stuff away."
+                    // FACEPALM
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError, null, null, 404)));
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)));
+                }
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)));
+            }
+        }
+
+
+
+        /// <summary>
+        /// Read file from Isolated Storage and sends it to server
+        /// </summary>
+        /// <param name="asynchronousResult"></param>
+        private void WriteCallback(IAsyncResult asynchronousResult)
+        {
+            try
+            {
+                HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
+                using (Stream requestStream = (webRequest.EndGetRequestStream(asynchronousResult)))
+                {
+                    string lineStart = "--";
+                    string lineEnd = Environment.NewLine;
+                    byte[] boundaryBytes = System.Text.Encoding.UTF8.GetBytes(lineStart + Boundary + lineEnd);
+                    string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"" + lineEnd + lineEnd + "{1}" + lineEnd;
+
+                    if (uploadOptions.Params != null)
+                    {
+
+                        string[] arrParams = uploadOptions.Params.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
+
+                        foreach (string param in arrParams)
+                        {
+                            string[] split = param.Split('=');
+                            string key = split[0];
+                            string val = split[1];
+                            requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
+                            string formItem = string.Format(formdataTemplate, key, val);
+                            byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes(formItem);
+                            requestStream.Write(formItemBytes, 0, formItemBytes.Length);
+                        }
+                        requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
+                    }
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (!isoFile.FileExists(uploadOptions.FilePath))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError, uploadOptions.Server, uploadOptions.FilePath, 0)));
+                            return;
+                        }
+
+                        using (FileStream fileStream = new IsolatedStorageFileStream(uploadOptions.FilePath, FileMode.Open, isoFile))
+                        {
+                            string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + lineEnd + "Content-Type: {2}" + lineEnd + lineEnd;
+                            string header = string.Format(headerTemplate, uploadOptions.FileKey, uploadOptions.FileName, uploadOptions.MimeType);
+                            byte[] headerBytes = System.Text.Encoding.UTF8.GetBytes(header);
+                            requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
+                            requestStream.Write(headerBytes, 0, headerBytes.Length);
+                            byte[] buffer = new byte[4096];
+                            int bytesRead = 0;
+
+                            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
+                            {
+                                requestStream.Write(buffer, 0, bytesRead);
+                                bytesSent += bytesRead;
+                            }
+                        }
+                        byte[] endRequest = System.Text.Encoding.UTF8.GetBytes(lineEnd + lineStart + Boundary + lineStart + lineEnd);
+                        requestStream.Write(endRequest, 0, endRequest.Length);
+                    }
+                }
+                webRequest.BeginGetResponse(ReadCallback, webRequest);
+            }
+            catch (Exception)
+            {
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)));
+                });
+            }
+        }
+
+        /// <summary>
+        /// Reads response into FileUploadResult
+        /// </summary>
+        /// <param name="asynchronousResult"></param>
+        private void ReadCallback(IAsyncResult asynchronousResult)
+        {
+            try
+            {
+                HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
+                using (HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult))
+                {
+                    using (Stream streamResponse = response.GetResponseStream())
+                    {
+                        using (StreamReader streamReader = new StreamReader(streamResponse))
+                        {
+                            string responseString = streamReader.ReadToEnd();
+                            Deployment.Current.Dispatcher.BeginInvoke(() =>
+                            {
+                                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileUploadResult(bytesSent, (long)response.StatusCode, responseString)));
+                            });
+                        }
+                    }
+                }
+            }
+            catch (Exception)
+            {
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    FileTransferError transferError = new FileTransferError(ConnectionError, uploadOptions.Server, uploadOptions.FilePath, 403);
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, transferError));
+                });
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/GeoLocation.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/GeoLocation.cs b/lib/cordova-wp8/templates/standalone/Plugins/GeoLocation.cs
new file mode 100644
index 0000000..c53cb29
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/GeoLocation.cs
@@ -0,0 +1,34 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Threading;
+using System.Device.Location;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// This is a command stub, the browser provides the correct implementation.  We use this to trigger the static analyzer that we require this permission 
+    /// </summary>
+    public class GeoLocation
+    {
+        /* Unreachable code, by design -jm */
+        private void triggerGeoInclusion()
+        {
+            new GeoCoordinateWatcher();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/Globalization.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/Globalization.cs b/lib/cordova-wp8/templates/standalone/Plugins/Globalization.cs
new file mode 100644
index 0000000..2c2f468
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/Globalization.cs
@@ -0,0 +1,1177 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Provides information about system locale, culture settings, number formats, ect.
+    /// </summary>
+    public class Globalization : BaseCommand
+    {
+
+        #region Globalization errors
+
+        /// <summary>
+        /// Globalization error codes.
+        /// </summary>
+        public enum ErrorCode : int
+        {
+            UnknownError = 0,
+            FormattingError = 1,
+            ParsingError = 2,
+            PatternError = 3
+        }
+
+        /// <summary>
+        /// Represents globalization error object.
+        /// </summary>
+        [DataContract]
+        public class GlobalizationError
+        {
+            #region Error messages
+            /// <summary>
+            /// Error messages
+            /// </summary>        
+            public const string UnknownError = "UNKNOWN_ERROR";
+            public const string FormattingError = "FORMATTIN_ERROR";
+            public const string ParsingError = "PARSING_ERROR";
+            public const string PatternError = "PATTERN_ERROR";
+
+            #endregion
+
+            /// <summary>
+            /// Error code
+            /// </summary>
+            [DataMember(Name = "code", IsRequired = false)]
+            public ErrorCode Code { get; set; }
+
+            /// <summary>
+            /// Error message
+            /// </summary>
+            [DataMember(Name = "message", IsRequired = false)]
+            public string Message { get; set; }
+
+            /// <summary>
+            /// Default constructor
+            /// </summary>
+            public GlobalizationError()
+            {
+                this.Code = ErrorCode.UnknownError;
+                this.Message = UnknownError;
+            }
+
+            /// <summary>
+            /// Constructor setting error code
+            /// </summary>
+            public GlobalizationError(ErrorCode error)
+            {
+                this.Code = error;
+
+                switch (error)
+                {
+                    case ErrorCode.ParsingError:
+                        {
+                            this.Message = ParsingError;
+                            break;
+                        }
+                    case ErrorCode.FormattingError:
+                        {
+                            this.Message = FormattingError;
+                            break;
+                        }
+                    case ErrorCode.PatternError:
+                        {
+                            this.Message = PatternError;
+                            break;
+                        }
+                    default:
+                        {
+                            this.Message = UnknownError;
+                            break;
+                        }
+                }
+            }
+        }
+
+        #endregion
+
+        #region Globalization options
+
+        /// <summary>
+        /// Represents globalization options.
+        /// </summary>
+        [DataContract]
+        public class GlobalizationOptions
+        {
+            #region available option values
+            /// <summary>
+            /// Number pattern types.
+            /// </summary>        
+            public const string Percent = "percent";
+            public const string Currency = "currency";
+            public const string Decimal = "decimal";
+
+            /// <summary>
+            /// Format length types
+            /// </summary>        
+            public const string Short = "short";
+            public const string Medium = "medium";
+            public const string Long = "long";
+            public const string Full = "full";
+
+            /// <summary>
+            /// Selector types
+            /// </summary>        
+            public const string TimeSelector = "time";
+            public const string DateSelector = "date";
+            public const string DateAndTimeSelector = "date and time";
+
+            /// <summary>
+            /// Date name types
+            /// </summary>        
+            public const string Narrow = "narrow";
+            public const string Wide = "wide";
+
+            /// <summary>
+            /// Date name items
+            /// </summary>        
+            public const string Months = "months";
+            public const string Days = "days";
+
+            #endregion
+
+            /// <summary>
+            /// Additional options
+            /// </summary>
+            [DataMember(Name = "options", IsRequired = false)]
+            public Options AdditionalOptions { get; set; }
+
+            /// <summary>
+            /// Date to convert
+            /// </summary>
+            [DataMember(Name = "date", IsRequired = false)]
+            public long Date { get; set; }
+
+            /// <summary>
+            /// Date as stirng
+            /// </summary>
+            [DataMember(Name = "dateString", IsRequired = false)]
+            public string DateString { get; set; }
+
+            /// <summary>
+            /// Currency code
+            /// </summary>
+            [DataMember(Name = "currencyCode", IsRequired = false)]
+            public string CurrencyCode { get; set; }
+
+            /// <summary>
+            /// Number as string
+            /// </summary>
+            [DataMember(Name = "numberString", IsRequired = false)]
+            public string NumberString { get; set; }
+
+            /// <summary>
+            /// Number to convert
+            /// </summary>
+            [DataMember(Name = "number", IsRequired = false)]
+            public double Number { get; set; }
+        }
+
+        /// <summary>
+        /// Represents additional options
+        /// </summary>
+        [DataContract]
+        public class Options
+        {
+            /// <summary>
+            /// Pattern type
+            /// </summary>
+            [DataMember(Name = "type", IsRequired = false)]
+            public string Type { get; set; }
+
+            /// <summary>
+            /// Format length
+            /// </summary>
+            [DataMember(Name = "formatLength", IsRequired = false)]
+            public string FormatLength { get; set; }
+
+            /// <summary>
+            /// Selector
+            /// </summary>
+            [DataMember(Name = "selector", IsRequired = false)]
+            public string Selector { get; set; }
+
+            /// <summary>
+            /// Date name item
+            /// </summary>
+            [DataMember(Name = "item", IsRequired = false)]
+            public string Item { get; set; }
+        }
+
+        #endregion
+
+        #region returned objects
+
+        #region Number pattern object
+
+        /// <summary>
+        /// Represents number pattern
+        /// </summary>
+        [DataContract]
+        public class NumberPattern
+        {
+            /// <summary>
+            /// Pattern
+            /// </summary>
+            [DataMember(Name = "pattern", IsRequired = false)]
+            public string Pattern { get; set; }
+
+            /// <summary>
+            /// Symbol
+            /// </summary>
+            [DataMember(Name = "symbol", IsRequired = false)]
+            public string Symbol { get; set; }
+
+            /// <summary>
+            /// Fraction
+            /// </summary>
+            [DataMember(Name = "fraction", IsRequired = false)]
+            public int Fraction { get; set; }
+
+            /// <summary>
+            /// Positive
+            /// </summary>
+            [DataMember(Name = "positive", IsRequired = false)]
+            public string Positive { get; set; }
+
+            /// <summary>
+            /// Negative
+            /// </summary>
+            [DataMember(Name = "negative", IsRequired = false)]
+            public string Negative { get; set; }
+
+            /// <summary>
+            /// Rounding
+            /// </summary>
+            [DataMember(Name = "rounding", IsRequired = false)]
+            public int Rounding { get; set; }
+
+            /// <summary>
+            /// Decimal
+            /// </summary>
+            [DataMember(Name = "decimal", IsRequired = false)]
+            public string Decimal { get; set; }
+
+            /// <summary>
+            /// Grouping
+            /// </summary>
+            [DataMember(Name = "grouping", IsRequired = false)]
+            public string Grouping { get; set; }
+
+            /// <summary>
+            /// Constructor of the class
+            /// </summary>
+            /// <param name="pattern"></param>
+            /// <param name="symbol"></param>
+            /// <param name="fraction"></param>
+            /// <param name="positive"></param>
+            /// <param name="negative"></param>
+            /// <param name="rounding"></param>
+            /// <param name="dec"></param>
+            /// <param name="grouping"></param>
+            public NumberPattern(string pattern, string symbol, int fraction, string positive, string negative, int rounding, string dec, string grouping)
+            {
+                this.Pattern = pattern;
+                this.Symbol = symbol;
+                this.Fraction = fraction;
+                this.Positive = positive;
+                this.Negative = negative;
+                this.Rounding = rounding;
+                this.Decimal = dec;
+                this.Grouping = grouping;
+            }
+        }
+        #endregion
+
+        #region Date format object
+
+        /// <summary>
+        /// Represents date format
+        /// </summary>
+        [DataContract]
+        public class DateFormat
+        {
+            /// <summary>
+            /// Year
+            /// </summary>
+            [DataMember(Name = "year", IsRequired = false)]
+            public int Year { get; set; }
+
+            /// <summary>
+            /// Month
+            /// </summary>
+            [DataMember(Name = "month", IsRequired = false)]
+            public int Month { get; set; }
+
+            /// <summary>
+            /// Day
+            /// </summary>
+            [DataMember(Name = "day", IsRequired = false)]
+            public int Day { get; set; }
+
+            /// <summary>
+            /// Hour
+            /// </summary>
+            [DataMember(Name = "hour", IsRequired = false)]
+            public int Hour { get; set; }
+
+            /// <summary>
+            /// Minute
+            /// </summary>
+            [DataMember(Name = "minute", IsRequired = false)]
+            public int Minute { get; set; }
+
+            /// <summary>
+            /// Second
+            /// </summary>
+            [DataMember(Name = "second", IsRequired = false)]
+            public int Second { get; set; }
+
+            /// <summary>
+            /// Millisecond
+            /// </summary>
+            [DataMember(Name = "millisecond", IsRequired = false)]
+            public int Millisecond { get; set; }
+
+            public DateFormat(int year, int month, int day, int hour, int minute, int second, int millisecond)
+            {
+                this.Year = year;
+                this.Month = month;
+                this.Day = day;
+                this.Hour = hour;
+                this.Minute = minute;
+                this.Millisecond = millisecond;
+            }
+
+        }
+        #endregion
+
+        #region Date pattern object
+
+        /// <summary>
+        /// Represents date pattern object
+        /// </summary>
+        [DataContract]
+        public class DatePattern
+        {
+
+            /// <summary>
+            /// Date pattern
+            /// </summary>
+            [DataMember(Name = "pattern", IsRequired = false)]
+            public string Pattern { get; set; }
+
+            /// <summary>
+            /// TimeZone
+            /// </summary>
+            [DataMember(Name = "timezone", IsRequired = false)]
+            public string TimeZone { get; set; }
+
+            /// <summary>
+            /// UTC offset
+            /// </summary>
+            [DataMember(Name = "utc_offset", IsRequired = false)]
+            public double UtcOffset { get; set; }
+
+            /// <summary>
+            /// Dst offset
+            /// </summary>
+            [DataMember(Name = "dst_offset", IsRequired = false)]
+            public double DstOffset { get; set; }
+
+            /// <summary>
+            /// Constructor of the class
+            /// </summary>
+            /// <param name="pattern"></param>
+            /// <param name="timezone"></param>
+            /// <param name="utcOffset"></param>
+            /// <param name="dstOffset"></param>
+            public DatePattern(string pattern, string timezone, double utcOffset, double dstOffset)
+            {
+                this.Pattern = pattern;
+                this.TimeZone = timezone;
+                this.UtcOffset = utcOffset;
+                this.DstOffset = dstOffset;
+            }
+
+        }
+
+        #endregion
+
+        #endregion
+
+        #region Locale info
+
+        /// <summary>
+        /// Gets the string identifier for the client's current locale setting.
+        /// </summary>
+        /// <param name="options"></param>               
+        public void getLocaleName(string options)
+        {
+            try
+            {
+                var locale = RegionInfo.CurrentRegion.TwoLetterISORegionName;
+                PluginResult result = new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(locale));
+                this.DispatchCommandResult(result);
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+            }
+        }
+
+        /// <summary>
+        /// Gets the string identifier for the client's current language.
+        /// </summary>
+        /// <param name="options"></param>               
+        public void getPreferredLanguage(string options)
+        {
+            try
+            {
+                var language = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
+                PluginResult result = new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(language));
+                this.DispatchCommandResult(result);
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+            }
+        }
+
+        #endregion
+
+        #region Date and time info
+
+        /// <summary>
+        /// Gets whether daylight savings time is in effect for a given date using the client's 
+        /// time zone and calendar.        
+        /// </summary>
+        /// <param name="opitons">Date to daylight savings check.</param>
+        public void isDayLightSavingsTime(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                DateTime start = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+                DateTime date = start.AddMilliseconds(globalOptions.Date).ToLocalTime();
+                TimeZoneInfo localZone = TimeZoneInfo.Local;
+                bool isDaylightSavingTime = localZone.IsDaylightSavingTime(date);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(isDaylightSavingTime, "dst")));
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+            }
+        }
+
+        /// <summary>
+        /// Gets the first day of the week according to the client's user preferences and calendar.
+        /// The days of the week are numbered starting from 1 where 1 is considered to be Sunday.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getFirstDayOfWeek(string options)
+        {
+            try
+            {
+                // DateTimeFormat returns days of the week numbered from zero, so we have to increase returned value by one.
+                var firstDayOfWeek = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek + 1;
+                PluginResult result = new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(firstDayOfWeek));
+                this.DispatchCommandResult(result);
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+            }
+        }
+
+        #endregion
+
+        #region Formatting
+
+        /// <summary>
+        /// Gets a date formatted as a string according to the client's user preferences and calendar using the time zone of the client. 
+        /// </summary>
+        /// <param name="options"></param>
+        public void dateToString(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                DateTime start = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+                DateTime date = start.AddMilliseconds(globalOptions.Date).ToLocalTime();
+
+                string format = "{0:M/dd/yy H:m:s}"; //short datetime by default
+                int formatLength = 0; //default format
+                int selector = 0; //default selector 
+
+                if (globalOptions.AdditionalOptions != null)
+                {
+                    if (globalOptions.AdditionalOptions.FormatLength != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.FormatLength;
+
+                        if (t.Equals(GlobalizationOptions.Full))
+                        {
+                            formatLength++;
+                        }
+                    }
+
+                    if (globalOptions.AdditionalOptions.Selector != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.Selector;
+
+                        if (t.Equals(GlobalizationOptions.DateSelector))
+                        {
+                            selector += 10;
+                        }
+                        else if (t.Equals(GlobalizationOptions.TimeSelector))
+                        {
+                            selector += 20;
+                        }
+                    }
+
+                    //determine return value
+                    int method = formatLength + selector;
+
+                    switch (method)
+                    {
+                        case 1: // full datetime
+                            {
+                                format = "{0:MMMM/dddd/yyyy HH:mm:ss tt}";
+                                break;
+                            }
+                        case 10: // short date
+                            {
+                                format = "{0:d}";
+                                break;
+                            }
+                        case 11: // full date
+                            {
+                                format = "{0:D}";
+                                break;
+                            }
+                        case 20: // short time
+                            {
+                                format = "{0:t}";
+                                break;
+                            }
+                        case 21: // full time
+                            {
+                                format = "{0:T}";
+                                break;
+                            }
+                        default: // short datetime
+                            {
+                                format = "{0:M/dd/yy H:m:s}";
+                                break;
+                            }
+                    }
+                }
+
+                string formattedValue = string.Format(CultureInfo.CurrentCulture, format, date);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(formattedValue)));
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.FormattingError)));
+            }
+        }
+
+        /// <summary>
+        /// Parses a date formatted as a string according to the client's user preferences and calendar using the time zone of the client and returns the corresponding date object
+        /// </summary>
+        /// <param name="options"></param>
+        public void stringToDate(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                if (string.IsNullOrEmpty(globalOptions.DateString))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                string format = "M/dd/yy H:m:s"; // short datetime by default
+                int formatLength = 0; //default format
+                int selector = 0; //default selector 
+
+                if (globalOptions.AdditionalOptions != null)
+                {
+                    if (globalOptions.AdditionalOptions.FormatLength != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.FormatLength;
+
+                        if (t.Equals(GlobalizationOptions.Full))
+                        {
+                            formatLength++;
+                        }
+                    }
+
+                    if (globalOptions.AdditionalOptions.Selector != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.Selector;
+
+                        if (t.Equals(GlobalizationOptions.DateSelector))
+                        {
+                            selector += 10;
+                        }
+                        else if (t.Equals(GlobalizationOptions.TimeSelector))
+                        {
+                            selector += 20;
+                        }
+                    }
+
+                    //determine return value
+                    int method = formatLength + selector;
+
+                    switch (method)
+                    {
+                        case 1: // full datetime
+                            {
+                                format = "MMMM/dddd/yyyy HH:mm:ss tt";
+                                break;
+                            }
+                        case 10: // short date
+                            {
+                                format = "d";
+                                break;
+                            }
+                        case 11: // full date
+                            {
+                                format = "D";
+                                break;
+                            }
+                        case 20: // short time
+                            {
+                                format = "t";
+                                break;
+                            }
+                        case 21: // full time
+                            {
+                                format = "T";
+                                break;
+                            }
+                        default: // short datetime
+                            {
+                                format = "M/dd/yy H:m:s";
+                                break;
+                            }
+                    }
+                }
+
+                DateTime date = DateTime.ParseExact(globalOptions.DateString, format, CultureInfo.CurrentCulture);
+                DateFormat dateFormat = new DateFormat(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Millisecond);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, dateFormat));
+
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.ParsingError)));
+            }
+        }
+
+        /// <summary>
+        /// Gets a pattern string for formatting and parsing dates according to the client's user preferences.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getDatePattern(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                DateTimeFormatInfo dateFormatInfo = DateTimeFormatInfo.CurrentInfo;
+                string pattern = dateFormatInfo.FullDateTimePattern; // full datetime by default
+                int formatLength = 0; //default format
+                int selector = 0; //default selector 
+
+                if (globalOptions.AdditionalOptions != null)
+                {
+                    if (globalOptions.AdditionalOptions.FormatLength != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.FormatLength;
+
+                        if (t.Equals(GlobalizationOptions.Full))
+                        {
+                            formatLength++;
+                        }
+                    }
+
+                    if (globalOptions.AdditionalOptions.Selector != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.Selector;
+
+                        if (t.Equals(GlobalizationOptions.DateSelector))
+                        {
+                            selector += 10;
+                        }
+                        else if (t.Equals(GlobalizationOptions.TimeSelector))
+                        {
+                            selector += 20;
+                        }
+                    }
+
+                    //determine return value
+                    int method = formatLength + selector;
+
+                    switch (method)
+                    {
+                        case 1: // full datetime
+                            {
+                                pattern = dateFormatInfo.FullDateTimePattern;
+                                break;
+                            }
+                        case 10: // short date
+                            {
+                                pattern = dateFormatInfo.ShortDatePattern;
+                                break;
+                            }
+                        case 11: // full date
+                            {
+                                pattern = dateFormatInfo.LongDatePattern;
+                                break;
+                            }
+                        case 20: // short time
+                            {
+                                pattern = dateFormatInfo.ShortTimePattern;
+                                break;
+                            }
+                        case 21: // full time
+                            {
+                                pattern = dateFormatInfo.LongTimePattern;
+                                break;
+                            }
+                        default: // short datetime
+                            {
+                                // Seems like C# doesn't support short datetime pattern so we use full format
+                                // http://msdn.microsoft.com/en-us/library/1at0z4ew%28v=vs.71%29.aspx
+                                pattern = dateFormatInfo.FullDateTimePattern;
+                                break;
+                            }
+                    }
+                }
+
+                TimeZoneInfo localZone = TimeZoneInfo.Local;
+                DatePattern datePattern = new DatePattern(pattern, localZone.DisplayName, localZone.BaseUtcOffset.TotalSeconds, 0);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, datePattern));
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.PatternError)));
+            }
+        }
+
+        /// <summary>
+        /// Gets an array of either the names of the months or days of the week according to the client's user preferences and calendar.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getDateNames(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                int type = 0; //default wide
+                int item = 0; //default months 
+
+                if (globalOptions.AdditionalOptions != null)
+                {
+                    if (globalOptions.AdditionalOptions.Type != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.Type;
+
+                        if (t.Equals(GlobalizationOptions.Narrow))
+                        {
+                            type++;
+                        }
+                    }
+
+                    if (globalOptions.AdditionalOptions.Item != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.Item;
+
+                        if (t.Equals(GlobalizationOptions.Days))
+                        {
+                            item += 10;
+                        }
+                    }
+                }
+
+                //determine return value
+                int method = item + type;
+                string[] namesArray;
+                CultureInfo currentCulture = CultureInfo.CurrentCulture;
+
+                if (method == 1) //months and narrow
+                {
+                    namesArray = currentCulture.DateTimeFormat.AbbreviatedMonthNames;
+                }
+                else if (method == 10) //days and wide
+                {
+                    namesArray = currentCulture.DateTimeFormat.DayNames;
+                }
+                else if (method == 11) //days and narrow
+                {
+                    namesArray = currentCulture.DateTimeFormat.AbbreviatedDayNames;
+                }
+                else //default: months and wide
+                {
+                    namesArray = currentCulture.DateTimeFormat.MonthNames;
+                }
+
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(namesArray)));
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+            }
+        }
+
+        /// <summary>
+        /// Gets a number formatted as a string according to the client's user preferences. 
+        /// </summary>
+        /// <param name="options"></param>
+        public void numberToString(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                string format = string.Empty;
+                string numberFormatType = (globalOptions.AdditionalOptions == null || string.IsNullOrEmpty(globalOptions.AdditionalOptions.Type)) ?
+                    GlobalizationOptions.Decimal : globalOptions.AdditionalOptions.Type;
+
+                switch (numberFormatType)
+                {
+                    case GlobalizationOptions.Percent:
+                        {
+                            format = "{0:p}";
+                            break;
+                        }
+
+                    case GlobalizationOptions.Currency:
+                        {
+                            format = "{0:c}";
+                            break;
+                        }
+
+                    default:
+                        {
+                            format = "{0:f}";
+                            break;
+                        }
+                }
+
+                string formattedValue = string.Format(CultureInfo.CurrentCulture, format, globalOptions.Number);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(formattedValue)));
+
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.FormattingError)));
+            }
+        }
+
+        /// <summary>
+        /// Gets a number formatted as a string according to the client's user preferences and returns the corresponding number.
+        /// </summary>
+        /// <param name="options"></param>
+        public void stringToNumber(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                if (string.IsNullOrEmpty(globalOptions.NumberString))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                string numberString = globalOptions.NumberString;
+                string numberFormatType = (globalOptions.AdditionalOptions == null || string.IsNullOrEmpty(globalOptions.AdditionalOptions.Type)) ?
+                    GlobalizationOptions.Decimal : globalOptions.AdditionalOptions.Type;
+
+                NumberStyles numberStyle;
+
+                switch (numberFormatType)
+                {
+                    case GlobalizationOptions.Percent:
+                        {
+                            numberStyle = NumberStyles.Any;
+                            numberString = numberString.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, "");
+                            break;
+                        }
+
+                    case GlobalizationOptions.Currency:
+                        {
+                            numberStyle = NumberStyles.Currency;
+                            break;
+                        }
+
+                    default:
+                        {
+                            numberStyle = NumberStyles.Number;
+                            break;
+                        }
+                }
+
+                double value = double.Parse(numberString, numberStyle, CultureInfo.CurrentCulture);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(value)));
+
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.ParsingError)));
+            }
+        }
+
+
+        /// <summary>
+        /// Gets a pattern string for formatting and parsing numbers according to the client's user preferences.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getNumberPattern(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                CultureInfo cultureInfo = CultureInfo.CurrentCulture;
+                NumberFormatInfo formatInfo = cultureInfo.NumberFormat;
+                string numberFormatType = (globalOptions.AdditionalOptions == null || string.IsNullOrEmpty(globalOptions.AdditionalOptions.Type)) ?
+                    GlobalizationOptions.Decimal : globalOptions.AdditionalOptions.Type;
+                NumberPattern pattern = null;
+                string symbol;
+
+                // TODO find out how to get format pattern and the number of fraction digits
+                switch (numberFormatType)
+                {
+                    case GlobalizationOptions.Percent:
+                        {
+                            symbol = formatInfo.PercentSymbol;
+                            pattern = new NumberPattern("", symbol, 0, formatInfo.PercentPositivePattern.ToString(), formatInfo.PercentNegativePattern.ToString(), 0, formatInfo.PercentDecimalSeparator, formatInfo.PercentGroupSeparator);
+                            break;
+                        }
+                    case GlobalizationOptions.Currency:
+                        {
+                            symbol = formatInfo.CurrencySymbol;
+                            pattern = new NumberPattern("", symbol, 0, formatInfo.CurrencyPositivePattern.ToString(), formatInfo.CurrencyNegativePattern.ToString(), 0, formatInfo.CurrencyDecimalSeparator, formatInfo.CurrencyGroupSeparator);
+                            break;
+                        }
+                    default:
+                        {
+                            symbol = formatInfo.NumberDecimalSeparator;
+                            pattern = new NumberPattern("", symbol, 0, "", formatInfo.NumberNegativePattern.ToString(), 0, formatInfo.NumberDecimalSeparator, formatInfo.NumberGroupSeparator);
+                            break;
+                        }
+                }
+
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, pattern));
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.PatternError)));
+            }
+        }
+
+        /// <summary>
+        /// Gets a pattern string for formatting and parsing currency values according to the client's user preferences and ISO 4217 currency code.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getCurrencyPattern(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                if (string.IsNullOrEmpty(globalOptions.CurrencyCode))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                string currencyCode = globalOptions.CurrencyCode;
+
+                // temporary not supported via lack of api required
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.INVALID_ACTION, "Not supported"));
+                return;
+
+                // TODO find the way to get currency info from currency code
+                // http://stackoverflow.com/questions/12373800/3-digit-currency-code-to-currency-symbol
+                // http://stackoverflow.com/questions/6924067/how-to-get-specific-culture-currency-pattern
+                // CultureInfo cultureInfo = new CultureInfo(currencyCode);
+                // NumberFormatInfo numberFormat = cultureInfo.NumberFormat;
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.FormattingError)));
+            }
+        }
+
+        #endregion
+
+        #region private methods
+
+        /// <summary>
+        /// Wraps data into JSON format
+        /// </summary>
+        /// <param name="data">data</param>
+        /// <returns>data formatted as JSON object</returns>
+        private string WrapIntoJSON<T>(T data, string keyName = "value")
+        {
+            string param = "{0}";
+            string stringifiedData = data.ToString();
+
+            if (data.GetType() == typeof(string))
+            {
+                param = "\"" + param + "\"";
+            }
+
+            if (data.GetType() == typeof(bool))
+            {
+                stringifiedData = stringifiedData.ToLower();
+            }
+
+            if (data.GetType() == typeof(string[]))
+            {
+                stringifiedData = JSON.JsonHelper.Serialize(data);
+            }
+
+            var formattedData = string.Format("\"" + keyName + "\":" + param, stringifiedData);
+            formattedData = "{" + formattedData + "}";
+
+            return formattedData;
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/ImageExifHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/ImageExifHelper.cs b/lib/cordova-wp8/templates/standalone/Plugins/ImageExifHelper.cs
new file mode 100644
index 0000000..68ddf87
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/ImageExifHelper.cs
@@ -0,0 +1,209 @@
+/*  
+	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.
+ 
+*/
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Windows.Media.Imaging;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public class ImageExifOrientation
+    {
+        public const int Portrait = 1;
+        public const int PortraitUpsideDown = 3;
+        public const int LandscapeLeft = 6;
+        public const int LandscapeRight = 8;
+    }
+
+    public class ImageExifHelper
+    {
+
+        public static Stream RotateStream(Stream stream, int angle)
+        {
+            stream.Position = 0;
+            if (angle % 90 != 0 || angle < 0)
+            {
+                throw new ArgumentException();
+            }
+            if (angle % 360 == 0)
+            {
+                return stream;
+            }
+
+            angle = angle % 360;
+
+            BitmapImage bitmap = new BitmapImage();
+            bitmap.SetSource(stream);
+            WriteableBitmap wbSource = new WriteableBitmap(bitmap);
+
+            WriteableBitmap wbTarget = null;
+
+            int srcPixelWidth = wbSource.PixelWidth;
+            int srcPixelHeight = wbSource.PixelHeight;
+
+            if (angle % 180 == 0)
+            {
+                wbTarget = new WriteableBitmap(srcPixelWidth, srcPixelHeight);
+            }
+            else
+            {
+                wbTarget = new WriteableBitmap(srcPixelHeight, srcPixelWidth);
+            }
+
+            int destPixelWidth = wbTarget.PixelWidth;
+            int[] srcPxls = wbSource.Pixels;
+            int[] destPxls = wbTarget.Pixels;
+
+            // this ugly if/else is to avoid a conditional check for every pixel
+            if (angle == 90)
+            {
+                for (int x = 0; x < srcPixelWidth; x++)
+                {
+                    for (int y = 0; y < srcPixelHeight; y++)
+                    {
+                        destPxls[(srcPixelHeight - y - 1) + (x * destPixelWidth)] = srcPxls[x + y * srcPixelWidth];
+                    }
+                }
+            }
+            else if (angle == 180)
+            {
+                for (int x = 0; x < srcPixelWidth; x++)
+                {
+                    for (int y = 0; y < srcPixelHeight; y++)
+                    {
+                        destPxls[(srcPixelWidth - x - 1) + (srcPixelHeight - y - 1) * srcPixelWidth] = srcPxls[x + y * srcPixelWidth];
+                    }
+                }
+            }
+            else if (angle == 270)
+            {
+                for (int x = 0; x < srcPixelWidth; x++)
+                {
+                    for (int y = 0; y < srcPixelHeight; y++)
+                    {
+                        destPxls[y + (srcPixelWidth - x - 1) * destPixelWidth] = srcPxls[x + y * srcPixelWidth];
+                    }
+                }
+            }
+
+            MemoryStream targetStream = new MemoryStream();
+            wbTarget.SaveJpeg(targetStream, destPixelWidth, wbTarget.PixelHeight, 0, 100);
+            return targetStream;
+        }
+
+        public static int getImageOrientationFromStream(Stream imgStream)
+        {
+
+            // 0xFFD8 : jpgHeader
+            // 0xFFE1 :
+            // 0x???? : length of exif data
+            // 0x????, 0x???? : Chars 'E','x','i','f'
+            // 0x0000 : 2 empty bytes
+            // <== mark beginning of tags SIZE:ID:VALUE
+            // 0x???? : 'II' or 'MM' for Intel or Motorola ( always getting II on my WP7 devices ), determins littleEndian-ness
+            // 0x002A : marker value
+            // 0x???? : offset to the Image File Data
+
+            // XXXX possible space before actual tag data ... we skip to mark + offset
+
+            // 0x???? number of exif tags present
+
+            // make sure we are at the begining
+            imgStream.Seek(0, SeekOrigin.Begin);
+            BinaryReader reader = new BinaryReader(imgStream);
+
+            byte[] jpgHdr = reader.ReadBytes(2); // always (0xFFD8)
+
+            byte start = reader.ReadByte(); // 0xFF
+            byte index = reader.ReadByte(); // 0xE1
+
+            while (start == 0xFF && index != 0xE1) // This never seems to happen, todo: optimize
+            {
+                // Get the data length
+                ushort dLen = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+                // skip along
+                reader.ReadBytes(dLen - 2);
+                start = reader.ReadByte();
+                index = reader.ReadByte();
+            }
+
+            // It's only success if we found the 0xFFE1 marker
+            if (start != 0xFF || index != 0xE1)
+            {
+                //   throw new Exception("Could not find Exif data block");
+                Debug.WriteLine("Did not find EXIF data");
+                return 0;
+            }
+
+            // read 2 byte length of EXIF data
+            ushort exifLen = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+            String exif = ""; // build the string
+            for (var n = 0; n < 4; n++)
+            {
+                exif += reader.ReadChar();
+            }
+            if (exif != "Exif")
+            {
+                // did not find exif data ...
+                Debug.WriteLine("Did not find EXIF data");
+                return 0;
+            }
+
+            // read 2 empty bytes
+            //ushort emptyBytes = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+            reader.ReadBytes(2);
+
+            long headerMark = reader.BaseStream.Position; // where are we now <==
+
+            //bool isLEndian = (reader.ReadChar() + "" + reader.ReadChar()) == "II";
+            reader.ReadBytes(2); // 'II' or 'MM', but we don't care
+
+            if (0x002A != BitConverter.ToUInt16(reader.ReadBytes(2), 0))
+            {
+                Debug.WriteLine("Error in data != 0x002A");
+                return 0;
+            }
+
+            // Get the offset to the IFD (image file directory)
+            ushort imgOffset = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+
+            imgStream.Position = headerMark + imgOffset;
+            ushort tagCount = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+            for (ushort x = 0; x < tagCount; x++)
+            {
+                // Orientation = 0x112, aka 274
+                if (0x112 == BitConverter.ToUInt16(reader.ReadBytes(2), 0))
+                {
+                    ushort dType = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+                    // don't care ..
+                    uint comps = reader.ReadUInt32();
+                    byte[] tagData = reader.ReadBytes(4);
+                    int orientation = (int)tagData[0];
+                    Debug.WriteLine("orientation = " + orientation.ToString());
+                    return orientation;
+                    // 6 means rotate clockwise 90 deg
+                    // 8 means rotate counter-clockwise 90 deg
+                    // 1 means all is good
+                    // 3 means flip vertical
+                }
+                // skip to the next item, 12 bytes each
+                reader.BaseStream.Seek(10, SeekOrigin.Current);
+            }
+            return 0;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/InAppBrowser.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/InAppBrowser.cs b/lib/cordova-wp8/templates/standalone/Plugins/InAppBrowser.cs
new file mode 100644
index 0000000..2741355
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/InAppBrowser.cs
@@ -0,0 +1,271 @@
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+using System.Diagnostics;
+using System.Runtime.Serialization;
+using WPCordovaClassLib.Cordova;
+using WPCordovaClassLib.Cordova.Commands;
+using WPCordovaClassLib.Cordova.JSON;
+using Microsoft.Phone.Shell;
+using Microsoft.Phone.Tasks;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    [DataContract]
+    public class BrowserOptions
+    {
+        [DataMember]
+        public string url;
+
+        [DataMember]
+        public bool isGeolocationEnabled;
+    }
+
+    public class InAppBrowser : BaseCommand
+    {
+
+        private static WebBrowser browser;
+        private static ApplicationBarIconButton backButton;
+        private static ApplicationBarIconButton fwdButton;
+
+        public void open(string options)
+        {
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+            //BrowserOptions opts = JSON.JsonHelper.Deserialize<BrowserOptions>(options);
+            string urlLoc = args[0];
+            string target = args[1];
+            /*
+                _self - opens in the Cordova WebView if url is in the white-list, else it opens in the InAppBrowser 
+                _blank - always open in the InAppBrowser 
+                _system - always open in the system web browser 
+            */
+            switch (target)
+            {
+                case "_blank":
+                    ShowInAppBrowser(urlLoc);
+                    break;
+                case "_self":
+                    ShowCordovaBrowser(urlLoc);
+                    break;
+                case "_system":
+                    ShowSystemBrowser(urlLoc);
+                    break;
+            }
+
+
+        }
+
+        private void ShowCordovaBrowser(string url)
+        {
+            Uri loc = new Uri(url, UriKind.RelativeOrAbsolute);
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                if (frame != null)
+                {
+                    PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+                    if (page != null)
+                    {
+                        CordovaView cView = page.FindName("CordovaView") as CordovaView;
+                        if (cView != null)
+                        {
+                            WebBrowser br = cView.Browser;
+                            br.Navigate(loc);
+                        }
+                    }
+
+                }
+            });
+        }
+
+        private void ShowSystemBrowser(string url)
+        {
+            WebBrowserTask webBrowserTask = new WebBrowserTask();
+            webBrowserTask.Uri = new Uri(url, UriKind.Absolute);
+            webBrowserTask.Show();
+        }
+
+
+        private void ShowInAppBrowser(string url)
+        {
+            Uri loc = new Uri(url);
+
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                if (browser != null)
+                {
+                    //browser.IsGeolocationEnabled = opts.isGeolocationEnabled;
+                    browser.Navigate(loc);
+                }
+                else
+                {
+                    PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                    if (frame != null)
+                    {
+                        PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+
+                        string baseImageUrl = "Images/";
+
+                        if (page != null)
+                        {
+                            Grid grid = page.FindName("LayoutRoot") as Grid;
+                            if (grid != null)
+                            {
+                                browser = new WebBrowser();
+                                browser.IsScriptEnabled = true;
+                                browser.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(browser_LoadCompleted);
+
+                                browser.Navigating += new EventHandler<NavigatingEventArgs>(browser_Navigating);
+                                browser.NavigationFailed += new System.Windows.Navigation.NavigationFailedEventHandler(browser_NavigationFailed);
+                                browser.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(browser_Navigated);
+                                browser.Navigate(loc);
+                                //browser.IsGeolocationEnabled = opts.isGeolocationEnabled;
+                                grid.Children.Add(browser);
+                            }
+
+                            ApplicationBar bar = new ApplicationBar();
+                            bar.BackgroundColor = Colors.Gray;
+                            bar.IsMenuEnabled = false;
+
+                            backButton = new ApplicationBarIconButton();
+                            backButton.Text = "Back";
+
+                            backButton.IconUri = new Uri(baseImageUrl + "appbar.back.rest.png", UriKind.Relative);
+                            backButton.Click += new EventHandler(backButton_Click);
+                            backButton.IsEnabled = false;
+                            bar.Buttons.Add(backButton);
+
+
+                            fwdButton = new ApplicationBarIconButton();
+                            fwdButton.Text = "Forward";
+                            fwdButton.IconUri = new Uri(baseImageUrl + "appbar.next.rest.png", UriKind.Relative);
+                            fwdButton.Click += new EventHandler(fwdButton_Click);
+                            fwdButton.IsEnabled = false;
+                            bar.Buttons.Add(fwdButton);
+
+                            ApplicationBarIconButton closeBtn = new ApplicationBarIconButton();
+                            closeBtn.Text = "Close";
+                            closeBtn.IconUri = new Uri(baseImageUrl + "appbar.close.rest.png", UriKind.Relative);
+                            closeBtn.Click += new EventHandler(closeBtn_Click);
+                            bar.Buttons.Add(closeBtn);
+
+                            page.ApplicationBar = bar;
+                        }
+
+                    }
+                }
+            });
+        }
+
+        void browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+
+        }
+
+        void fwdButton_Click(object sender, EventArgs e)
+        {
+            if (browser != null)
+            {
+                try
+                {
+                    browser.GoForward();
+                    //browser.InvokeScript("execScript", "history.forward();");
+                }
+                catch (Exception)
+                {
+
+                }
+            }
+        }
+
+        void backButton_Click(object sender, EventArgs e)
+        {
+            if (browser != null)
+            {
+                try
+                {
+                    browser.GoBack();
+                    //browser.InvokeScript("execScript", "history.back();");
+                }
+                catch (Exception)
+                {
+
+                }
+            }
+        }
+
+        void closeBtn_Click(object sender, EventArgs e)
+        {
+            this.close();
+        }
+
+
+        public void close(string options = "")
+        {
+            if (browser != null)
+            {
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                    if (frame != null)
+                    {
+                        PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+                        if (page != null)
+                        {
+                            Grid grid = page.FindName("LayoutRoot") as Grid;
+                            if (grid != null)
+                            {
+                                grid.Children.Remove(browser);
+                            }
+                            page.ApplicationBar = null;
+                        }
+                    }
+                    browser = null;
+                    string message = "{\"type\":\"exit\"}";
+                    PluginResult result = new PluginResult(PluginResult.Status.OK, message);
+                    result.KeepCallback = false;
+                    this.DispatchCommandResult(result);
+                });
+            }
+        }
+
+        void browser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            if (browser != null)
+            {
+                backButton.IsEnabled = browser.CanGoBack;
+                fwdButton.IsEnabled = browser.CanGoForward;
+
+            }
+            string message = "{\"type\":\"loadstop\", \"url\":\"" + e.Uri.AbsoluteUri + "\"}";
+            PluginResult result = new PluginResult(PluginResult.Status.OK, message);
+            result.KeepCallback = true;
+            this.DispatchCommandResult(result);
+        }
+
+        void browser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
+        {
+            string message = "{\"type\":\"error\",\"url\":\"" + e.Uri.AbsoluteUri + "\"}";
+            PluginResult result = new PluginResult(PluginResult.Status.ERROR, message);
+            result.KeepCallback = true;
+            this.DispatchCommandResult(result);
+        }
+
+        void browser_Navigating(object sender, NavigatingEventArgs e)
+        {
+            string message = "{\"type\":\"loadstart\",\"url\":\"" + e.Uri.AbsoluteUri + "\"}";
+            PluginResult result = new PluginResult(PluginResult.Status.OK, message);
+            result.KeepCallback = true;
+            this.DispatchCommandResult(result);
+        }
+
+    }
+}


[19/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoRecorder.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoRecorder.xaml.cs b/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoRecorder.xaml.cs
new file mode 100644
index 0000000..6ab1cc3
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoRecorder.xaml.cs
@@ -0,0 +1,405 @@
+/*  
+	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.
+*/
+
+using System;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Windows.Media;
+using System.Windows.Navigation;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Shell;
+using Microsoft.Phone.Tasks;
+using VideoResult = WPCordovaClassLib.Cordova.UI.VideoCaptureTask.VideoResult;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    public partial class VideoRecorder : PhoneApplicationPage
+    {
+
+        #region Constants
+
+        /// <summary>
+        /// Caption for record button in ready state
+        /// </summary>
+        private const string RecordingStartCaption = "Record";
+
+        /// <summary>
+        /// Caption for record button in recording state
+        /// </summary>
+        private const string RecordingStopCaption = "Stop";
+
+        /// <summary>
+        /// Start record icon URI
+        /// </summary>
+        private const string StartIconUri = "/Images/appbar.feature.video.rest.png";
+
+        /// <summary>
+        /// Stop record icon URI
+        /// </summary>
+        private const string StopIconUri = "/Images/appbar.stop.rest.png";
+
+        /// <summary>
+        /// Folder to save video clips
+        /// </summary>
+        private const string LocalFolderName = "VideoCache";
+
+        /// <summary>
+        /// File name format
+        /// </summary>
+        private const string FileNameFormat = "Video-{0}.mp4";
+
+        /// <summary>
+        /// Temporary file name
+        /// </summary>
+        private const string defaultFileName = "NewVideoFile.mp4";
+
+        #endregion
+
+        #region Callbacks
+        /// <summary>
+        /// Occurs when a video recording task is completed.
+        /// </summary>
+        public event EventHandler<VideoResult> Completed;
+
+        #endregion
+
+        #region Fields
+
+        /// <summary>
+        /// Viewfinder for capturing video
+        /// </summary>
+        private VideoBrush videoRecorderBrush;
+
+        /// <summary>
+        /// Path to save video clip
+        /// </summary>
+        private string filePath;
+
+        /// <summary>
+        /// Source for capturing video. 
+        /// </summary>
+        private CaptureSource captureSource;
+
+        /// <summary>
+        /// Video device
+        /// </summary>
+        private VideoCaptureDevice videoCaptureDevice;
+
+        /// <summary>
+        /// File sink so save recording video in Isolated Storage
+        /// </summary>
+        private FileSink fileSink;
+
+        /// <summary>
+        /// For managing button and application state 
+        /// </summary>
+        private enum VideoState { Initialized, Ready, Recording, CameraNotSupported };
+
+        /// <summary>
+        /// Current video state
+        /// </summary>
+        private VideoState currentVideoState;
+
+        /// <summary>
+        /// Stream to return result
+        /// </summary>
+        private MemoryStream memoryStream;
+
+        /// <summary>
+        /// Recording result, dispatched back when recording page is closed
+        /// </summary>
+        private VideoResult result = new VideoResult(TaskResult.Cancel);
+
+        #endregion
+
+        /// <summary>
+        /// Initializes components
+        /// </summary>
+        public VideoRecorder()
+        {
+            InitializeComponent();
+
+            PhoneAppBar = (ApplicationBar)ApplicationBar;
+            PhoneAppBar.IsVisible = true;
+            btnStartRecording = ((ApplicationBarIconButton)ApplicationBar.Buttons[0]);
+            btnTakeVideo = ((ApplicationBarIconButton)ApplicationBar.Buttons[1]);
+        }
+
+        /// <summary>
+        /// Initializes the video recorder then page is loading
+        /// </summary>
+        protected override void OnNavigatedTo(NavigationEventArgs e)
+        {
+            base.OnNavigatedTo(e);
+            this.InitializeVideoRecorder();
+        }
+
+        /// <summary>
+        /// Disposes camera and media objects then leave the page
+        /// </summary>
+        protected override void OnNavigatedFrom(NavigationEventArgs e)
+        {
+            this.DisposeVideoRecorder();
+
+            if (this.Completed != null)
+            {
+                this.Completed(this, result);
+            }
+            base.OnNavigatedFrom(e);
+        }
+
+        /// <summary>
+        /// Handles TakeVideo button click
+        /// </summary>
+        private void TakeVideo_Click(object sender, EventArgs e)
+        {
+            this.result = this.SaveVideoClip();
+            this.NavigateBack();
+        }
+
+        private void NavigateBack()
+        {
+            if (this.NavigationService.CanGoBack)
+            {
+                this.NavigationService.GoBack();
+            }
+        }
+
+        /// <summary>
+        /// Resaves video clip from temporary directory to persistent 
+        /// </summary>
+        private VideoResult SaveVideoClip()
+        {
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    if (string.IsNullOrEmpty(filePath) || (!isoFile.FileExists(filePath)))
+                    {
+                        return new VideoResult(TaskResult.Cancel);
+                    }
+
+                    string fileName = String.Format(FileNameFormat, Guid.NewGuid().ToString());
+                    string newPath = Path.Combine("/" + LocalFolderName + "/", fileName);
+                    isoFile.CopyFile(filePath, newPath);
+                    isoFile.DeleteFile(filePath);
+
+                    memoryStream = new MemoryStream();
+                    using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(newPath, FileMode.Open, isoFile))
+                    {
+                        fileStream.CopyTo(memoryStream);
+                    }
+
+                    VideoResult result = new VideoResult(TaskResult.OK);
+                    result.VideoFileName = newPath;
+                    result.VideoFile = this.memoryStream;
+                    result.VideoFile.Seek(0, SeekOrigin.Begin);
+                    return result;
+                }
+
+            }
+            catch (Exception)
+            {
+                return new VideoResult(TaskResult.None);
+            }
+        }
+
+        /// <summary>
+        /// Updates the buttons on the UI thread based on current state. 
+        /// </summary>
+        /// <param name="currentState">current UI state</param>
+        private void UpdateUI(VideoState currentState)
+        {
+            Dispatcher.BeginInvoke(delegate
+            {
+                switch (currentState)
+                {
+                    case VideoState.CameraNotSupported:
+                        btnStartRecording.IsEnabled = false;
+                        btnTakeVideo.IsEnabled = false;
+                        break;
+
+                    case VideoState.Initialized:
+                        btnStartRecording.Text = RecordingStartCaption;
+                        btnStartRecording.IconUri = new Uri(StartIconUri, UriKind.Relative);
+                        btnTakeVideo.IsEnabled = false;
+                        break;
+
+                    case VideoState.Ready:
+                        btnStartRecording.Text = RecordingStartCaption;
+                        btnStartRecording.IconUri = new Uri(StartIconUri, UriKind.Relative);
+                        btnTakeVideo.IsEnabled = true;
+                        break;
+
+                    case VideoState.Recording:
+                        btnStartRecording.Text = RecordingStopCaption;
+                        btnStartRecording.IconUri = new Uri(StopIconUri, UriKind.Relative);
+                        btnTakeVideo.IsEnabled = false;
+                        break;
+
+                    default:
+                        break;
+                }
+                currentVideoState = currentState;
+            });
+        }
+
+        /// <summary>
+        /// Initializes VideoRecorder
+        /// </summary>
+        public void InitializeVideoRecorder()
+        {
+            if (captureSource == null)
+            {
+                captureSource = new CaptureSource();
+                fileSink = new FileSink();
+                videoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
+
+                if (videoCaptureDevice != null)
+                {
+                    videoRecorderBrush = new VideoBrush();
+                    videoRecorderBrush.SetSource(captureSource);
+                    viewfinderRectangle.Fill = videoRecorderBrush;
+                    captureSource.Start();
+                    this.UpdateUI(VideoState.Initialized);
+                }
+                else
+                {
+                    this.UpdateUI(VideoState.CameraNotSupported);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Sets recording state: start recording 
+        /// </summary>
+        private void StartVideoRecording()
+        {
+            try
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Started))
+                {
+                    captureSource.Stop();
+                    fileSink.CaptureSource = captureSource;
+                    filePath = System.IO.Path.Combine("/" + LocalFolderName + "/", defaultFileName);
+
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (!isoFile.DirectoryExists(LocalFolderName))
+                        {
+                            isoFile.CreateDirectory(LocalFolderName);
+                        }
+
+                        if (isoFile.FileExists(filePath))
+                        {
+                            isoFile.DeleteFile(filePath);
+                        }
+                    }
+
+                    fileSink.IsolatedStorageFileName = filePath;
+                }
+
+                if (captureSource.VideoCaptureDevice != null
+                    && captureSource.State == CaptureState.Stopped)
+                {
+                    captureSource.Start();
+                }
+                this.UpdateUI(VideoState.Recording);
+            }
+            catch (Exception)
+            {
+                this.result = new VideoResult(TaskResult.None);
+                this.NavigateBack();
+            }
+        }
+
+        /// <summary>
+        /// Sets the recording state: stop recording
+        /// </summary>
+        private void StopVideoRecording()
+        {
+            try
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Started))
+                {
+                    captureSource.Stop();
+                    fileSink.CaptureSource = null;
+                    fileSink.IsolatedStorageFileName = null;
+                    this.StartVideoPreview();
+                }
+            }
+            catch (Exception)
+            {
+                this.result = new VideoResult(TaskResult.None);
+                this.NavigateBack();
+            }
+        }
+
+        /// <summary>
+        /// Sets the recording state: display the video on the viewfinder. 
+        /// </summary>
+        private void StartVideoPreview()
+        {
+            try
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Stopped))
+                {
+                    videoRecorderBrush.SetSource(captureSource);
+                    viewfinderRectangle.Fill = videoRecorderBrush;
+                    captureSource.Start();
+                    this.UpdateUI(VideoState.Ready);
+                }
+            }
+            catch (Exception)
+            {
+                this.result = new VideoResult(TaskResult.None);
+                this.NavigateBack();
+            }
+        }
+
+        /// <summary>
+        /// Starts video recording 
+        /// </summary>
+        private void StartRecording_Click(object sender, EventArgs e)
+        {
+            if (currentVideoState == VideoState.Recording)
+            {
+                this.StopVideoRecording();
+            }
+            else
+            {
+                this.StartVideoRecording();
+            }
+        }
+
+        /// <summary>
+        /// Releases resources
+        /// </summary>
+        private void DisposeVideoRecorder()
+        {
+            if (captureSource != null)
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Started))
+                {
+                    captureSource.Stop();
+                }
+                captureSource = null;
+                videoCaptureDevice = null;
+                fileSink = null;
+                videoRecorderBrush = null;
+            }
+        }
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Properties/AppManifest.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Properties/AppManifest.xml b/lib/cordova-wp8/templates/standalone/Properties/AppManifest.xml
new file mode 100644
index 0000000..877ea4b
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Properties/AppManifest.xml
@@ -0,0 +1,6 @@
+<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+>
+  <Deployment.Parts>
+  </Deployment.Parts>
+</Deployment>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Properties/AssemblyInfo.cs b/lib/cordova-wp8/templates/standalone/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..8d9eb89
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Properties/AssemblyInfo.cs
@@ -0,0 +1,39 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Resources;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CordovaAppProj")]
+[assembly: AssemblyDescription("2.0.0.0")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Apache Cordova")]
+[assembly: AssemblyProduct("CordovaAppProj")]
+[assembly: AssemblyCopyright("Copyright © Apache Cordova 2013")]
+[assembly: AssemblyTrademark("Apache Cordova")]
+[assembly: AssemblyCulture("")]
+
+
+[assembly: NeutralResourcesLanguageAttribute("en-US")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("9e27b972-0825-4386-ba17-63c695262c3d")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Properties/WMAppManifest.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Properties/WMAppManifest.xml b/lib/cordova-wp8/templates/standalone/Properties/WMAppManifest.xml
new file mode 100644
index 0000000..3c18727
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Properties/WMAppManifest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2012/deployment" AppPlatformVersion="8.0">
+  <DefaultLanguage xmlns="" code="en-US" />
+  <Languages xmlns="">
+    <Language code="en-US" />
+  </Languages>
+  <App xmlns="" ProductID="{13b7ec4f-d4cf-422b-90ef-cd9c48af66ab}" Title="$safeprojectname$" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" Author="$safeprojectname$ author" BitsPerPixel="32" Description="Apache Cordova for Windows Phone" Publisher="$safeprojectname$" PublisherID="{db093ed5-53b1-45f7-af72-751e8f36ab80}">
+    <IconPath IsRelative="true" IsResource="false">ApplicationIcon.png</IconPath>
+    <Capabilities>
+      <Capability Name="ID_CAP_IDENTITY_DEVICE" />
+      <Capability Name="ID_CAP_IDENTITY_USER" />
+      <Capability Name="ID_CAP_LOCATION" />
+      <Capability Name="ID_CAP_NETWORKING" />
+      <Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
+      <Capability Name="ID_CAP_APPOINTMENTS" />
+      <Capability Name="ID_CAP_CONTACTS" />
+      <Capability Name="ID_CAP_ISV_CAMERA" />
+      <Capability Name="ID_CAP_MICROPHONE" />
+      <Capability Name="ID_CAP_PHONEDIALER" />
+      <Capability Name="ID_CAP_PUSH_NOTIFICATION" />
+      <Capability Name="ID_CAP_SENSORS" />
+      <Capability Name="ID_CAP_MEDIALIB_AUDIO" />
+      <Capability Name="ID_CAP_MEDIALIB_PHOTO" />
+      <Capability Name="ID_CAP_MEDIALIB_PLAYBACK" />
+    </Capabilities>
+    <Tasks>
+      <DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
+    </Tasks>
+    <Tokens>
+      <PrimaryToken TokenID="$safeprojectname$Token" TaskName="_default">
+        <TemplateFlip>
+          <SmallImageURI IsResource="false" IsRelative="true">Background.png</SmallImageURI>
+          <Count>0</Count>
+          <BackgroundImageURI IsResource="false" IsRelative="true">Background.png</BackgroundImageURI>
+          <Title>$safeprojectname$</Title>
+          <BackContent></BackContent>
+          <BackBackgroundImageURI></BackBackgroundImageURI>
+          <BackTitle></BackTitle>
+          <LargeBackgroundImageURI></LargeBackgroundImageURI>
+          <LargeBackContent></LargeBackContent>
+          <LargeBackBackgroundImageURI></LargeBackBackgroundImageURI>
+          <DeviceLockImageURI></DeviceLockImageURI>
+          <HasLarge>false</HasLarge>
+        </TemplateFlip>
+      </PrimaryToken>
+    </Tokens>
+    <ScreenResolutions>
+      <ScreenResolution Name="ID_RESOLUTION_WVGA" />
+      <ScreenResolution Name="ID_RESOLUTION_WXGA" />
+      <ScreenResolution Name="ID_RESOLUTION_HD720P" />
+    </ScreenResolutions>
+  </App>
+</Deployment>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/SplashScreenImage.jpg
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/SplashScreenImage.jpg b/lib/cordova-wp8/templates/standalone/SplashScreenImage.jpg
new file mode 100644
index 0000000..d35501d
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/SplashScreenImage.jpg differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/VERSION
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/VERSION b/lib/cordova-wp8/templates/standalone/VERSION
new file mode 100644
index 0000000..9aa3464
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/VERSION
@@ -0,0 +1 @@
+2.7.0
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/config.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/config.xml b/lib/cordova-wp8/templates/standalone/config.xml
new file mode 100644
index 0000000..170f9fe
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/config.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+#
+# 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.
+#
+-->
+<widget>
+
+  <plugins>
+    <plugin name="Device"/>
+    <plugin name="Logger"/>
+    <plugin name="Compass"/>
+    <plugin name="Accelerometer"/>
+    <plugin name="Camera"/>
+    <plugin name="NetworkStatus"/>
+    <plugin name="Contacts"/>
+    <plugin name="DebugConsole" />
+    <plugin name="Echo"/>
+    <plugin name="File"/>
+    <plugin name="FileTransfer"/>
+    <plugin name="Geolocation"/>
+    <plugin name="Notification"/>
+    <plugin name="Media"/>
+    <plugin name="Capture"/>
+    <plugin name="SplashScreen"/>
+    <plugin name="Battery"/>
+    <plugin name="Globalization"/>
+    <plugin name="InAppBrowser"/>
+  </plugins>
+
+
+  <access origin="*"/>
+
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/build.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/build.bat b/lib/cordova-wp8/templates/standalone/cordova/build.bat
new file mode 100644
index 0000000..b48598a
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/build.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%lib\build.js (
+    cscript "%full_path%lib\build.js" %* //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'build.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/clean.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/clean.bat b/lib/cordova-wp8/templates/standalone/cordova/clean.bat
new file mode 100644
index 0000000..1bafe1b
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/clean.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%lib\clean.js (
+    cscript "%full_path%lib\clean.js" %* //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'clean.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy.sln
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy.sln b/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy.sln
new file mode 100644
index 0000000..3305276
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CordovaDeploy", "CordovaDeploy\CordovaDeploy.csproj", "{E752165B-AF59-4FF0-8601-A2A69FE09E0E}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x86 = Debug|x86
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{E752165B-AF59-4FF0-8601-A2A69FE09E0E}.Debug|x86.ActiveCfg = Debug|x86
+		{E752165B-AF59-4FF0-8601-A2A69FE09E0E}.Debug|x86.Build.0 = Debug|x86
+		{E752165B-AF59-4FF0-8601-A2A69FE09E0E}.Release|x86.ActiveCfg = Release|x86
+		{E752165B-AF59-4FF0-8601-A2A69FE09E0E}.Release|x86.Build.0 = Release|x86
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/CordovaDeploy.csproj
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/CordovaDeploy.csproj b/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/CordovaDeploy.csproj
new file mode 100644
index 0000000..a52b532
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/CordovaDeploy.csproj
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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. 
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{E752165B-AF59-4FF0-8601-A2A69FE09E0E}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>CordovaDeploy</RootNamespace>
+    <AssemblyName>CordovaDeploy</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <TargetFrameworkProfile>
+    </TargetFrameworkProfile>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup>
+    <StartupObject>CordovaDeploy.DeployTool</StartupObject>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Smartdevice.Connectivity, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\..\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SmartDevice.Connectivity\v4.0_11.0.0.0__b03f5f7f11d50a3a\Microsoft.Smartdevice.Connectivity.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Smartdevice.Connectivity.Interface, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\..\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SmartDevice.Connectivity.Interface\v4.0_11.0.0.0__b03f5f7f11d50a3a\Microsoft.Smartdevice.Connectivity.Interface.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Smartdevice.MultiTargeting.Connectivity, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\..\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SmartDevice.MultiTargeting.Connectivity\v4.0_11.0.0.0__b03f5f7f11d50a3a\Microsoft.Smartdevice.MultiTargeting.Connectivity.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="app.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Program.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Program.cs b/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Program.cs
new file mode 100644
index 0000000..c7e5e74
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Program.cs
@@ -0,0 +1,424 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Xml.XPath;
+using System.Xml;
+using System.Xml.Linq;
+using System.Globalization;
+// Windows Phone Emulator Libraries
+using Microsoft.SmartDevice.Connectivity;
+using Microsoft.SmartDevice.Connectivity.Interface;
+using Microsoft.SmartDevice.MultiTargeting.Connectivity;
+
+
+namespace CordovaDeploy
+{
+
+    class DeployTool
+    {
+
+        static void Usage()
+        {
+            Log("Usage: CordovaDeploy [ -devices  BuildOutputPath -d:DeviceIndex ]");
+            Log("    -devices : lists the devices and exits");
+            Log("    BuildOutputPath : path to the built application, typically Bin/Debug/ or Bin/Release/");
+            Log("    -d : index of the device to deploy, default is 0 ");
+            Log("examples:");
+            Log("  CordovaDeploy -devices");
+            Log("  CordovaDeploy Bin/Debug");
+            Log("  CordovaDeploy Bin/Release -d:1");
+        }
+
+        static void ReadWait()
+        {
+            // This is used when running in Visual Studio, the Command Window is created at launch, and disappears at the 
+            // end of the program run, this let's us see the output before the window is closed.
+
+            /*
+            Console.WriteLine("\nPress ENTER to continue...");
+            Console.Read();
+            */
+        }
+
+        static void Log(string msg, bool error = false)
+        {
+            Debug.WriteLine(msg);
+            if (error)
+            {
+                Console.Error.WriteLine(msg);
+            }
+            else
+            {
+                Console.Out.WriteLine(msg);
+            }
+        }
+
+        static Guid ReadAppId(string root)
+        {
+            Guid appID = Guid.Empty;
+            string manifestFilePath = root + @"\Properties\WMAppManifest.xml";
+
+            if (File.Exists(manifestFilePath))
+            {
+                XDocument xdoc = XDocument.Load(manifestFilePath);
+                var appNode = xdoc.Root.Descendants("App").FirstOrDefault();
+                if (appNode != null)
+                {
+                    string guidStr = appNode.Attribute("ProductID").Value;
+                    appID = new Guid(guidStr);
+                }
+                else
+                {
+                    Log(string.Format("Unable to find appID, expected to find an App.ProductID property defined in the file {0}", manifestFilePath), true);
+                }
+            }
+            else
+            {
+                Log(string.Format("Error: the file {0} does not exist", manifestFilePath), true);
+            }
+            return appID;
+        }
+
+        static void ListDevices()
+        {
+            MultiTargetingConnectivity mtConn = new MultiTargetingConnectivity(CultureInfo.CurrentUICulture.LCID);
+            Collection<ConnectableDevice> deviceList = mtConn.GetConnectableDevices();
+
+            for (int index = 0; index < deviceList.Count; index++)
+            {
+                ConnectableDevice d = deviceList[index];
+                string info = string.Format("{0} : {1} : {2}", index.ToString(), d.Id, d.Name);
+                Log(info);
+            }
+        }
+
+        static ConnectableDevice GetDeviceAtIndex(int index)
+        {
+            MultiTargetingConnectivity mtConn = new MultiTargetingConnectivity(CultureInfo.CurrentUICulture.LCID);
+            Collection<ConnectableDevice> deviceList = mtConn.GetConnectableDevices();
+            return deviceList[index];
+        }
+
+        static void Main(string[] args)
+        {
+            int deviceIndex = 0;
+
+            string iconFilePath = "";
+            string xapFilePath = "";
+            Guid appID = Guid.Empty;
+
+            string root = Directory.GetCurrentDirectory();
+
+            if (args.Length < 1)
+            {
+                Usage();
+                ReadWait();
+                return;
+            }
+            else if (args[0] == "-devices")
+            {
+                ListDevices();
+                ReadWait();
+                return;
+            }
+            else if (args.Length > 1 && args[1].StartsWith("-d:"))
+            {
+                deviceIndex = int.Parse(args[1].Substring(3));
+            }
+
+
+            if (Directory.Exists(args[0]))
+            {
+                DirectoryInfo info = new DirectoryInfo(args[0]);
+                root = info.FullName;
+            }
+
+            appID = ReadAppId(root);
+            if (appID == Guid.Empty)
+            {
+                return;    // Logging of errors is done in ReadAppId
+            }
+
+            if (File.Exists(root + @"\ApplicationIcon.png"))
+            {
+                iconFilePath = root + @"\ApplicationIcon.png";
+            }
+            else
+            {
+                Log(string.Format("Error: could not find application icon at {0}", root + @"\ApplicationIcon.png"), true);
+                ReadWait();
+                return;
+            }
+
+            try {
+                xapFilePath = Directory.GetFiles(root + @"\Bin\Debug", "*.xap").FirstOrDefault();
+            } catch (DirectoryNotFoundException e) {
+                try {
+                    xapFilePath = Directory.GetFiles(root + @"\Bin\Release", "*.xap").FirstOrDefault();
+                } catch (DirectoryNotFoundException ex) {
+                    Log(string.Format("Error: could not find project build directoy in {0}", root), true);
+                    Log("make sure your app has been successfully built before deploying.", true);
+                }
+            }
+
+            if (string.IsNullOrEmpty(xapFilePath))
+            {
+                Log(string.Format("Error: could not find application .xap in folder {0}", root), true);
+                ReadWait();
+                return;
+            }
+
+            ConnectableDevice deviceConn = GetDeviceAtIndex(deviceIndex);
+            Log("Connecting to device :: " + deviceConn.Id + " : " + deviceConn.Name);
+            try
+            {
+                IDevice device = deviceConn.Connect();
+                IRemoteApplication app = null;
+                if (device.IsApplicationInstalled(appID))
+                {
+                    Log("Uninstalling XAP from " + deviceConn.Name);
+                    app = device.GetApplication(appID);
+                    app.Uninstall();
+                }
+
+                Log("Installing app on " + deviceConn.Name);
+                app = device.InstallApplication(appID, appID, "NormalApp", iconFilePath, xapFilePath);
+
+                Log("Launching app on " + deviceConn.Name);
+                app.Launch();
+
+                // To Stop :
+                //app.TerminateRunningInstances();
+
+                device.Disconnect();
+
+                ReadWait();
+
+            }
+            catch (Exception ex)
+            {
+                Log("Error :: " + ex.Message, true);
+            }
+        }
+
+        // To read and write ISO storage files!! :
+        /*
+        try
+        {
+            IRemoteIsolatedStorageFile isoStore = app.GetIsolatedStore();
+            remoteIsolatedStorageFile.ReceiveFile("sourcePath", "destPath", true);
+        }
+        catch (Exception ex) { }
+        */
+
+    }
+    class Program
+    {
+        static void Usage()
+        {
+            Log("Usage: CordovaDeploy [ -devices  BuildOutputPath -d:DeviceIndex ]");
+            Log("    -devices : lists the devices and exits");
+            Log("    BuildOutputPath : path to the built application, typically Bin/Debug/ or Bin/Release/");
+            Log("    -d : index of the device to deploy, default is 0 ");
+            Log("examples:");
+            Log("  CordovaDeploy -devices");
+            Log("  CordovaDeploy Bin/Debug");
+            Log("  CordovaDeploy Bin/Release -d:1");
+        }
+
+        static void ReadWait()
+        {
+            // This is used when running in Visual Studio, the Command Window is created at launch, and disappears at the 
+            // end of the program run, this let's us see the output before the window is closed.
+
+            /*
+            Console.WriteLine("\nPress ENTER to continue...");
+            Console.Read();
+            */
+        }
+
+        static void Log(string msg, bool error = false)
+        {
+            Debug.WriteLine(msg);
+            if (error)
+            {
+                Console.Error.WriteLine(msg);
+            }
+            else
+            {
+                Console.Out.WriteLine(msg);
+            }
+        }
+
+        static Guid ReadAppId(string root)
+        {
+            Guid appID = Guid.Empty;
+            string manifestFilePath = root + @"\Properties\WMAppManifest.xml";
+
+            if (File.Exists(manifestFilePath))
+            {
+                XDocument xdoc = XDocument.Load(manifestFilePath);
+                var appNode = xdoc.Root.Descendants("App").FirstOrDefault();
+                if (appNode != null)
+                {
+                    string guidStr = appNode.Attribute("ProductID").Value;
+                    appID = new Guid(guidStr);
+                }
+                else
+                {
+                    Log(string.Format("Unable to find appID, expected to find an App.ProductID property defined in the file {0}", manifestFilePath), true);
+                }
+            }
+            else
+            {
+                Log(string.Format("Error: the file {0} does not exist", manifestFilePath), true);
+            }
+            return appID;
+        }
+
+        static void ListDevices()
+        {
+            MultiTargetingConnectivity mtConn = new MultiTargetingConnectivity(CultureInfo.CurrentUICulture.LCID);
+            Collection<ConnectableDevice> deviceList = mtConn.GetConnectableDevices();
+
+            for (int index = 0; index < deviceList.Count; index++)
+            {
+                ConnectableDevice d = deviceList[index];
+                string info = string.Format("{0} : {1} : {2}", index.ToString(), d.Id, d.Name);
+                Log(info);
+            }
+        }
+
+        static ConnectableDevice GetDeviceAtIndex(int index)
+        {
+            MultiTargetingConnectivity mtConn = new MultiTargetingConnectivity(CultureInfo.CurrentUICulture.LCID);
+            Collection<ConnectableDevice> deviceList = mtConn.GetConnectableDevices();
+            return deviceList[index];
+        }
+
+        static void Main(string[] args)
+        {
+            int deviceIndex = 0;
+
+            string iconFilePath = "";
+            string xapFilePath = "";
+            Guid appID = Guid.Empty;
+
+            string root = Directory.GetCurrentDirectory();
+
+            if (args.Length < 1)
+            {
+                Usage();
+                ReadWait();
+                return;
+            }
+            else if (args[0] == "-devices")
+            {
+                ListDevices();
+                ReadWait();
+                return;
+            }
+            else if (args.Length > 1 && args[1].StartsWith("-d:"))
+            {
+                deviceIndex = int.Parse(args[1].Substring(3));
+            }
+
+
+            if (Directory.Exists(args[0]))
+            {
+                DirectoryInfo info = new DirectoryInfo(args[0]);
+                root = info.FullName;
+            }
+
+            appID = ReadAppId(root);
+            if (appID == Guid.Empty)
+            {
+                return;    // Logging of errors is done in ReadAppId
+            }
+
+            if (File.Exists(root + @"\ApplicationIcon.png"))
+            {
+                iconFilePath = root + @"\ApplicationIcon.png";
+            }
+            else
+            {
+                Log(string.Format("Error: could not find application icon at {0}", root + @"\ApplicationIcon.png"), true);
+                ReadWait();
+                return;
+            }
+
+            xapFilePath = Directory.GetFiles(root + @"\Bin\Debug", "*.xap").FirstOrDefault();
+            if (string.IsNullOrEmpty(xapFilePath))
+            {
+                Log(string.Format("Error: could not find application .xap in folder {0}", root), true);
+                ReadWait();
+                return;
+            }
+
+            ConnectableDevice deviceConn = GetDeviceAtIndex(deviceIndex);
+            Log("Connecting to device :: " + deviceConn.Id + " : " + deviceConn.Name);
+            try
+            {
+                IDevice device = deviceConn.Connect();
+                IRemoteApplication app = null;
+                if (device.IsApplicationInstalled(appID))
+                {
+                    Log("Uninstalling XAP from " + deviceConn.Name);
+                    app = device.GetApplication(appID);
+                    app.Uninstall();
+                }
+
+                Log("Installing app on " + deviceConn.Name);
+                app = device.InstallApplication(appID, appID, "NormalApp", iconFilePath, xapFilePath);
+
+                Log("Launching app on " + deviceConn.Name);
+                app.Launch();
+
+                // To Stop :
+                //app.TerminateRunningInstances();
+
+                device.Disconnect();
+
+                ReadWait();
+
+            }
+            catch (Exception ex)
+            {
+                Log("Error :: " + ex.Message, true);
+            }
+        }
+
+        // To read and write ISO storage files!! :
+        /*
+        try
+        {
+            IRemoteIsolatedStorageFile isoStore = app.GetIsolatedStore();
+            remoteIsolatedStorageFile.ReceiveFile("sourcePath", "destPath", true);
+        }
+        catch (Exception ex) { }
+        */ 
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Properties/AssemblyInfo.cs b/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..3c26c87
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CordovaDeploy")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("CordovaDeploy")]
+[assembly: AssemblyCopyright("Copyright ©  2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("256b11aa-d4bb-48cf-8024-7c040421fa8d")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/app.config
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/app.config b/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/app.config
new file mode 100644
index 0000000..c5e1dae
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/app.config
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<configuration>
+<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/build.js b/lib/cordova-wp8/templates/standalone/cordova/lib/build.js
new file mode 100644
index 0000000..9986a7e
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/build.js
@@ -0,0 +1,181 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject');
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments;
+
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\build.js').join('');
+
+// help/usage function
+function Usage() {
+    Log("");
+    Log("Usage: build [ --debug | --release ]");
+    Log("    --help    : Displays this dialog.");
+    Log("    --debug   : Cleans and builds project in debug mode.");
+    Log("    --release : Cleans and builds project in release mode.");
+    Log("examples:");
+    Log("    build ");
+    Log("    build --debug");
+    Log("    build --release");
+    Log("");
+}
+
+// logs messaged to stdout and stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    //Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if (!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oShell.StdErr.AtEndOfStream) {
+        var line = oShell.StdErr.ReadAll();
+        Log(line, true);
+        WScript.Quit(2);
+    }
+}
+
+// checks to see if a .csproj file exists in the project root
+function is_cordova_project(path) {
+    if (fso.FolderExists(path)) {
+        var proj_folder = fso.GetFolder(path);
+        var proj_files = new Enumerator(proj_folder.Files);
+        for (;!proj_files.atEnd(); proj_files.moveNext()) {
+            if (fso.GetExtensionName(proj_files.item()) == 'csproj') {
+                return true;  
+            }
+        }
+    }
+    return false;
+}
+
+// builds the project and .xap in release mode
+function build_xap_release(path) {
+    Log("Building Cordova-WP8 Project:");
+    Log("\tConfiguration : Release");
+    Log("\tDirectory : " + path);
+    
+    wscript_shell.CurrentDirectory = path;
+    exec_verbose('msbuild /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo /p:Configuration=Release');
+    
+    // check if file xap was created
+    if (fso.FolderExists(path + '\\Bin\\Release')) {
+        var out_folder = fso.GetFolder(path + '\\Bin\\Release');
+        var out_files = new Enumerator(out_folder.Files);
+        for (;!out_files.atEnd(); out_files.moveNext()) {
+            if (fso.GetExtensionName(out_files.item()) == 'xap') {
+                Log("BUILD SUCCESS.");
+                return;  
+            }
+        }
+    }
+    Log('ERROR: MSBuild failed to create .xap when building cordova-wp8 for release.', true);
+    WScript.Quit(2);
+}
+
+// builds the project and .xap in debug mode
+function build_xap_debug(path) {
+    Log("Building Cordova-WP8 Project:");
+    Log("\tConfiguration : Debug");
+    Log("\tDirectory : " + path);
+    
+    wscript_shell.CurrentDirectory = path;
+    exec_verbose('msbuild /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo /p:Configuration=Debug');
+    
+    // check if file xap was created
+    if (fso.FolderExists(path + '\\Bin\\Debug')) {
+        var out_folder = fso.GetFolder(path + '\\Bin\\Debug');
+        var out_files = new Enumerator(out_folder.Files);
+        for (;!out_files.atEnd(); out_files.moveNext()) {
+            if (fso.GetExtensionName(out_files.item()) == 'xap') {
+                Log("BUILD SUCCESS.");
+                return;  
+            }
+        }
+    }
+    Log('ERROR: MSBuild failed to create .xap when building cordova-wp8 for debugging.', true);
+    WScript.Quit(2);
+}
+
+
+Log("");
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help") {
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (args.Count() > 1) {
+        Log("Error: Too many arguments.", true);
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (fso.FolderExists(ROOT)) {
+        if (!is_cordova_project(ROOT)) {
+            Log('Error: .csproj file not found in ' + ROOT, true);
+            Log('could not build project.', true);
+            WScript.Quit(2);
+        }
+
+        if (args(0) == "--debug" || args(0) == "-d") {
+            exec_verbose('%comspec% /c ' + ROOT + '\\cordova\\clean');
+            build_xap_debug(ROOT);
+        }
+        else if (args(0) == "--release" || args(0) == "-r") {
+            exec_verbose('%comspec% /c ' + ROOT + '\\cordova\\clean');
+            build_xap_release(ROOT);
+        }
+        else {
+            Log("Error: \"" + args(0) + "\" is not recognized as a build option", true);
+            Usage();
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log("Error: Project directory not found,", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
+else {
+    Log("WARNING: [ --debug | --release ] not specified, defaulting to debug...");
+    build_xap_debug(ROOT);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/clean.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/clean.js b/lib/cordova-wp8/templates/standalone/cordova/lib/clean.js
new file mode 100644
index 0000000..3a8c871
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/clean.js
@@ -0,0 +1,124 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject');
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+var args = WScript.Arguments;
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\clean.js').join('');
+
+
+// help function
+function Usage() {
+    Log("");
+    Log("Usage: clean [ --debug | --release ]");
+    Log("    --debug   : Cleans generated debug files in project.");
+    Log("    --release : Cleans generated release files in project.");
+    Log("examples:");
+    Log("    clean --debug");
+    Log("    clean");
+    Log("         - deletes all generated files in project");
+    Log("");
+}
+
+//  logs to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// cleans any generated files in the cordova project
+function clean_project(path) {
+    if (fso.FolderExists(path + "\\obj")) {
+        fso.DeleteFolder(path + "\\obj");
+    }
+    if (fso.FolderExists(path + "\\Bin")) {
+        fso.DeleteFolder(path + "\\Bin");
+    }
+    //TODO: delete CordovaAppProj.csproj.user as well? Service References?
+}
+
+// cleans any files generated by build --debug
+function clean_debug(path) {
+    if (fso.FolderExists(path + "\\obj\\Debug")) {
+        fso.DeleteFolder(path + "\\obj\\Debug");
+    }
+    if (fso.FolderExists(path + "\\Bin\\Debug")) {
+        fso.DeleteFolder(path + "\\Bin\\Debug");
+    }
+}
+
+// cleans any files generated by build --release
+function clean_release(path) {
+    if (fso.FolderExists(path + "\\obj\\Release")) {
+        fso.DeleteFolder(path + "\\obj\\Release");
+    }
+    if (fso.FolderExists(path + "\\Bin\\Release")) {
+        fso.DeleteFolder(path + "\\Bin\\Release");
+    }
+}
+
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help") {
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (args.Count() > 1) {
+        Log("Error: Too many arguments.", true);
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (fso.FolderExists(ROOT)) {
+        if (args(0) == "--debug" || args(0) == "-d") {
+            clean_debug(ROOT);
+        }
+        else if (args(0) == "--release" || args(0) == "-r") {
+            clean_release(ROOT);
+        }
+        else {
+            Log("Error: \"" + args(0) + "\" is not recognized as a build option", true);
+            Usage();
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log("Error: Project directory not found,", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
+else {
+   if (fso.FolderExists(ROOT)) {
+        Log("Cleaning cordova project...");
+        clean_project(ROOT);
+    }
+    else {
+        Log("Error: Project directory not found,", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/deploy.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/deploy.js b/lib/cordova-wp8/templates/standalone/cordova/lib/deploy.js
new file mode 100644
index 0000000..29a3f7d
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/deploy.js
@@ -0,0 +1,326 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject');
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments;
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\deploy.js').join('');
+    // path to CordovaDeploy.exe
+var CORDOVA_DEPLOY_EXE = '\\cordova\\lib\\CordovaDeploy\\CordovaDeploy\\bin\\Debug\\CordovaDeploy.exe';
+    // path to CordovaDeploy
+var CORDOVA_DEPLOY = '\\cordova\\lib\\CordovaDeploy';
+
+//build types
+var NONE = 0,
+    DEBUG = 1,
+    RELEASE = 2,
+    NO_BUILD = 3;
+var build_type = NONE;
+
+
+// help function
+function Usage() {
+    Log("");
+    Log("Usage: run [ --device | --emulator | --target=<id> ] [ --debug | --release | --nobuild ]");
+    Log("    --device      : Deploys and runs the project on the connected device.");
+    Log("    --emulator    : Deploys and runs the project on an emulator.");
+    Log("    --target=<id> : Deploys and runs the project on the specified target.");
+    Log("    --debug       : Builds project in debug mode.");
+    Log("    --release     : Builds project in release mode.");
+    Log("    --nobuild     : Ueses pre-built xap, or errors if project is not built.");
+    Log("examples:");
+    Log("    run");
+    Log("    run --emulator");
+    Log("    run --device");
+    Log("    run --target=7988B8C3-3ADE-488d-BA3E-D052AC9DC710");
+    Log("    run --device --release");
+    Log("    run --emulator --debug");
+    Log("");
+}
+
+// log to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+} 
+
+var ForReading = 1, ForWriting = 2, ForAppending = 8;
+var TristateUseDefault = 2, TristateTrue = 1, TristateFalse = 0;
+
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        WScript.sleep(100);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    //Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if (!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadAll();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oShell.StdErr.AtEndOfStream) {
+        var line = oShell.StdErr.ReadAll();
+        Log(line, true);
+        WScript.Quit(2);
+    }
+}
+
+// returns the contents of a file
+function read(filename) {
+    if (fso.FileExists(filename)) {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else {
+        Log('Cannot read non-existant file : ' + filename, true);
+        WScript.Quit(2);
+    }
+    return null;
+}
+
+// builds the CordovaDeploy.exe if it does not already exist 
+function cordovaDeploy(path) {
+    if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        return;
+    }
+
+    Log('CordovaDeploy.exe not found, attempting to build CordovaDeploy.exe...');
+
+    // build CordovaDeploy.exe
+    if (fso.FolderExists(path + '\\cordova') && fso.FolderExists(path + CORDOVA_DEPLOY) && 
+        fso.FileExists(path + CORDOVA_DEPLOY + '\\CordovaDeploy.sln')) {
+        // delete any previously generated files
+        if (fso.FolderExists(path + CORDOVA_DEPLOY + '\\CordovaDeploy\\obj')) {
+            fso.DeleteFolder(path + CORDOVA_DEPLOY + '\\CordovaDeploy\\obj');
+        }
+        if (fso.FolderExists(path + CORDOVA_DEPLOY + '\\CordovaDeploy\\Bin')) {
+            fso.DeleteFolder(path + CORDOVA_DEPLOY + '\\CordovaDeploy\\Bin');
+        }
+        exec_verbose('msbuild ' + path + CORDOVA_DEPLOY + '\\CordovaDeploy.sln');
+
+        if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+            Log('CordovaDeploy.exe compiled, SUCCESS.');
+        }
+        else {
+            Log('ERROR: MSBUILD FAILED TO COMPILE CordovaDeploy.exe', true);
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log('ERROR: CordovaDeploy.sln not found, unable to compile CordovaDeploy tool.', true);
+        WScript.Quit(2);
+    }
+}
+
+// launches project on device
+function device(path)
+{
+    cordovaDeploy(path);
+    if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        Log('Deploying to device ...');
+        //TODO: get device ID from list-devices and deploy to first one
+        exec_verbose('%comspec% /c ' + path + CORDOVA_DEPLOY_EXE + ' ' + path + ' -d:0');
+    }
+    else
+    {
+        Log('Error: Failed to find CordovaDeploy.exe in ' + path, true);
+        Log('DEPLOY FAILED.', true);
+        WScript.Quit(2);
+    }
+}
+
+// launches project on emulator
+function emulator(path)
+{
+    cordovaDeploy(path);
+    if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        Log('Deploying to emulator ...');
+        //TODO: get emulator ID from list-emulators and deploy to first one
+        exec_verbose('%comspec% /c ' + path + CORDOVA_DEPLOY_EXE + ' ' + path + ' -d:1');
+    }
+    else
+    {
+        Log('Error: Failed to find CordovaDeploy.exe in ' + path, true);
+        Log('DEPLOY FAILED.', true);
+        WScript.Quit(2);
+    }
+}
+
+// builds and launches the project on the specified target
+function target(path, device_id) {
+    if (!fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        cordovaDeploy(path);
+    }
+    wscript_shell.CurrentDirectory = path + CORDOVA_DEPLOY + '\\CordovaDeploy\\bin\\Debug';
+    var cmd = 'CordovaDeploy -devices';
+    var out = wscript_shell.Exec(cmd);
+    while(out.Status == 0) {
+        WScript.Sleep(100);
+    }
+    if (!out.StdErr.AtEndOfStream) {
+        var line = out.StdErr.ReadAll();
+        Log("Error calling CordovaDeploy : ", true);
+        Log(line, true);
+        WScript.Quit(2);
+    }
+    else {
+        if (!out.StdOut.AtEndOfStream) {
+            var line = out.StdOut.ReadAll();
+            var targets = line.split('\r\n');
+            var check_id = new RegExp(device_id);
+            for (target in targets) {
+                if (targets[target].match(check_id)) {
+                    //TODO: this only gets single digit index, account for device index of 10+?
+                    var index = targets[target].substr(0,1);
+                    exec_verbose(path + CORDOVA_DEPLOY_EXE + ' ' + path + ' -d:' + index);
+                    return;
+                }
+            }
+            Log('Error : target ' + device_id + ' was not found.', true);
+            Log('DEPLOY FAILED.', true);
+            WScript.Quit(2);
+        }
+        else {
+            Log('Error : CordovaDeploy Failed to find any devices', true);
+            Log('DEPLOY FAILED.', true);
+            WScript.Quit(2);
+        }
+    }
+}
+
+function build(path) {
+    switch (build_type) {
+        case DEBUG :
+            exec_verbose('%comspec% /c ' + ROOT + '\\cordova\\build --debug');
+            break;
+        case RELEASE :
+            exec_verbose('%comspec% /c ' + ROOT + '\\cordova\\build --release');
+            break;
+        case NO_BUILD :
+            break;
+        case NONE :
+            Log("WARNING: [ --debug | --release | --nobuild ] not specified, defaulting to --debug.");
+            exec_verbose('%comspec% /c ' + ROOT + '\\cordova\\build --debug');
+            break;
+        default :
+            Log("Build option not recognized: " + build_type, true);
+            WScript.Quit(2);
+            break;
+    }
+}
+
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help") {
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (args.Count() > 2) {
+        Log('Error: Too many arguments.', true);
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (fso.FolderExists(ROOT)) {
+        if (args.Count() > 1) {
+            if (args(1) == "--release") {
+                build_type = RELEASE;
+            }
+            else if (args(1) == "--debug") {
+                build_type = DEBUG;
+            }
+            else if (args(1) == "--nobuild") {
+                build_type = NO_BUILD;
+            }
+            else {
+                Log('Error: \"' + args(1) + '\" is not recognized as a deploy option', true);
+                Usage();
+                WScript.Quit(2);
+            }
+        }
+
+        if (args(0) == "--emulator" || args(0) == "-e") {
+            build(ROOT);
+            emulator(ROOT);
+        }
+        else if (args(0) == "--device" || args(0) == "-d") {
+            build(ROOT);
+            device(ROOT);
+        }
+        else if (args(0).substr(0,9) == "--target=") {
+            build(ROOT);
+            var device_id = args(0).split("--target=").join("");
+            target(ROOT, device_id);
+        }
+        else {
+            Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, defaulting to --emulator");
+            if (args(0) == "--release") {
+                build_type = RELEASE;
+                build(ROOT);
+                emulator(ROOT);
+            }
+            else if (args(0) == "--debug") {
+                build_type = DEBUG;
+                build(ROOT);
+                emulator(ROOT);
+            }
+            else if (args(0) == "--nobuild") {
+                build_type = NO_BUILD;
+                emulator(ROOT);
+            }
+            else {
+                Log('Error: \"' + args(0) + '\" is not recognized as a deploy option', true);
+                Usage();
+                WScript.Quit(2);
+            }
+        }
+    }
+    else {
+        Log('Error: Project directory not found,', true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
+else {
+    Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, defaulting to --emulator");
+    build(ROOT);
+    emulator(ROOT);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/install-device.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/install-device.bat b/lib/cordova-wp8/templates/standalone/cordova/lib/install-device.bat
new file mode 100644
index 0000000..9507c36
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/install-device.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%deploy.js (
+    cscript "%full_path%deploy.js" %* --device --nobuild //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'deploy.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/install-emulator.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/install-emulator.bat b/lib/cordova-wp8/templates/standalone/cordova/lib/install-emulator.bat
new file mode 100644
index 0000000..b3ee451
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/install-emulator.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%deploy.js (
+    cscript "%full_path%deploy.js" %* --emulator --nobuild //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'deploy.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/list-devices.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/list-devices.bat b/lib/cordova-wp8/templates/standalone/cordova/lib/list-devices.bat
new file mode 100644
index 0000000..bf4492b
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/list-devices.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%target-list.js (
+    cscript "%full_path%target-list.js" %* --devices //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'target-list.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/list-emulator-images.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/list-emulator-images.bat b/lib/cordova-wp8/templates/standalone/cordova/lib/list-emulator-images.bat
new file mode 100644
index 0000000..3f571c7
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/list-emulator-images.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%target-list.js (
+    cscript "%full_path%target-list.js" %* --emulators //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'target-list.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/list-started-emulators.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/list-started-emulators.bat b/lib/cordova-wp8/templates/standalone/cordova/lib/list-started-emulators.bat
new file mode 100644
index 0000000..d779b5d
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/list-started-emulators.bat
@@ -0,0 +1,3 @@
+@ECHO OFF
+ECHO Sorry, list-started-emulators is not availible yet for Windows Phone. 1>&2
+EXIT /B 1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/log.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/log.js b/lib/cordova-wp8/templates/standalone/cordova/lib/log.js
new file mode 100644
index 0000000..0b4ea7d
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/log.js
@@ -0,0 +1,77 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject');
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+var args = WScript.Arguments;
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\log.js').join('');
+
+
+// help function
+function Usage() {
+    Log("");
+    Log("Usage: log");
+    Log("examples:");
+    Log("    log");
+    Log("         - logs output from running application  *NOT IMPLIMENTED*");
+    Log("");
+}
+
+//  logs to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// log output from running projects *NOT IMPLEMENTED*
+function log_output(path) {
+    Log("ERROR: Logging is not supported on Windows Phone", true);
+    WScript.Quit(1);
+}
+
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help") {
+        Usage();
+        WScript.Quit(2);
+    }
+    else {
+        Log("Error: \"" + args(0) + "\" is not recognized as a log option.", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
+else {
+   if (fso.FolderExists(ROOT)) {
+        log_output(ROOT);
+    }
+    else {
+        Log("Error: Project directory not found,", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/start-emulator.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/start-emulator.bat b/lib/cordova-wp8/templates/standalone/cordova/lib/start-emulator.bat
new file mode 100644
index 0000000..19983fd
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/start-emulator.bat
@@ -0,0 +1,3 @@
+@ECHO OFF
+ECHO Sorry, start-emulator is not availible yet for Windows Phone. 1>&2
+EXIT /B 1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/lib/target-list.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/lib/target-list.js b/lib/cordova-wp8/templates/standalone/cordova/lib/target-list.js
new file mode 100644
index 0000000..805eea5
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/lib/target-list.js
@@ -0,0 +1,233 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject');
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments;
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\target-list.js').join('');
+    // path to CordovaDeploy.exe
+var CORDOVA_DEPLOY_EXE = '\\cordova\\lib\\CordovaDeploy\\CordovaDeploy\\bin\\Debug\\CordovaDeploy.exe';
+    // path to CordovaDeploy
+var CORDOVA_DEPLOY = '\\cordova\\lib\\CordovaDeploy';
+
+// help/usage function
+function Usage() {
+    Log("");
+    Log("Usage: cscript target-list.js  [ --emulators | --devices | --started_emulators | --all ]");
+    Log("    --emulators         : List the possible target emulators availible.");
+    Log("    --devices           : List the possible target devices availible. *NOT IMPLEMENTED YET*");
+    Log("    --started_emulators : List any started emulators availible. *NOT IMPLEMENTED YET*");
+    Log("    --all               : List all devices returned by CordovaDeploy.exe -devices ");
+    Log("examples:");
+    Log("    cscript target-list.js --emulators");
+    Log("    cscript target-list.js --devices");
+    Log("    cscript target-list.js --started_emulators");
+    Log("    cscript target-list.js --all");
+    Log("");
+}
+
+// logs messaged to stdout and stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print output? Naa.....
+        if (!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadAll();
+            //Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oShell.StdErr.AtEndOfStream) {
+        var line = oShell.StdErr.ReadAll();
+        Log(line, true);
+        WScript.Quit(2);
+    }
+}
+
+// returns all possible targets generated by the CordovaDeploy tool
+function get_targets(path) {
+    if (!fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        cordovaDeploy(path);
+    }
+    wscript_shell.CurrentDirectory = path + CORDOVA_DEPLOY + '\\CordovaDeploy\\bin\\Debug';
+    var cmd = 'CordovaDeploy -devices';
+    var out = wscript_shell.Exec(cmd);
+    while(out.Status == 0) {
+        WScript.Sleep(100);
+    }
+    //Check to make sure our script did not encounter an error
+    if (!out.StdErr.AtEndOfStream) {
+        var line = out.StdErr.ReadAll();
+        Log("Error calling CordovaDeploy : ", true);
+        Log(line, true);
+        WScript.Quit(2);
+    }
+    else {
+        if (!out.StdOut.AtEndOfStream) {
+            var line = out.StdOut.ReadAll();
+            var targets = line.split('\r\n');
+            //format (ID DESCRIPTION)
+            for (i in targets) {
+                // remove device index and separator colen
+                targets[i] = targets[i].replace(/\d*\s\:\s/, '').replace(/\:\s/, '');
+            }
+            return targets;
+        }
+        else {
+            Log('Error : CordovaDeploy Failed to find any devices', true);
+            WScript.Quit(2);
+        }
+    }
+}
+
+function list_targets(path) {
+    var targets = get_targets(path);
+    for (i in targets) {
+        Log(targets[i]);
+    }
+}
+
+// lists the Device returned by CordovaDeploy (NOTE: this does not indicate that a device is connected)
+function list_devices(path) {
+    var targets = get_targets(path);
+    var device_found = false;
+    for (i in targets) {
+        if (targets[i].match(/Device/)) {
+            Log(targets[i]);
+            device_found = true;
+        }
+    }
+    if (device_found) {
+        Log('');
+        Log('WARNING : This does not mean that a device is connected, make');
+        Log(' sure your device is connected before deploying to it.');
+    }
+}
+
+// lists the emulators availible to CordovaDeploy
+function list_emulator_images(path) {
+    var targets = get_targets(path);
+    for (i in targets) {
+        if (targets[i].match(/Emulator/)) {
+            Log(targets[i]);
+        }
+    }
+}
+
+// lists any started emulators *NOT IMPLEMENTED*
+function list_started_emulators(path) {
+    Log('ERROR : list-started-emulators is not supported on Windows Phone.', true);
+    WScript.Quit(1);
+}
+
+// builds the CordovaDeploy.exe if it does not already exist 
+function cordovaDeploy(path) {
+    if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        return;
+    }
+
+    // build CordovaDeploy.exe
+    if (fso.FolderExists(path + '\\cordova') && fso.FolderExists(path + CORDOVA_DEPLOY) && 
+        fso.FileExists(path + CORDOVA_DEPLOY + '\\CordovaDeploy.sln')) {
+        // delete any previously generated files
+        if (fso.FolderExists(path + CORDOVA_DEPLOY + "\\CordovaDeploy\\obj")) {
+            fso.DeleteFolder(path + CORDOVA_DEPLOY + "\\CordovaDeploy\\obj");
+        }
+        if (fso.FolderExists(path + CORDOVA_DEPLOY + "\\CordovaDeploy\\Bin")) {
+            fso.DeleteFolder(path + CORDOVA_DEPLOY + "\\CordovaDeploy\\Bin");
+        }
+        exec('msbuild ' + path + CORDOVA_DEPLOY + '\\CordovaDeploy.sln');
+
+        if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+            return;
+        }
+        else {
+            Log("ERROR: MSBUILD FAILED TO COMPILE CordovaDeploy.exe", true);
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log("ERROR: CordovaDeploy.sln not found, unable to compile CordovaDeploy tool.", true);
+        WScript.Quit(2);
+    }
+}
+
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help") {
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (args.Count() > 1) {
+        Log("Error: Too many arguments.", true);
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (fso.FolderExists(ROOT)) {
+        if (!fso.FolderExists(ROOT + '\\cordova')) {
+            Log("Error: cordova tooling folder not found in project directory,", true);
+            Log("could not lsit targets.", true);
+            WScript.Quit(2);
+        }
+
+        if (args(0) == "--emulators" || args(0) == "-e") {
+            list_emulator_images(ROOT);
+        }
+        else if (args(0) == "--devices" || args(0) == "-d") {
+            list_devices(ROOT);
+        }
+        else if (args(0) == "--started_emulators" || args(0) == "-s") {
+            list_started_emulators(ROOT);
+        }
+        else if (args(0) == "--all" || args(0) == "-a") {
+            list_targets(ROOT);
+        }
+        else {
+            Log("Error: \"" + args(0) + "\" is not recognized as a target-list option", true);
+            Usage();
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log("Error: Project directory not found,", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
+else {
+    Log("WARNING: target list not specified, showing all targets...");
+    list_targets(ROOT);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/log.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/log.bat b/lib/cordova-wp8/templates/standalone/cordova/log.bat
new file mode 100644
index 0000000..46dbe5c
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/log.bat
@@ -0,0 +1,3 @@
+@ECHO OFF
+ECHO Sorry, loging is yet supported for Windows Phone. 1>&2
+EXIT /B 1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordova/run.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordova/run.bat b/lib/cordova-wp8/templates/standalone/cordova/run.bat
new file mode 100644
index 0000000..b966856
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordova/run.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%lib\deploy.js (
+        cscript "%full_path%lib\deploy.js" %* //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'deploy.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)


[18/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/BrowserMouseHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/BrowserMouseHelper.cs b/lib/cordova-wp8/templates/standalone/cordovalib/BrowserMouseHelper.cs
new file mode 100644
index 0000000..fc83e03
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/BrowserMouseHelper.cs
@@ -0,0 +1,162 @@
+/*
+ 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. 
+ */
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Phone.Controls;
+using System.Windows.Input;
+using System.Diagnostics;
+using System.Windows.Media;
+using System;
+using System.Collections.Generic;
+
+namespace WPCordovaClassLib
+{
+
+    /// <summary>
+    /// Suppresses pinch zoom and optionally scrolling of the WebBrowser control
+    /// </summary>
+    public class BrowserMouseHelper
+    {
+        private WebBrowser _browser;
+
+        /// <summary>
+        /// Gets or sets whether to suppress the scrolling of
+        /// the WebBrowser control;
+        /// </summary>
+        public bool ScrollDisabled {
+            get;
+            set;
+        }
+
+        private bool userScalable = true;
+        private double maxScale = 2.0;
+        private double minScale = 0.5;
+        protected Border border;
+
+        /// <summary>
+        /// Represent min delta value to consider event as a mouse move. Experimental calculated.
+        /// </summary>
+        private const int MouseMoveDeltaThreshold = 10;
+
+        public BrowserMouseHelper(ref WebBrowser browser)
+        {
+            _browser = browser;
+            browser.Loaded += new RoutedEventHandler(browser_Loaded);
+        }
+
+        private void browser_Loaded(object sender, RoutedEventArgs e)
+        {
+            var border0 = VisualTreeHelper.GetChild(_browser, 0);
+            var border1 = VisualTreeHelper.GetChild(border0, 0);
+            var panZoom = VisualTreeHelper.GetChild(border1, 0);
+            var grid = VisualTreeHelper.GetChild(panZoom, 0);             
+            var grid2 = VisualTreeHelper.GetChild(grid, 0);
+            border = VisualTreeHelper.GetChild(grid2, 0) as Border;
+            
+            if (border != null)
+            {
+                border.ManipulationDelta += Border_ManipulationDelta;
+                border.ManipulationCompleted += Border_ManipulationCompleted;
+            }
+
+            _browser.LoadCompleted += Browser_LoadCompleted;
+
+        }
+
+        void ParseViewportMeta()
+        {
+            string metaScript = "(function() { return document.querySelector('meta[name=viewport]').content; })()";
+
+            try
+            {
+                string metaContent = _browser.InvokeScript("eval", new string[] { metaScript }) as string;
+                string[] arr = metaContent.Split(new[] { ' ', ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
+                Dictionary<string, string> metaDictionary = new Dictionary<string, string>();
+                foreach (string val in arr)
+                {
+                    string[] keyVal = val.Split('=');
+                    metaDictionary.Add(keyVal[0], keyVal[1]);
+                }
+
+                this.userScalable = false; // reset to default
+                if (metaDictionary.ContainsKey("user-scalable"))
+                {
+                    this.userScalable = metaDictionary["user-scalable"] == "yes";
+                }
+
+                this.maxScale = 2.0;// reset to default
+                if (metaDictionary.ContainsKey("maximum-scale"))
+                {
+                    this.maxScale = double.Parse(metaDictionary["maximum-scale"]);
+                }
+
+                this.minScale = 0.5;// reset to default
+                if (metaDictionary.ContainsKey("minimum-scale"))
+                {
+                    this.minScale = double.Parse(metaDictionary["minimum-scale"]);
+                }
+            }
+            catch (Exception)
+            {
+
+            }
+        }
+
+        void Browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            ParseViewportMeta();
+        }
+
+        #region ManipulationEvents
+
+        private void Border_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
+        {
+            //Debug.WriteLine("Border_ManipulationDelta");
+            // optionally suppress zoom
+            if ((ScrollDisabled || !userScalable) && (e.DeltaManipulation.Scale.X != 0.0 || e.DeltaManipulation.Scale.Y != 0.0))
+            {
+                e.Handled = true;
+            }
+            // optionally suppress scrolling
+            if (ScrollDisabled && (e.DeltaManipulation.Translation.X != 0.0 || e.DeltaManipulation.Translation.Y != 0.0))
+            {
+                e.Handled = true;
+            }
+        }
+
+        private void Border_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
+        {
+            //Debug.WriteLine("Border_ManipulationCompleted");
+            // suppress zoom
+            if (!userScalable && e.FinalVelocities != null)
+            {
+                if (e.FinalVelocities.ExpansionVelocity.X != 0.0 ||
+                   e.FinalVelocities.ExpansionVelocity.Y != 0.0)
+                {
+                    e.Handled = true;
+                }
+            }
+        }
+
+
+        #endregion
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/CommandFactory.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/CommandFactory.cs b/lib/cordova-wp8/templates/standalone/cordovalib/CommandFactory.cs
new file mode 100644
index 0000000..974ee79
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/CommandFactory.cs
@@ -0,0 +1,112 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Collections.Generic;
+using WPCordovaClassLib.Cordova.Commands;
+using System.Reflection;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova
+{
+    /// <summary>
+    /// Provides functionality to create Cordova command by name.
+    /// </summary>
+    public static class CommandFactory
+    {
+        /// <summary>
+        /// Represents predefined namespace name for custom plugins
+        /// </summary>
+        private static readonly string CustomPluginNamespacePrefix = "Cordova.Extension.Commands.";
+
+        private static readonly string BaseCommandNamespacePrefix = "WPCordovaClassLib.Cordova.Commands.";
+
+        /// <summary>
+        /// Cache instantiated commands in a map.
+        /// </summary>
+
+        private static Dictionary<string, BaseCommand> commandMap = new Dictionary<string, BaseCommand>();
+
+        /// <summary>
+        /// Creates command using command class name. Returns null for unknown commands.
+        /// </summary>
+        /// <param name="service">Command class name, for example Device or Notification</param>
+        /// <returns>Command class instance or null</returns>
+        public static BaseCommand CreateByServiceName(string service)
+        {
+
+            if (string.IsNullOrEmpty(service))
+            {
+                throw new ArgumentNullException("service", "service to create can't be null");
+            }
+
+            if (!commandMap.ContainsKey(service))
+            {
+
+                Type t = Type.GetType(BaseCommandNamespacePrefix + service);
+
+                // custom plugin could be defined in own namespace and assembly
+                if (t == null)
+                {
+                    string serviceFullName = service.Contains(".") ? service : CustomPluginNamespacePrefix + service;
+
+                    foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
+                    {
+                        // in this case service name represents full type name including namespace
+                        t = a.GetType(serviceFullName);
+
+                        if (t == null) // try the Commands Namespace
+                        {
+                            t = a.GetType(BaseCommandNamespacePrefix + service);
+                        }
+
+                        if (t != null)
+                        {
+                            break;
+                        }
+                    }
+
+                }
+
+                // unknown command, still didn't find it
+                if (t == null)
+                {
+                    Debug.WriteLine("Unable to locate command :: " + service);
+                    return null;
+                }
+
+                commandMap[service] = Activator.CreateInstance(t) as BaseCommand;
+            }
+
+            return commandMap[service];
+        }
+
+        public static void ResetAllCommands()
+        {
+            foreach (BaseCommand bc in commandMap.Values)
+            {
+                bc.OnReset();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/Commands/BaseCommand.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/Commands/BaseCommand.cs b/lib/cordova-wp8/templates/standalone/cordovalib/Commands/BaseCommand.cs
new file mode 100644
index 0000000..9de0e4d
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/Commands/BaseCommand.cs
@@ -0,0 +1,187 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Reflection;
+using Microsoft.Phone.Shell;
+using System.Diagnostics;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public abstract class BaseCommand : IDisposable
+    {
+        /*
+         *  All commands + plugins must extend BaseCommand, because they are dealt with as BaseCommands in CordovaView.xaml.cs
+         *  
+         **/
+
+        public event EventHandler<PluginResult> OnCommandResult;
+
+        public event EventHandler<ScriptCallback> OnCustomScript;
+
+        public string CurrentCommandCallbackId { get; set; }
+
+        public BaseCommand()
+        {
+            ResultHandlers = new Dictionary<string, EventHandler<PluginResult>>();
+            PhoneApplicationService service = PhoneApplicationService.Current;
+            service.Activated += this.OnResume;
+            service.Deactivated += this.OnPause;
+        }
+
+        protected Dictionary<string, EventHandler<PluginResult>> ResultHandlers;
+        public void AddResultHandler(string callbackId, EventHandler<PluginResult> handler)
+        {
+            ResultHandlers.Add(callbackId, handler);
+        }
+        public bool RemoveResultHandler(string callbackId)
+        {
+            return ResultHandlers.Remove(callbackId);
+        }
+
+        /*
+         *  InvokeMethodNamed will call the named method of a BaseCommand subclass if it exists and pass the variable arguments list along.
+         **/
+
+        public object InvokeMethodNamed(string callbackId, string methodName, params object[] args)
+        {
+            //Debug.WriteLine(string.Format("InvokeMethodNamed:{0} callbackId:{1}",methodName,callbackId));
+            this.CurrentCommandCallbackId = callbackId;
+            return InvokeMethodNamed(methodName, args);
+        }
+
+        public object InvokeMethodNamed(string methodName, params object[] args)
+        {
+            MethodInfo mInfo = this.GetType().GetMethod(methodName);
+
+            if (mInfo != null)
+            {
+                // every function handles DispatchCommandResult by itself
+                return mInfo.Invoke(this, args);
+            }
+
+            // actually methodName could refer to a property
+            if (args == null || args.Length == 0 ||
+               (args.Length == 1 && "undefined".Equals(args[0])))
+            {
+                PropertyInfo pInfo = this.GetType().GetProperty(methodName);
+                if (pInfo != null)
+                {
+                    object res = pInfo.GetValue(this, null);
+
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, res));
+
+                    return res;
+                }
+            }
+
+            throw new MissingMethodException(methodName);
+
+        }
+
+        [Obsolete]
+        public void InvokeCustomScript(ScriptCallback script, bool removeHandler)
+        {
+            if (this.OnCustomScript != null)
+            {
+                this.OnCustomScript(this, script);
+                if (removeHandler)
+                {
+                    this.OnCustomScript = null;
+                }
+            }
+        }
+
+        public void DispatchCommandResult()
+        {
+            this.DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
+        }
+
+        public void DispatchCommandResult(PluginResult result,string callbackId="")
+        {
+            if (!string.IsNullOrEmpty(callbackId)) 
+            {
+                result.CallbackId = callbackId;
+            }
+            else
+            {
+                result.CallbackId = this.CurrentCommandCallbackId;
+            }
+
+            if (ResultHandlers.ContainsKey(result.CallbackId))
+            {
+                ResultHandlers[result.CallbackId](this, result);
+            }
+            else if (this.OnCommandResult != null)
+            {
+                OnCommandResult(this, result);
+            }
+            else
+            {
+                Debug.WriteLine("Failed to locate callback for id : " + result.CallbackId);
+            }
+
+            if (!result.KeepCallback)
+            {
+                this.Dispose();
+            }
+
+        }
+
+
+        /// <summary>
+        /// Occurs when the application is being deactivated.
+        /// </summary>        
+        public virtual void OnReset() {}
+
+        /// <summary>
+        /// Occurs when the application is being loaded, and the config.xml has an autoload entry
+        /// </summary>    
+        public virtual void OnInit() {}
+
+
+        /// <summary>
+        /// Occurs when the application is being deactivated.
+        /// </summary>        
+        public virtual void OnPause(object sender, DeactivatedEventArgs e) {}
+
+        /// <summary>
+        /// Occurs when the application is being made active after previously being put
+        /// into a dormant state or tombstoned.
+        /// </summary>        
+        public virtual void OnResume(object sender, Microsoft.Phone.Shell.ActivatedEventArgs e) {}
+
+        public void Dispose()
+        {
+            PhoneApplicationService service = PhoneApplicationService.Current;
+            service.Activated -= this.OnResume;
+            service.Deactivated -= this.OnPause;
+            this.OnCommandResult = null;
+        }
+
+        public static string GetBaseURL()
+        {
+#if CORDOVA_CLASSLIB
+            return "/WPCordovaClassLib;component/";
+#else
+            return "./";
+#endif
+        }
+    }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/ConfigHandler.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/ConfigHandler.cs b/lib/cordova-wp8/templates/standalone/cordovalib/ConfigHandler.cs
new file mode 100644
index 0000000..06806d3
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/ConfigHandler.cs
@@ -0,0 +1,242 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Resources;
+using System.Xml.Linq;
+
+namespace WPCordovaClassLib.CordovaLib
+{
+    class ConfigHandler
+    {
+        public class PluginConfig
+        {
+            public PluginConfig(string name, bool autoLoad = false)
+            {
+                Name = name;
+                isAutoLoad = autoLoad;
+            }
+            public string Name;
+            public bool isAutoLoad;
+        }
+
+        protected Dictionary<string, PluginConfig> AllowedPlugins;
+        protected List<string> AllowedDomains;
+        protected Dictionary<string, string> Preferences;
+
+        protected bool AllowAllDomains = false;
+        protected bool AllowAllPlugins = false;
+
+        public ConfigHandler()
+        {
+            AllowedPlugins = new Dictionary<string, PluginConfig>();
+            AllowedDomains = new List<string>();
+            Preferences = new Dictionary<string, string>();
+        }
+
+        public string GetPreference(string key)
+        {
+            return Preferences[key];
+        }
+
+        protected static string[] AllowedSchemes = { "http", "https", "ftp", "ftps" };
+        protected bool SchemeIsAllowed(string scheme)
+        {
+            return AllowedSchemes.Contains(scheme);
+        }
+
+        protected void AddWhiteListEntry(string origin, bool allowSubdomains)
+        {
+
+            if (origin == "*")
+            {
+                AllowAllDomains = true;
+            }
+
+            if (AllowAllDomains)
+            {
+                return;
+            }
+
+            string hostMatchingRegex = "";
+            string hostName;
+
+            try
+            {
+
+                Uri uri = new Uri(origin.Replace("*", "replaced-text"), UriKind.Absolute);
+
+                string tempHostName = uri.Host.Replace("replaced-text", "*");
+                //if (uri.HostNameType == UriHostNameType.Dns){}        
+                // starts with wildcard match - we make the first '.' optional (so '*.org.apache.cordova' will match 'org.apache.cordova')
+                if (tempHostName.StartsWith("*."))
+                {    //"(\\s{0}|*.)"
+                    hostName = @"\w*.*" + tempHostName.Substring(2).Replace(".", @"\.").Replace("*", @"\w*");
+                }
+                else
+                {
+                    hostName = tempHostName.Replace(".", @"\.").Replace("*", @"\w*");
+                }
+                //  "^https?://"
+                hostMatchingRegex = uri.Scheme + "://" + hostName + uri.PathAndQuery;
+                Debug.WriteLine("Adding regex :: " + hostMatchingRegex);
+                AllowedDomains.Add(hostMatchingRegex);
+
+            }
+            catch (Exception)
+            {
+                Debug.WriteLine("Invalid Whitelist entry (probably missing the protocol):: " + origin);
+            }
+
+        }
+
+        /**   
+         
+         An access request is granted for a given URI if there exists an item inside the access-request list such that:
+
+            - The URI's scheme component is the same as scheme; and
+            - if subdomains is false or if the URI's host component is not a domain name (as defined in [RFC1034]), the URI's host component is the same as host; or
+            - if subdomains is true, the URI's host component is either the same as host, or is a subdomain of host (as defined in [RFC1034]); and
+            - the URI's port component is the same as port.
+         
+         **/
+
+        public bool URLIsAllowed(string url)
+        {
+            // Debug.WriteLine("Testing URLIsAllowed : " + url);
+            // easy case first
+            if (this.AllowAllDomains)
+            {
+                return true;
+            }
+            else
+            {
+                // start simple
+                Uri uri = new Uri(url, UriKind.RelativeOrAbsolute);
+                if (uri.IsAbsoluteUri)
+                {
+                    if (this.SchemeIsAllowed(uri.Scheme))
+                    {
+                        // additional test because our pattern will always have a trailing '/'
+                        string matchUrl = url;
+                        if (uri.PathAndQuery == "/")
+                        {
+                            matchUrl = url + "/";
+                        }
+                        foreach (string pattern in AllowedDomains)
+                        {
+                            if (Regex.IsMatch(matchUrl, pattern))
+                            {
+                                // make sure it is at the start, and not part of the query string
+                                // special case :: http://some.other.domain/page.html?x=1&g=http://build.apache.org/
+                                if (Regex.IsMatch(uri.Scheme + "://" + uri.Host + "/", pattern) ||
+                                     (!Regex.IsMatch(uri.PathAndQuery, pattern)))
+                                {
+                                    return true;
+                                }
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public bool IsPluginAllowed(string key)
+        {
+            return AllowAllPlugins || AllowedPlugins.Keys.Contains(key);
+        }
+
+        public string[] AutoloadPlugins
+        {
+            get
+            {
+                var res = from results in AllowedPlugins.TakeWhile(p => p.Value.isAutoLoad)
+                          select results.Value.Name;
+
+                foreach (var s in res)
+                {
+                    Debug.WriteLine(s);
+                }
+                //string[] res = from results in (AllowedPlugins.Where(p => p.Value.isAutoLoad) )
+                //                select (string)results.Key;
+
+                return new string[] { "", "asd" };
+            }
+        }
+
+
+        public void LoadAppPackageConfig()
+        {
+            StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("config.xml", UriKind.Relative));
+
+            if (streamInfo != null)
+            {
+                StreamReader sr = new StreamReader(streamInfo.Stream);
+                //This will Read Keys Collection for the xml file
+                XDocument document = XDocument.Parse(sr.ReadToEnd());
+
+                var plugins = from results in document.Descendants("plugin")
+                              select new
+                              {
+                                  name = (string)results.Attribute("name"),
+                                  autoLoad = results.Attribute("onload")
+                              };
+
+                foreach (var plugin in plugins)
+                {
+                    Debug.WriteLine("plugin " + plugin.name);
+                    PluginConfig pConfig = new PluginConfig(plugin.name, plugin.autoLoad != null && plugin.autoLoad.Value == "true");
+                    if (pConfig.Name == "*")
+                    {
+                        AllowAllPlugins = true;
+                        // break; wait, don't, some still could be autoload
+                    }
+                    else
+                    {
+                        AllowedPlugins.Add(pConfig.Name, pConfig);
+                    }
+                }
+
+                var preferences = from results in document.Descendants("preference")
+                                  select new
+                                  {
+                                      name = (string)results.Attribute("name"),
+                                      value = (string)results.Attribute("value")
+                                  };
+
+                foreach (var pref in preferences)
+                {
+                    Debug.WriteLine("pref" + pref.name + ", " + pref.value);
+                }
+
+                var accessList = from results in document.Descendants("access")
+                                 select new
+                                 {
+                                     origin = (string)results.Attribute("origin"),
+                                     subdomains = (string)results.Attribute("subdomains") == "true"
+                                 };
+
+                foreach (var accessElem in accessList)
+                {
+                    AddWhiteListEntry(accessElem.origin, accessElem.subdomains);
+                }
+            }
+            else
+            {
+                // no config.xml, allow all
+                AllowAllDomains = true;
+                AllowAllPlugins = true;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/CordovaCommandCall.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/CordovaCommandCall.cs b/lib/cordova-wp8/templates/standalone/cordovalib/CordovaCommandCall.cs
new file mode 100644
index 0000000..facc991
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/CordovaCommandCall.cs
@@ -0,0 +1,99 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace WPCordovaClassLib.Cordova
+{
+    /// <summary>
+    /// Represents Cordova native command call: action callback, etc
+    /// </summary>
+    public class CordovaCommandCall
+    {
+        public String Service { get; private set; }
+        public String Action { get; private set; }
+        public String CallbackId { get; private set; }
+        public String Args { get; private set; }
+
+        /// <summary>
+        /// Retrieves command call parameters and creates wrapper for them
+        /// </summary>
+        /// <param name="commandStr">Command string in the form 'service/action/callback/args'</param>
+        /// <returns>New class instance or null of string does not represent Cordova command</returns>
+        public static CordovaCommandCall Parse(string commandStr)
+        {
+            System.Diagnostics.Debug.WriteLine("CommandString : " + commandStr);
+            if (string.IsNullOrEmpty(commandStr))
+            {
+                return null;
+            }
+
+            string[] split = commandStr.Split('/');
+            if (split.Length < 3)
+            {
+                return null;
+            }
+
+            CordovaCommandCall commandCallParameters = new CordovaCommandCall();
+            commandCallParameters.Service = split[0];
+            commandCallParameters.Action = split[1];
+            commandCallParameters.CallbackId = split[2];
+
+            try
+            {
+                string arg = split.Length <= 3 ? "[]" : String.Join("/", split.Skip(3));
+                if (!arg.StartsWith("[")) // save the exception
+                {
+                    arg = string.Format("[{0}]", arg);
+                }
+                List<string> args = JSON.JsonHelper.Deserialize<List<string>>(arg);
+                args.Add(commandCallParameters.CallbackId);
+                commandCallParameters.Args = JSON.JsonHelper.Serialize(args.ToArray());
+            }
+            catch (Exception)
+            {
+                return null; 
+            }
+            // sanity check for illegal names
+            // was failing with ::
+            // CordovaCommandResult :: 1, Device1, {"status":1,"message":"{\"name\":\"XD.....
+            if (commandCallParameters.Service.IndexOfAny(new char[] { '@', ':', ',', '!', ' ' }) > -1)
+            {
+                return null;
+            }
+
+            return commandCallParameters;
+        }
+
+
+        /// <summary>
+        /// Private ctr to disable class creation.
+        /// New class instance must be initialized via CordovaCommandCall.Parse static method.
+        /// </summary>
+        private CordovaCommandCall() { }
+
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/CordovaView.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/CordovaView.xaml b/lib/cordova-wp8/templates/standalone/cordovalib/CordovaView.xaml
new file mode 100644
index 0000000..41d7631
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/CordovaView.xaml
@@ -0,0 +1,65 @@
+<!--
+ 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.  
+-->
+<UserControl x:Class="WPCordovaClassLib.CordovaView"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    d:DesignHeight="480" d:DesignWidth="480" 
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone">
+    
+    <Grid x:Name="LayoutRoot" Background="Transparent">
+        
+        <phone:WebBrowser x:Name="CordovaBrowser" 
+                          Opacity="0"
+                          HorizontalAlignment="Stretch"  
+                          VerticalAlignment="Stretch" 
+                          IsScriptEnabled="True" 
+                          Foreground="White"
+                          Background="Black"
+                          Navigated="CordovaBrowser_Navigated" 
+                          Loaded="CordovaBrowser_Loaded" 
+                          Unloaded="CordovaBrowser_Unloaded" 
+                          ScriptNotify="CordovaBrowser_ScriptNotify" 
+                          LoadCompleted="CordovaBrowser_LoadCompleted" 
+                          Navigating="CordovaBrowser_Navigating" 
+                          NavigationFailed="CordovaBrowser_NavigationFailed" 
+                          IsGeolocationEnabled="True">
+            <phone:WebBrowser.Projection>
+                <PlaneProjection x:Name="BrowserProjector" CenterOfRotationX="0" RotationY="-180"/>
+            </phone:WebBrowser.Projection>
+            <phone:WebBrowser.Resources>
+                <Storyboard x:Name="RotateIn" BeginTime="0:0:0.5">
+                    <DoubleAnimation
+                        Storyboard.TargetName="BrowserProjector"
+                        Storyboard.TargetProperty="RotationY"
+                        To="0" Duration="0:0:0.6"/>
+                </Storyboard>
+            </phone:WebBrowser.Resources>
+
+        </phone:WebBrowser>
+        
+    </Grid>
+</UserControl>
+
+    

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/CordovaView.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/CordovaView.xaml.cs b/lib/cordova-wp8/templates/standalone/cordovalib/CordovaView.xaml.cs
new file mode 100644
index 0000000..7b92f65
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/CordovaView.xaml.cs
@@ -0,0 +1,503 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+using System.IO.IsolatedStorage;
+using System.Windows.Resources;
+using System.Windows.Interop;
+using System.Runtime.Serialization.Json;
+using System.IO;
+using System.ComponentModel;
+using System.Xml.Linq;
+using WPCordovaClassLib.Cordova.Commands;
+using System.Diagnostics;
+using System.Text;
+using WPCordovaClassLib.Cordova;
+using System.Threading;
+using Microsoft.Phone.Shell;
+using WPCordovaClassLib.Cordova.JSON;
+using WPCordovaClassLib.CordovaLib;
+
+
+
+namespace WPCordovaClassLib
+{
+    public partial class CordovaView : UserControl
+    {
+
+        /// <summary>
+        /// Indicates whether web control has been loaded and no additional initialization is needed.
+        /// Prevents data clearing during page transitions.
+        /// </summary>
+        private bool IsBrowserInitialized = false;
+
+        /// <summary>
+        /// Set when the user attaches a back button handler inside the WebBrowser
+        /// </summary>
+        private bool OverrideBackButton = false;
+
+        /// <summary>
+        /// Sentinal to keep track of page changes as a result of the hardware back button
+        /// Set to false when the back-button is pressed, which calls js window.history.back()
+        /// If the page changes as a result of the back button the event is cancelled.
+        /// </summary>
+        private bool PageDidChange = false;
+
+        private static string AppRoot = "";
+
+
+        /// <summary>
+        /// Handles native api calls
+        /// </summary>
+        private NativeExecution nativeExecution;
+
+        protected BrowserMouseHelper bmHelper;
+        protected DOMStorageHelper domStorageHelper;
+        protected OrientationHelper orientationHelper;
+
+        private ConfigHandler configHandler;
+
+        public System.Windows.Controls.Grid _LayoutRoot
+        {
+            get
+            {
+                return ((System.Windows.Controls.Grid)(this.FindName("LayoutRoot")));
+            }
+        }
+
+        public WebBrowser Browser
+        {
+            get
+            {
+                return CordovaBrowser;
+            }
+        }
+
+        /*
+         * Setting StartPageUri only has an effect if called before the view is loaded.
+         **/
+        protected Uri _startPageUri = null;
+        public Uri StartPageUri
+        {
+            get
+            {
+                if (_startPageUri == null)
+                {
+                    // default
+                    return new Uri(AppRoot + "www/index.html", UriKind.Relative);
+                }
+                else
+                {
+                    return _startPageUri;
+                }
+            }
+            set
+            {
+                if (!this.IsBrowserInitialized)
+                {
+                    _startPageUri = value;
+                }
+            }
+        }
+       
+        /// <summary>
+        /// Gets or sets whether to suppress bouncy scrolling of
+        /// the WebBrowser control;
+        /// </summary>
+        public bool DisableBouncyScrolling
+        {
+            get;
+            set;
+        }
+
+        public CordovaView()
+        {
+
+            InitializeComponent();
+
+            if (DesignerProperties.IsInDesignTool)
+            {
+                return;
+            }
+
+
+            StartupMode mode = PhoneApplicationService.Current.StartupMode;
+
+            if (mode == StartupMode.Launch)
+            {
+                PhoneApplicationService service = PhoneApplicationService.Current;
+                service.Activated += new EventHandler<Microsoft.Phone.Shell.ActivatedEventArgs>(AppActivated);
+                service.Launching += new EventHandler<LaunchingEventArgs>(AppLaunching);
+                service.Deactivated += new EventHandler<DeactivatedEventArgs>(AppDeactivated);
+                service.Closing += new EventHandler<ClosingEventArgs>(AppClosing);
+            }
+            else
+            {
+
+            }
+
+            // initializes native execution logic
+            configHandler = new ConfigHandler();
+            configHandler.LoadAppPackageConfig();
+
+            nativeExecution = new NativeExecution(ref this.CordovaBrowser);
+            bmHelper = new BrowserMouseHelper(ref this.CordovaBrowser);
+        }
+
+
+
+        void AppClosing(object sender, ClosingEventArgs e)
+        {
+            Debug.WriteLine("AppClosing");
+        }
+
+        void AppDeactivated(object sender, DeactivatedEventArgs e)
+        {
+            Debug.WriteLine("INFO: AppDeactivated");
+
+            try
+            {
+                CordovaBrowser.InvokeScript("eval", new string[] { "cordova.fireDocumentEvent('pause');" });
+            }
+            catch (Exception)
+            {
+                Debug.WriteLine("ERROR: Pause event error");
+            }
+        }
+
+        void AppLaunching(object sender, LaunchingEventArgs e)
+        {
+            Debug.WriteLine("INFO: AppLaunching");
+        }
+
+        void AppActivated(object sender, Microsoft.Phone.Shell.ActivatedEventArgs e)
+        {
+            Debug.WriteLine("INFO: AppActivated");
+            try
+            {
+                CordovaBrowser.InvokeScript("eval", new string[] { "cordova.fireDocumentEvent('resume');" });
+            }
+            catch (Exception)
+            {
+                Debug.WriteLine("ERROR: Resume event error");
+            }
+        }
+
+        void CordovaBrowser_Loaded(object sender, RoutedEventArgs e)
+        {
+            this.bmHelper.ScrollDisabled = this.DisableBouncyScrolling;
+
+            if (DesignerProperties.IsInDesignTool)
+            {
+                return;
+            }
+
+            // prevents refreshing web control to initial state during pages transitions
+            if (this.IsBrowserInitialized) return;
+
+
+
+            this.domStorageHelper = new DOMStorageHelper(this.CordovaBrowser);
+
+            try
+            {
+
+                // Before we possibly clean the ISO-Store, we need to grab our generated UUID, so we can rewrite it after.
+                string deviceUUID = "";
+
+                using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    try
+                    {
+                        IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Open, FileAccess.Read, appStorage);
+
+                        using (StreamReader reader = new StreamReader(fileStream))
+                        {
+                            deviceUUID = reader.ReadLine();
+                        }
+                    }
+                    catch (Exception /*ex*/)
+                    {
+                        deviceUUID = Guid.NewGuid().ToString();
+                    }
+
+                    Debug.WriteLine("Updating IsolatedStorage for APP:DeviceID :: " + deviceUUID);
+                    IsolatedStorageFileStream file = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Create, FileAccess.Write, appStorage);
+                    using (StreamWriter writeFile = new StreamWriter(file))
+                    {
+                        writeFile.WriteLine(deviceUUID);
+                        writeFile.Close();
+                    }
+
+                }
+
+                /*
+                 * 11/08/12 Ruslan Kokorev
+                 * Copying files to isolated storage is no more required in WP8. WebBrowser control now works with files located in XAP.
+                */
+
+                //StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("CordovaSourceDictionary.xml", UriKind.Relative));
+
+                //if (streamInfo != null)
+                //{
+                //    StreamReader sr = new StreamReader(streamInfo.Stream);
+                //    //This will Read Keys Collection for the xml file
+
+                //    XDocument document = XDocument.Parse(sr.ReadToEnd());
+
+                //    var files = from results in document.Descendants("FilePath")
+                //                select new
+                //                {
+                //                    path = (string)results.Attribute("Value")
+                //                };
+                //    StreamResourceInfo fileResourceStreamInfo;
+
+                //    using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
+                //    {
+
+                //        foreach (var file in files)
+                //        {
+                //            fileResourceStreamInfo = Application.GetResourceStream(new Uri(file.path, UriKind.Relative));
+
+                //            if (fileResourceStreamInfo != null)
+                //            {
+                //                using (BinaryReader br = new BinaryReader(fileResourceStreamInfo.Stream))
+                //                {
+                //                    byte[] data = br.ReadBytes((int)fileResourceStreamInfo.Stream.Length);
+
+                //                    string strBaseDir = AppRoot + file.path.Substring(0, file.path.LastIndexOf(System.IO.Path.DirectorySeparatorChar));
+
+                //                    if (!appStorage.DirectoryExists(strBaseDir))
+                //                    {
+                //                        Debug.WriteLine("INFO: Creating Directory :: " + strBaseDir);
+                //                        appStorage.CreateDirectory(strBaseDir);
+                //                    }
+
+                //                    // This will truncate/overwrite an existing file, or 
+                //                    using (IsolatedStorageFileStream outFile = appStorage.OpenFile(AppRoot + file.path, FileMode.Create))
+                //                    {
+                //                        Debug.WriteLine("INFO: Writing data for " + AppRoot + file.path + " and length = " + data.Length);
+                //                        using (var writer = new BinaryWriter(outFile))
+                //                        {
+                //                            writer.Write(data);
+                //                        }
+                //                    }
+                //                }
+                //            }
+                //            else
+                //            {
+                //                Debug.WriteLine("ERROR: Failed to write file :: " + file.path + " did you forget to add it to the project?");
+                //            }
+                //        }
+                //    }
+                //}
+
+                CordovaBrowser.Navigate(StartPageUri);
+                IsBrowserInitialized = true;
+                AttachHardwareButtonHandlers();
+            }
+            catch (Exception ex)
+            {
+                Debug.WriteLine("ERROR: Exception in CordovaBrowser_Loaded :: {0}", ex.Message);
+            }
+        }
+
+        void AttachHardwareButtonHandlers()
+        {
+            PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+            if (frame != null)
+            {
+                PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+
+                if (page != null)
+                {
+                    page.BackKeyPress += new EventHandler<CancelEventArgs>(page_BackKeyPress);
+
+                    this.orientationHelper = new OrientationHelper(this.CordovaBrowser, page);
+
+                }
+            }
+        }
+
+        void page_BackKeyPress(object sender, CancelEventArgs e)
+        {
+
+            if (OverrideBackButton)
+            {
+                try
+                {
+                    CordovaBrowser.InvokeScript("eval", new string[] { "cordova.fireDocumentEvent('backbutton');" });
+                    e.Cancel = true;
+                }
+                catch (Exception ex)
+                {
+                    Console.WriteLine("Exception while invoking backbutton into cordova view: " + ex.Message);
+                }
+            }
+            else
+            {
+                try
+                {
+                    PageDidChange = false;
+
+                    Uri uriBefore = this.Browser.Source;
+                    // calling js history.back with result in a page change if history was valid.
+                    CordovaBrowser.InvokeScript("eval", new string[] { "(function(){window.history.back();})()" });
+
+                    Uri uriAfter = this.Browser.Source;
+
+                    e.Cancel = PageDidChange || (uriBefore != uriAfter);
+                }
+                catch (Exception)
+                {
+                    e.Cancel = false; // exit the app ... ?
+                }
+            }
+        }
+
+        void CordovaBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            string[] autoloadPlugs = this.configHandler.AutoloadPlugins;
+            foreach (string plugName in autoloadPlugs)
+            {
+               // nativeExecution.ProcessCommand(commandCallParams); 
+            }
+
+            string nativeReady = "(function(){ cordova.require('cordova/channel').onNativeReady.fire()})();";
+
+            try
+            {
+                CordovaBrowser.InvokeScript("execScript", new string[] { nativeReady });
+            }
+            catch (Exception /*ex*/)
+            {
+                Debug.WriteLine("Error calling js to fire nativeReady event. Did you include cordova-x.x.x.js in your html script tag?");
+            }
+
+            if (this.CordovaBrowser.Opacity < 1)
+            {
+                this.CordovaBrowser.Opacity = 1;
+                RotateIn.Begin();
+            }
+        }
+
+
+        void CordovaBrowser_Navigating(object sender, NavigatingEventArgs e)
+        {
+            if (!configHandler.URLIsAllowed(e.Uri.ToString()))
+            {
+                Debug.WriteLine("Whitelist exception: Stopping browser from navigating to :: " + e.Uri.ToString());
+                e.Cancel = true;
+                return;
+            }
+
+            this.PageDidChange = true;
+            this.nativeExecution.ResetAllCommands();
+        }
+
+        /*
+         *  This method does the work of routing commands
+         *  NotifyEventArgs.Value contains a string passed from JS 
+         *  If the command already exists in our map, we will just attempt to call the method(action) specified, and pass the args along
+         *  Otherwise, we create a new instance of the command, add it to the map, and call it ...
+         *  This method may also receive JS error messages caught by window.onerror, in any case where the commandStr does not appear to be a valid command
+         *  it is simply output to the debugger output, and the method returns.
+         * 
+         **/
+        void CordovaBrowser_ScriptNotify(object sender, NotifyEventArgs e)
+        {
+            string commandStr = e.Value;
+
+            if (commandStr.IndexOf("DOMStorage") == 0)
+            {
+                this.domStorageHelper.HandleStorageCommand(commandStr);
+                return;
+            }
+            else if (commandStr.IndexOf("Orientation") == 0)
+            {
+                this.orientationHelper.HandleCommand(commandStr);
+                return;
+            }
+
+            CordovaCommandCall commandCallParams = CordovaCommandCall.Parse(commandStr);
+
+            if (commandCallParams == null)
+            {
+                // ERROR
+                Debug.WriteLine("ScriptNotify :: " + commandStr);
+            }
+            else if (commandCallParams.Service == "CoreEvents")
+            {
+                switch (commandCallParams.Action.ToLower())
+                {
+                    case "overridebackbutton":
+                        string arg0 = JsonHelper.Deserialize<string[]>(commandCallParams.Args)[0];
+                        this.OverrideBackButton = (arg0 != null && arg0.Length > 0 && arg0.ToLower() == "true"); 
+                        break;
+                }
+            }
+            else
+            {
+                if (configHandler.IsPluginAllowed(commandCallParams.Service))
+                {
+                    nativeExecution.ProcessCommand(commandCallParams);
+                }
+                else
+                {
+                    Debug.WriteLine("Error::Plugin not allowed in config.xml. " + commandCallParams.Service); 
+                }
+            }
+        }
+
+        public void LoadPage(string url)
+        {
+            if (this.configHandler.URLIsAllowed(url))
+            {
+                this.CordovaBrowser.Navigate(new Uri(url, UriKind.RelativeOrAbsolute));
+            }
+            else
+            {
+                Debug.WriteLine("Oops, Can't load url based on config.xml :: " + url);
+            }
+        }
+
+        private void CordovaBrowser_Unloaded(object sender, RoutedEventArgs e)
+        {
+
+        }
+
+        private void CordovaBrowser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
+        {
+            Debug.WriteLine("CordovaBrowser_NavigationFailed :: " + e.Uri.ToString());
+        }
+
+        private void CordovaBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            Debug.WriteLine("CordovaBrowser_Navigated :: " + e.Uri.ToString());
+        }
+
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/DOMStorageHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/DOMStorageHelper.cs b/lib/cordova-wp8/templates/standalone/cordovalib/DOMStorageHelper.cs
new file mode 100644
index 0000000..01b6273
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/DOMStorageHelper.cs
@@ -0,0 +1,145 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.IO.IsolatedStorage;
+using System.Collections.Generic;
+using Microsoft.Phone.Controls;
+using System.Linq;
+using WPCordovaClassLib.Cordova.JSON;
+
+/*
+ * Translates DOMStorage API between JS and Isolated Storage
+ * Missing pieces : QUOTA_EXCEEDED_ERR  + StorageEvent  
+ * */
+
+namespace WPCordovaClassLib
+{
+    public class DOMStorageHelper
+    {
+        protected WebBrowser webBrowser1;
+
+        public DOMStorageHelper(WebBrowser aBrowser)
+        {
+            this.webBrowser1 = aBrowser;
+            // always clear session at creation
+            UserSettings["sessionStorage"] = new Dictionary<string, string>();
+
+            if (!UserSettings.Contains("localStorage"))
+            {
+                UserSettings["localStorage"] = new Dictionary<string, string>();
+                UserSettings.Save();
+            }
+            Application.Current.Exit += new EventHandler(OnAppExit);
+        }
+
+        void OnAppExit(object sender, EventArgs e)
+        {
+            UserSettings.Remove("sessionStorage");
+            UserSettings.Save();
+        }
+
+        protected IsolatedStorageSettings UserSettings
+        {
+            get
+            {
+                return IsolatedStorageSettings.ApplicationSettings;
+            }
+        }
+
+        protected Dictionary<string, string> getStorageByType(string type)
+        {
+            if (!UserSettings.Contains(type))
+            {
+                UserSettings[type] = new Dictionary<string, string>();
+                UserSettings.Save();
+            }
+            return UserSettings[type] as Dictionary<string, string>;
+        }
+
+
+        public void HandleStorageCommand(string commandStr)
+        {
+
+            string[] split = commandStr.Split('/');
+            if (split.Length > 3)
+            {
+                string api = split[0];
+                string type = split[1]; // localStorage || sessionStorage
+                string command = split[2];
+                string param = split[3];
+
+                Dictionary<string, string> currentStorage = getStorageByType(type);
+
+                switch (command)
+                {
+                    case "get":
+                        {
+
+                            if (currentStorage.Keys.Contains(param))
+                            {
+                                string value = currentStorage[param];
+                                webBrowser1.InvokeScript("execScript", "window." + type + ".onResult('" + param + "','" + value + "');");
+                            }
+                            else
+                            {
+                                webBrowser1.InvokeScript("execScript", "window." + type + ".onResult('" + param + "');");
+                            }
+
+                        }
+                        break;
+                    case "load":
+                        {
+                            string[] keys = currentStorage.Keys.ToArray();
+                            string jsonString = JsonHelper.Serialize(keys);
+                            string callbackJS = "window." + type + ".onKeysChanged('" + jsonString + "');";
+                            webBrowser1.InvokeScript("execScript", callbackJS);
+                        }
+                        break;
+                    case "set":
+                        {
+                            // TODO: check that length is not out of bounds
+                            currentStorage[param] = split[4];
+                            UserSettings.Save();
+                            string[] keys = currentStorage.Keys.ToArray();
+                            string jsonString = JsonHelper.Serialize(keys);
+                            string callbackJS = "window." + type + ".onKeysChanged('" + jsonString + "');";
+                            webBrowser1.InvokeScript("execScript", callbackJS);
+                        }
+                        break;
+                    case "remove":
+                        currentStorage.Remove(param);
+                        UserSettings.Save();
+                        break;
+                    case "clear":
+                        currentStorage = new Dictionary<string, string>();
+                        UserSettings[type] = currentStorage;
+                        UserSettings.Save();
+                        break;
+                }
+
+            }
+
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/JSON/JsonHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/JSON/JsonHelper.cs b/lib/cordova-wp8/templates/standalone/cordovalib/JSON/JsonHelper.cs
new file mode 100644
index 0000000..44511f6
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/JSON/JsonHelper.cs
@@ -0,0 +1,97 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Runtime.Serialization.Json;
+using System.IO;
+using System.Collections.Generic;
+using System.Text;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.JSON
+{
+    /// <summary>
+    /// Provides JSON serialization/deserialization functionality.
+    /// </summary>
+    public static class JsonHelper
+    {
+        /// <summary>
+        /// Serializes object to JSON string representation
+        /// </summary>
+        /// <param name="obj">object to serialize</param>
+        /// <returns>JSON representation of the object. Returns 'null' string for null passed as argument</returns>
+        public static string Serialize(object obj)
+        {
+            if (obj == null)
+            {
+                return "null";
+            }
+
+            DataContractJsonSerializer ser = new DataContractJsonSerializer(obj.GetType());
+
+            MemoryStream ms = new MemoryStream();
+            ser.WriteObject(ms, obj);
+
+            ms.Position = 0;
+
+            string json = String.Empty;
+
+            using (StreamReader sr = new StreamReader(ms))
+            {
+                json = sr.ReadToEnd();
+            }
+
+            ms.Close();
+
+            return json;
+
+        }
+
+        /// <summary>
+        /// Parses json string to object instance
+        /// </summary>
+        /// <typeparam name="T">type of the object</typeparam>
+        /// <param name="json">json string representation of the object</param>
+        /// <returns>Deserialized object instance</returns>
+        public static T Deserialize<T>(string json)
+        {
+            DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(T));
+            object result = null;
+            try
+            {
+                using (MemoryStream mem = new MemoryStream(Encoding.UTF8.GetBytes(json)))
+                {
+                    result = deserializer.ReadObject(mem);
+                }
+            }
+            catch (Exception ex)
+            {
+                Debug.WriteLine(ex.Message);
+                Debug.WriteLine("Failed to deserialize " + typeof(T) + " with JSON value :: " + json);
+            }
+
+            return (T)result;
+
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/NativeExecution.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/NativeExecution.cs b/lib/cordova-wp8/templates/standalone/cordovalib/NativeExecution.cs
new file mode 100644
index 0000000..af6b207
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/NativeExecution.cs
@@ -0,0 +1,246 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+using Microsoft.Devices;
+using Microsoft.Phone.Controls;
+using WPCordovaClassLib.Cordova.Commands;
+using System.Windows;
+
+namespace WPCordovaClassLib.Cordova
+{
+    /// <summary>
+    /// Implements logic to execute native command and return result back.
+    /// All commands are executed asynchronous.
+    /// </summary>
+    public class NativeExecution
+    {
+        /// <summary>
+        /// Reference to web part where application is hosted
+        /// </summary>
+        private readonly WebBrowser webBrowser;
+
+        /// <summary>
+        /// Creates new instance of a NativeExecution class. 
+        /// </summary>
+        /// <param name="browser">Reference to web part where application is hosted</param>
+        public NativeExecution(ref WebBrowser browser)
+        {
+            if (browser == null)
+            {
+                throw new ArgumentNullException("browser");
+            }
+
+            this.webBrowser = browser;
+        }
+
+        /// <summary>
+        /// Returns where application is running on emulator
+        /// </summary>
+        /// <returns>True if running on emulator, otherwise False</returns>
+        public static bool IsRunningOnEmulator()
+        {
+            return Microsoft.Devices.Environment.DeviceType == DeviceType.Emulator;
+        }
+
+        public void ResetAllCommands()
+        {
+            CommandFactory.ResetAllCommands();
+        }
+
+        public void AutoLoadCommand(string commandService)
+        {
+            BaseCommand bc = CommandFactory.CreateByServiceName(commandService);
+            if (bc != null)
+            {
+                bc.OnInit();
+            }
+
+        }
+
+        /// <summary>
+        /// Executes command and returns result back.
+        /// </summary>
+        /// <param name="commandCallParams">Command to execute</param>
+        public void ProcessCommand(CordovaCommandCall commandCallParams)
+        {
+
+            if (commandCallParams == null)
+            {
+                throw new ArgumentNullException("commandCallParams");
+            }
+
+            try
+            {
+                BaseCommand bc = CommandFactory.CreateByServiceName(commandCallParams.Service);
+
+                if (bc == null)
+                {
+                    this.OnCommandResult(commandCallParams.CallbackId, new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION));
+                    return;
+                }
+
+                EventHandler<PluginResult> OnCommandResultHandler = delegate(object o, PluginResult res)
+                {
+                    if (res.CallbackId == null || res.CallbackId == commandCallParams.CallbackId)
+                    {
+                        this.OnCommandResult(commandCallParams.CallbackId, res);
+                        if (!res.KeepCallback)
+                        {
+                            bc.RemoveResultHandler(commandCallParams.CallbackId);
+                        }
+                    }
+                };
+
+                //bc.OnCommandResult += OnCommandResultHandler;
+                bc.AddResultHandler(commandCallParams.CallbackId, OnCommandResultHandler);
+
+                EventHandler<ScriptCallback> OnCustomScriptHandler = delegate(object o, ScriptCallback script)
+                {
+                    this.InvokeScriptCallback(script);
+                };
+
+                bc.OnCustomScript += OnCustomScriptHandler;
+
+                ThreadStart methodInvokation = () =>
+                {
+                    try
+                    {
+                        bc.InvokeMethodNamed(commandCallParams.CallbackId,commandCallParams.Action, commandCallParams.Args);
+                    }
+                    catch (Exception ex)
+                    {
+                        Debug.WriteLine("ERROR: Exception in ProcessCommand :: " + ex.Message);
+                        bc.RemoveResultHandler(commandCallParams.CallbackId);
+                        bc.OnCustomScript -= OnCustomScriptHandler;
+
+                        Debug.WriteLine("ERROR: failed to InvokeMethodNamed :: " + commandCallParams.Action + " on Object :: " + commandCallParams.Service);
+                        this.OnCommandResult(commandCallParams.CallbackId, new PluginResult(PluginResult.Status.INVALID_ACTION));
+                        return;
+                    }
+                };
+
+                if ((bc is File) || (bc is Accelerometer))
+                {
+                    // Due to some issues with the IsolatedStorage in current version of WP8 SDK we have to run all File Api commands synchronously.
+                    // TODO: test this in WP8 RTM
+                    methodInvokation.Invoke();
+                }
+                else
+                {
+                    new Thread(methodInvokation).Start();
+                }
+
+
+            }
+            catch (Exception ex)
+            {
+                // ERROR
+                Debug.WriteLine(String.Format("ERROR: Unable to execute command :: {0}:{1}:{3} ",
+                    commandCallParams.Service, commandCallParams.Action, ex.Message));
+
+                this.OnCommandResult(commandCallParams.CallbackId, new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+        }
+
+        /// <summary>
+        /// Handles command execution result.
+        /// </summary>
+        /// <param name="callbackId">Command callback identifier on client side</param>
+        /// <param name="result">Execution result</param>
+        private void OnCommandResult(string callbackId, PluginResult result)
+        {
+            #region  args checking
+
+            if (result == null)
+            {
+                Debug.WriteLine("ERROR: OnCommandResult missing result argument");
+                return;
+            }
+
+            if (String.IsNullOrEmpty(callbackId))
+            {
+                Debug.WriteLine("ERROR: OnCommandResult missing callbackId argument");
+                return;
+            }
+
+            if (!String.IsNullOrEmpty(result.CallbackId) && callbackId != result.CallbackId)
+            {
+                Debug.WriteLine("Multiple Overlapping Results :: " + result.CallbackId + " :: " + callbackId);
+                return;
+            }
+
+            #endregion
+
+            string jsonResult = result.ToJSONString();
+
+            string callback;
+            string args = string.Format("('{0}',{1});", callbackId, jsonResult);
+
+            if (result.Result == PluginResult.Status.NO_RESULT ||
+               result.Result == PluginResult.Status.OK)
+            {
+                callback = @"(function(callbackId,args) {
+                try { args.message = JSON.parse(args.message); } catch (ex) { }
+                cordova.callbackSuccess(callbackId,args);
+                })" + args;
+            }
+            else
+            {
+                callback = @"(function(callbackId,args) {
+                try { args.message = JSON.parse(args.message); } catch (ex) { }
+                cordova.callbackError(callbackId,args);
+                })" + args;
+            }
+            this.InvokeScriptCallback(new ScriptCallback("eval", new string[] { callback }));
+
+        }
+
+        /// <summary>
+        /// Executes client java script
+        /// </summary>
+        /// <param name="script">Script to execute on client side</param>
+        private void InvokeScriptCallback(ScriptCallback script)
+        {
+            if (script == null)
+            {
+                throw new ArgumentNullException("script");
+            }
+
+            if (String.IsNullOrEmpty(script.ScriptName))
+            {
+                throw new ArgumentNullException("ScriptName");
+            }
+
+            //Debug.WriteLine("INFO:: About to invoke ::" + script.ScriptName + " with args ::" + script.Args[0]);
+            this.webBrowser.Dispatcher.BeginInvoke((ThreadStart)delegate()
+            {
+                try
+                {
+                    //Debug.WriteLine("INFO:: InvokingScript::" + script.ScriptName + " with args ::" + script.Args[0]);
+                    this.webBrowser.InvokeScript(script.ScriptName, script.Args);
+                }
+                catch (Exception ex)
+                {
+                    Debug.WriteLine("ERROR: Exception in InvokeScriptCallback :: " + ex.Message);
+                }
+
+            });
+        }
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/OrientationHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/OrientationHelper.cs b/lib/cordova-wp8/templates/standalone/cordovalib/OrientationHelper.cs
new file mode 100644
index 0000000..0e29b22
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/OrientationHelper.cs
@@ -0,0 +1,128 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+
+namespace WPCordovaClassLib.Cordova
+{
+    public class OrientationHelper
+    {
+        protected WebBrowser CordovaBrowser;
+        protected PhoneApplicationPage Page;
+        // private PageOrientation CurrentOrientation = PageOrientation.PortraitUp;
+        //private PageOrientation[] SupportedOrientations; // TODO:
+
+        public OrientationHelper(WebBrowser browser, PhoneApplicationPage page)
+        {
+            CordovaBrowser = browser;
+            Page = page;
+
+            Page.OrientationChanged += new EventHandler<OrientationChangedEventArgs>(page_OrientationChanged);
+            CordovaBrowser.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(browser_LoadCompleted);
+
+
+        }
+
+        void browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            int i = 0;
+
+            switch (Page.Orientation)
+            {
+                case PageOrientation.Portrait: // intentional fall through
+                case PageOrientation.PortraitUp:
+                    i = 0;
+                    break;
+                case PageOrientation.PortraitDown:
+                    i = 180;
+                    break;
+                case PageOrientation.Landscape: // intentional fall through
+                case PageOrientation.LandscapeLeft:
+                    i = -90;
+                    break;
+                case PageOrientation.LandscapeRight:
+                    i = 90;
+                    break;
+            }
+            // Cordova.fireEvent('orientationchange', window);
+            string jsCallback = String.Format("window.orientation = {0};", i);
+
+            try
+            {
+                CordovaBrowser.InvokeScript("execScript", jsCallback);
+            }
+            catch (Exception)
+            {
+            }
+        }
+
+        void page_OrientationChanged(object sender, OrientationChangedEventArgs e)
+        {
+            int i = 0;
+
+            switch (e.Orientation)
+            {
+                case PageOrientation.Portrait: // intentional fall through
+                case PageOrientation.PortraitUp:
+                    i = 0;
+                    break;
+                case PageOrientation.PortraitDown:
+                    i = 180;
+                    break;
+                case PageOrientation.Landscape: // intentional fall through
+                case PageOrientation.LandscapeLeft:
+                    i = -90;
+                    break;
+                case PageOrientation.LandscapeRight:
+                    i = 90;
+                    break;
+            }
+            // Cordova.fireEvent('orientationchange', window);
+            string jsCallback = String.Format("window.orientation = {0};", i);
+
+            try
+            {
+
+                CordovaBrowser.InvokeScript("execScript", jsCallback);
+
+                jsCallback = "var evt = document.createEvent('HTMLEvents');";
+                jsCallback += "evt.initEvent( 'orientationchange', true, false );";
+                jsCallback += "window.dispatchEvent(evt);";
+                jsCallback += "if(window.onorientationchange){window.onorientationchange(evt);}";
+
+                CordovaBrowser.InvokeScript("execScript", jsCallback);
+            }
+            catch (Exception)
+            {
+            }
+        }
+
+        public void HandleCommand(string commandStr)
+        {
+
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/PluginResult.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/PluginResult.cs b/lib/cordova-wp8/templates/standalone/cordovalib/PluginResult.cs
new file mode 100644
index 0000000..00017d2
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/PluginResult.cs
@@ -0,0 +1,139 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Text;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova
+{
+    /// <summary>
+    /// Represents command execution result
+    /// </summary>
+    public class PluginResult : EventArgs
+    {
+        /// <summary>
+        /// Predefined resultant messages
+        /// </summary>
+        public static string[] StatusMessages = new string[] 
+		{
+			"No result",
+			"OK",
+			"Class not found",
+			"Illegal access",
+			"Instantiation error",
+			"Malformed url",
+			"IO error",
+			"Invalid action",
+			"JSON error",
+			"Error"
+		};
+
+        /// <summary>
+        /// Possible command results status codes
+        /// </summary>
+        public enum Status : int
+        {
+            NO_RESULT = 0,
+            OK,
+            CLASS_NOT_FOUND_EXCEPTION,
+            ILLEGAL_ACCESS_EXCEPTION,
+            INSTANTIATION_EXCEPTION,
+            MALFORMED_URL_EXCEPTION,
+            IO_EXCEPTION,
+            INVALID_ACTION,
+            JSON_EXCEPTION,
+            ERROR
+        };
+
+        public Status Result { get; private set; }
+        public string Message { get; set; }
+        public bool KeepCallback { get; set; }
+        public string CallbackId { get; set; }
+
+        /// <summary>
+        /// Whether command succeded or not
+        /// </summary>
+        public bool IsSuccess
+        {
+            get
+            {
+                return this.Result == Status.OK || this.Result == Status.NO_RESULT;
+            }
+        }
+
+        /// <summary>
+        /// Creates new instance of the PluginResult class.
+        /// </summary>
+        /// <param name="status">Execution result</param>
+        public PluginResult(Status status)
+            : this(status, PluginResult.StatusMessages[(int)status])
+        {
+        }
+
+        /// <summary>
+        /// Creates new instance of the PluginResult class.
+        /// </summary>
+        /// <param name="status">Execution result</param>
+        /// <param name="message">The message</param>
+        public PluginResult(Status status, object message)
+        {
+            this.Result = status;
+            this.Message = JSON.JsonHelper.Serialize(message);
+        }
+
+        public string ToJSONString()
+        {
+            string res = String.Format("\"status\":{0},\"message\":{1},\"keepCallback\":{2}",
+                (int)this.Result,
+                this.Message,
+                this.KeepCallback.ToString().ToLower());
+
+            res = "{" + res + "}";
+            return res;
+
+        }
+
+        [Obsolete]
+        public string ToCallbackString(string callbackId, string successCallback, string errorCallback)
+        {
+            if (this.IsSuccess)
+            {
+                StringBuilder buf = new StringBuilder("");
+                buf.Append(String.Format("{0}('{1}',{2});", successCallback, callbackId, this.ToJSONString()));
+                return buf.ToString();
+            }
+            else
+            {
+                return String.Format("{0}('{1}',{2});", errorCallback, callbackId, this.ToJSONString());
+            }
+        }
+
+        public override String ToString()
+        {
+            return this.ToJSONString();
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/ScriptCallback.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/ScriptCallback.cs b/lib/cordova-wp8/templates/standalone/cordovalib/ScriptCallback.cs
new file mode 100644
index 0000000..7878134
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/cordovalib/ScriptCallback.cs
@@ -0,0 +1,80 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using WPCordovaClassLib.Cordova.JSON;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova
+{
+    /// <summary>
+    /// Represents client script function to execute
+    /// </summary>
+    public class ScriptCallback : EventArgs
+    {
+        /// <summary>
+        /// The scripting function to execute.
+        /// </summary>
+        public string ScriptName { get; private set; }
+
+        /// <summary>
+        /// A variable number of strings to pass to the function as parameters.
+        /// </summary>
+        public string[] Args { get; private set; }
+
+        /// <summary>
+        /// Creates new instance of a ScriptCallback class.
+        /// </summary>
+        /// <param name="function">The scripting function to execute</param>
+        /// <param name="args">A variable number of strings to pass to the function as parameters</param>
+        public ScriptCallback(string function, string[] args)
+        {
+            this.ScriptName = function;
+            this.Args = args;
+        }
+
+        /// <summary>
+        /// Creates new instance of a ScriptCallback class.
+        /// </summary>
+        /// <param name="function">The scripting function to execute</param>
+        /// <param name="id">The id argument</param>
+        /// <param name="msg">The message argument</param>
+        /// <param name="value">The value argument</param>
+        public ScriptCallback(string function, string id, object msg, object value)
+        {
+            this.ScriptName = function;
+
+            String arg = String.Format("{{\"id\": {0}, \"msg\": {1}, \"value\": {2}}}",
+                 JsonHelper.Serialize(id), JsonHelper.Serialize(msg), JsonHelper.Serialize(value));
+
+            this.Args = new string[] { arg };
+        }
+
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/cordovalib/resources/notification-beep.wav
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/cordovalib/resources/notification-beep.wav b/lib/cordova-wp8/templates/standalone/cordovalib/resources/notification-beep.wav
new file mode 100644
index 0000000..d0ad085
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/cordovalib/resources/notification-beep.wav differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/resources/notification-beep.wav
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/resources/notification-beep.wav b/lib/cordova-wp8/templates/standalone/resources/notification-beep.wav
new file mode 100644
index 0000000..d0ad085
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/resources/notification-beep.wav differ


[26/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/www/cordova-2.7.0.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/www/cordova-2.7.0.js b/lib/cordova-wp7/templates/standalone/www/cordova-2.7.0.js
new file mode 100644
index 0000000..f930212
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/www/cordova-2.7.0.js
@@ -0,0 +1,6700 @@
+// Platform: windowsphone
+
+// commit cd29cf0f224ccf25e9d422a33fd02ef67d3a78f4
+
+// File generated at :: Thu Apr 25 2013 15:58:48 GMT-0700 (Pacific Daylight Time)
+
+/*
+ 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.
+*/
+
+;(function() {
+
+// file: lib\scripts\require.js
+
+var require,
+    define;
+
+(function () {
+    var modules = {};
+    // Stack of moduleIds currently being built.
+    var requireStack = [];
+    // Map of module ID -> index into requireStack of modules currently being built.
+    var inProgressModules = {};
+
+    function build(module) {
+        var factory = module.factory;
+        module.exports = {};
+        delete module.factory;
+        factory(require, module.exports, module);
+        return module.exports;
+    }
+
+    require = function (id) {
+        if (!modules[id]) {
+            throw "module " + id + " not found";
+        } else if (id in inProgressModules) {
+            var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;
+            throw "Cycle in require graph: " + cycle;
+        }
+        if (modules[id].factory) {
+            try {
+                inProgressModules[id] = requireStack.length;
+                requireStack.push(id);
+                return build(modules[id]);
+            } finally {
+                delete inProgressModules[id];
+                requireStack.pop();
+            }
+        }
+        return modules[id].exports;
+    };
+
+    define = function (id, factory) {
+        if (modules[id]) {
+            throw "module " + id + " already defined";
+        }
+
+        modules[id] = {
+            id: id,
+            factory: factory
+        };
+    };
+
+    define.remove = function (id) {
+        delete modules[id];
+    };
+
+    define.moduleMap = modules;
+})();
+
+//Export for use in node
+if (typeof module === "object" && typeof require === "function") {
+    module.exports.require = require;
+    module.exports.define = define;
+}
+
+// file: lib/cordova.js
+define("cordova", function(require, exports, module) {
+
+
+var channel = require('cordova/channel');
+
+/**
+ * Listen for DOMContentLoaded and notify our channel subscribers.
+ */
+document.addEventListener('DOMContentLoaded', function() {
+    channel.onDOMContentLoaded.fire();
+}, false);
+if (document.readyState == 'complete' || document.readyState == 'interactive') {
+    channel.onDOMContentLoaded.fire();
+}
+
+/**
+ * Intercept calls to addEventListener + removeEventListener and handle deviceready,
+ * resume, and pause events.
+ */
+var m_document_addEventListener = document.addEventListener;
+var m_document_removeEventListener = document.removeEventListener;
+var m_window_addEventListener = window.addEventListener;
+var m_window_removeEventListener = window.removeEventListener;
+
+/**
+ * Houses custom event handlers to intercept on document + window event listeners.
+ */
+var documentEventHandlers = {},
+    windowEventHandlers = {};
+
+document.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    if (typeof documentEventHandlers[e] != 'undefined') {
+        documentEventHandlers[e].subscribe(handler);
+    } else {
+        m_document_addEventListener.call(document, evt, handler, capture);
+    }
+};
+
+window.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    if (typeof windowEventHandlers[e] != 'undefined') {
+        windowEventHandlers[e].subscribe(handler);
+    } else {
+        m_window_addEventListener.call(window, evt, handler, capture);
+    }
+};
+
+document.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    // If unsubscribing from an event that is handled by a plugin
+    if (typeof documentEventHandlers[e] != "undefined") {
+        documentEventHandlers[e].unsubscribe(handler);
+    } else {
+        m_document_removeEventListener.call(document, evt, handler, capture);
+    }
+};
+
+window.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    // If unsubscribing from an event that is handled by a plugin
+    if (typeof windowEventHandlers[e] != "undefined") {
+        windowEventHandlers[e].unsubscribe(handler);
+    } else {
+        m_window_removeEventListener.call(window, evt, handler, capture);
+    }
+};
+
+function createEvent(type, data) {
+    var event = document.createEvent('Events');
+    event.initEvent(type, false, false);
+    if (data) {
+        for (var i in data) {
+            if (data.hasOwnProperty(i)) {
+                event[i] = data[i];
+            }
+        }
+    }
+    return event;
+}
+
+if(typeof window.console === "undefined") {
+    window.console = {
+        log:function(){}
+    };
+}
+
+var cordova = {
+    define:define,
+    require:require,
+    /**
+     * Methods to add/remove your own addEventListener hijacking on document + window.
+     */
+    addWindowEventHandler:function(event) {
+        return (windowEventHandlers[event] = channel.create(event));
+    },
+    addStickyDocumentEventHandler:function(event) {
+        return (documentEventHandlers[event] = channel.createSticky(event));
+    },
+    addDocumentEventHandler:function(event) {
+        return (documentEventHandlers[event] = channel.create(event));
+    },
+    removeWindowEventHandler:function(event) {
+        delete windowEventHandlers[event];
+    },
+    removeDocumentEventHandler:function(event) {
+        delete documentEventHandlers[event];
+    },
+    /**
+     * Retrieve original event handlers that were replaced by Cordova
+     *
+     * @return object
+     */
+    getOriginalHandlers: function() {
+        return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
+        'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
+    },
+    /**
+     * Method to fire event from native code
+     * bNoDetach is required for events which cause an exception which needs to be caught in native code
+     */
+    fireDocumentEvent: function(type, data, bNoDetach) {
+        var evt = createEvent(type, data);
+        if (typeof documentEventHandlers[type] != 'undefined') {
+            if( bNoDetach ) {
+              documentEventHandlers[type].fire(evt);
+            }
+            else {
+              setTimeout(function() {
+                  // Fire deviceready on listeners that were registered before cordova.js was loaded.
+                  if (type == 'deviceready') {
+                      document.dispatchEvent(evt);
+                  }
+                  documentEventHandlers[type].fire(evt);
+              }, 0);
+            }
+        } else {
+            document.dispatchEvent(evt);
+        }
+    },
+    fireWindowEvent: function(type, data) {
+        var evt = createEvent(type,data);
+        if (typeof windowEventHandlers[type] != 'undefined') {
+            setTimeout(function() {
+                windowEventHandlers[type].fire(evt);
+            }, 0);
+        } else {
+            window.dispatchEvent(evt);
+        }
+    },
+
+    /**
+     * Plugin callback mechanism.
+     */
+    // Randomize the starting callbackId to avoid collisions after refreshing or navigating.
+    // This way, it's very unlikely that any new callback would get the same callbackId as an old callback.
+    callbackId: Math.floor(Math.random() * 2000000000),
+    callbacks:  {},
+    callbackStatus: {
+        NO_RESULT: 0,
+        OK: 1,
+        CLASS_NOT_FOUND_EXCEPTION: 2,
+        ILLEGAL_ACCESS_EXCEPTION: 3,
+        INSTANTIATION_EXCEPTION: 4,
+        MALFORMED_URL_EXCEPTION: 5,
+        IO_EXCEPTION: 6,
+        INVALID_ACTION: 7,
+        JSON_EXCEPTION: 8,
+        ERROR: 9
+    },
+
+    /**
+     * Called by native code when returning successful result from an action.
+     */
+    callbackSuccess: function(callbackId, args) {
+        try {
+            cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
+        } catch (e) {
+            console.log("Error in error callback: " + callbackId + " = "+e);
+        }
+    },
+
+    /**
+     * Called by native code when returning error result from an action.
+     */
+    callbackError: function(callbackId, args) {
+        // TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative.
+        // Derive success from status.
+        try {
+            cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
+        } catch (e) {
+            console.log("Error in error callback: " + callbackId + " = "+e);
+        }
+    },
+
+    /**
+     * Called by native code when returning the result from an action.
+     */
+    callbackFromNative: function(callbackId, success, status, args, keepCallback) {
+        var callback = cordova.callbacks[callbackId];
+        if (callback) {
+            if (success && status == cordova.callbackStatus.OK) {
+                callback.success && callback.success.apply(null, args);
+            } else if (!success) {
+                callback.fail && callback.fail.apply(null, args);
+            }
+
+            // Clear callback if not expecting any more results
+            if (!keepCallback) {
+                delete cordova.callbacks[callbackId];
+            }
+        }
+    },
+    addConstructor: function(func) {
+        channel.onCordovaReady.subscribe(function() {
+            try {
+                func();
+            } catch(e) {
+                console.log("Failed to run constructor: " + e);
+            }
+        });
+    }
+};
+
+// Register pause, resume and deviceready channels as events on document.
+channel.onPause = cordova.addDocumentEventHandler('pause');
+channel.onResume = cordova.addDocumentEventHandler('resume');
+channel.onDeviceReady = cordova.addStickyDocumentEventHandler('deviceready');
+
+module.exports = cordova;
+
+});
+
+// file: lib\common\argscheck.js
+define("cordova/argscheck", function(require, exports, module) {
+
+var exec = require('cordova/exec');
+var utils = require('cordova/utils');
+
+var moduleExports = module.exports;
+
+var typeMap = {
+    'A': 'Array',
+    'D': 'Date',
+    'N': 'Number',
+    'S': 'String',
+    'F': 'Function',
+    'O': 'Object'
+};
+
+function extractParamName(callee, argIndex) {
+  return (/.*?\((.*?)\)/).exec(callee)[1].split(', ')[argIndex];
+}
+
+function checkArgs(spec, functionName, args, opt_callee) {
+    if (!moduleExports.enableChecks) {
+        return;
+    }
+    var errMsg = null;
+    var typeName;
+    for (var i = 0; i < spec.length; ++i) {
+        var c = spec.charAt(i),
+            cUpper = c.toUpperCase(),
+            arg = args[i];
+        // Asterix means allow anything.
+        if (c == '*') {
+            continue;
+        }
+        typeName = utils.typeName(arg);
+        if ((arg === null || arg === undefined) && c == cUpper) {
+            continue;
+        }
+        if (typeName != typeMap[cUpper]) {
+            errMsg = 'Expected ' + typeMap[cUpper];
+            break;
+        }
+    }
+    if (errMsg) {
+        errMsg += ', but got ' + typeName + '.';
+        errMsg = 'Wrong type for parameter "' + extractParamName(opt_callee || args.callee, i) + '" of ' + functionName + ': ' + errMsg;
+        // Don't log when running jake test.
+        if (typeof jasmine == 'undefined') {
+            console.error(errMsg);
+        }
+        throw TypeError(errMsg);
+    }
+}
+
+function getValue(value, defaultValue) {
+    return value === undefined ? defaultValue : value;
+}
+
+moduleExports.checkArgs = checkArgs;
+moduleExports.getValue = getValue;
+moduleExports.enableChecks = true;
+
+
+});
+
+// file: lib\common\builder.js
+define("cordova/builder", function(require, exports, module) {
+
+var utils = require('cordova/utils');
+
+function each(objects, func, context) {
+    for (var prop in objects) {
+        if (objects.hasOwnProperty(prop)) {
+            func.apply(context, [objects[prop], prop]);
+        }
+    }
+}
+
+function clobber(obj, key, value) {
+    exports.replaceHookForTesting(obj, key);
+    obj[key] = value;
+    // Getters can only be overridden by getters.
+    if (obj[key] !== value) {
+        utils.defineGetter(obj, key, function() {
+            return value;
+        });
+    }
+}
+
+function assignOrWrapInDeprecateGetter(obj, key, value, message) {
+    if (message) {
+        utils.defineGetter(obj, key, function() {
+            console.log(message);
+            delete obj[key];
+            clobber(obj, key, value);
+            return value;
+        });
+    } else {
+        clobber(obj, key, value);
+    }
+}
+
+function include(parent, objects, clobber, merge) {
+    each(objects, function (obj, key) {
+        try {
+          var result = obj.path ? require(obj.path) : {};
+
+          if (clobber) {
+              // Clobber if it doesn't exist.
+              if (typeof parent[key] === 'undefined') {
+                  assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+              } else if (typeof obj.path !== 'undefined') {
+                  // If merging, merge properties onto parent, otherwise, clobber.
+                  if (merge) {
+                      recursiveMerge(parent[key], result);
+                  } else {
+                      assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+                  }
+              }
+              result = parent[key];
+          } else {
+            // Overwrite if not currently defined.
+            if (typeof parent[key] == 'undefined') {
+              assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+            } else {
+              // Set result to what already exists, so we can build children into it if they exist.
+              result = parent[key];
+            }
+          }
+
+          if (obj.children) {
+            include(result, obj.children, clobber, merge);
+          }
+        } catch(e) {
+          utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"');
+        }
+    });
+}
+
+/**
+ * Merge properties from one object onto another recursively.  Properties from
+ * the src object will overwrite existing target property.
+ *
+ * @param target Object to merge properties into.
+ * @param src Object to merge properties from.
+ */
+function recursiveMerge(target, src) {
+    for (var prop in src) {
+        if (src.hasOwnProperty(prop)) {
+            if (target.prototype && target.prototype.constructor === target) {
+                // If the target object is a constructor override off prototype.
+                clobber(target.prototype, prop, src[prop]);
+            } else {
+                if (typeof src[prop] === 'object' && typeof target[prop] === 'object') {
+                    recursiveMerge(target[prop], src[prop]);
+                } else {
+                    clobber(target, prop, src[prop]);
+                }
+            }
+        }
+    }
+}
+
+exports.buildIntoButDoNotClobber = function(objects, target) {
+    include(target, objects, false, false);
+};
+exports.buildIntoAndClobber = function(objects, target) {
+    include(target, objects, true, false);
+};
+exports.buildIntoAndMerge = function(objects, target) {
+    include(target, objects, true, true);
+};
+exports.recursiveMerge = recursiveMerge;
+exports.assignOrWrapInDeprecateGetter = assignOrWrapInDeprecateGetter;
+exports.replaceHookForTesting = function() {};
+
+});
+
+// file: lib\common\channel.js
+define("cordova/channel", function(require, exports, module) {
+
+var utils = require('cordova/utils'),
+    nextGuid = 1;
+
+/**
+ * Custom pub-sub "channel" that can have functions subscribed to it
+ * This object is used to define and control firing of events for
+ * cordova initialization, as well as for custom events thereafter.
+ *
+ * The order of events during page load and Cordova startup is as follows:
+ *
+ * onDOMContentLoaded*         Internal event that is received when the web page is loaded and parsed.
+ * onNativeReady*              Internal event that indicates the Cordova native side is ready.
+ * onCordovaReady*             Internal event fired when all Cordova JavaScript objects have been created.
+ * onCordovaInfoReady*         Internal event fired when device properties are available.
+ * onCordovaConnectionReady*   Internal event fired when the connection property has been set.
+ * onDeviceReady*              User event fired to indicate that Cordova is ready
+ * onResume                    User event fired to indicate a start/resume lifecycle event
+ * onPause                     User event fired to indicate a pause lifecycle event
+ * onDestroy*                  Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
+ *
+ * The events marked with an * are sticky. Once they have fired, they will stay in the fired state.
+ * All listeners that subscribe after the event is fired will be executed right away.
+ *
+ * The only Cordova events that user code should register for are:
+ *      deviceready           Cordova native code is initialized and Cordova APIs can be called from JavaScript
+ *      pause                 App has moved to background
+ *      resume                App has returned to foreground
+ *
+ * Listeners can be registered as:
+ *      document.addEventListener("deviceready", myDeviceReadyListener, false);
+ *      document.addEventListener("resume", myResumeListener, false);
+ *      document.addEventListener("pause", myPauseListener, false);
+ *
+ * The DOM lifecycle events should be used for saving and restoring state
+ *      window.onload
+ *      window.onunload
+ *
+ */
+
+/**
+ * Channel
+ * @constructor
+ * @param type  String the channel name
+ */
+var Channel = function(type, sticky) {
+    this.type = type;
+    // Map of guid -> function.
+    this.handlers = {};
+    // 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
+    this.state = sticky ? 1 : 0;
+    // Used in sticky mode to remember args passed to fire().
+    this.fireArgs = null;
+    // Used by onHasSubscribersChange to know if there are any listeners.
+    this.numHandlers = 0;
+    // Function that is called when the first listener is subscribed, or when
+    // the last listener is unsubscribed.
+    this.onHasSubscribersChange = null;
+},
+    channel = {
+        /**
+         * Calls the provided function only after all of the channels specified
+         * have been fired. All channels must be sticky channels.
+         */
+        join: function(h, c) {
+            var len = c.length,
+                i = len,
+                f = function() {
+                    if (!(--i)) h();
+                };
+            for (var j=0; j<len; j++) {
+                if (c[j].state === 0) {
+                    throw Error('Can only use join with sticky channels.');
+                }
+                c[j].subscribe(f);
+            }
+            if (!len) h();
+        },
+        create: function(type) {
+            return channel[type] = new Channel(type, false);
+        },
+        createSticky: function(type) {
+            return channel[type] = new Channel(type, true);
+        },
+
+        /**
+         * cordova Channels that must fire before "deviceready" is fired.
+         */
+        deviceReadyChannelsArray: [],
+        deviceReadyChannelsMap: {},
+
+        /**
+         * Indicate that a feature needs to be initialized before it is ready to be used.
+         * This holds up Cordova's "deviceready" event until the feature has been initialized
+         * and Cordova.initComplete(feature) is called.
+         *
+         * @param feature {String}     The unique feature name
+         */
+        waitForInitialization: function(feature) {
+            if (feature) {
+                var c = channel[feature] || this.createSticky(feature);
+                this.deviceReadyChannelsMap[feature] = c;
+                this.deviceReadyChannelsArray.push(c);
+            }
+        },
+
+        /**
+         * Indicate that initialization code has completed and the feature is ready to be used.
+         *
+         * @param feature {String}     The unique feature name
+         */
+        initializationComplete: function(feature) {
+            var c = this.deviceReadyChannelsMap[feature];
+            if (c) {
+                c.fire();
+            }
+        }
+    };
+
+function forceFunction(f) {
+    if (typeof f != 'function') throw "Function required as first argument!";
+}
+
+/**
+ * Subscribes the given function to the channel. Any time that
+ * Channel.fire is called so too will the function.
+ * Optionally specify an execution context for the function
+ * and a guid that can be used to stop subscribing to the channel.
+ * Returns the guid.
+ */
+Channel.prototype.subscribe = function(f, c) {
+    // need a function to call
+    forceFunction(f);
+    if (this.state == 2) {
+        f.apply(c || this, this.fireArgs);
+        return;
+    }
+
+    var func = f,
+        guid = f.observer_guid;
+    if (typeof c == "object") { func = utils.close(c, f); }
+
+    if (!guid) {
+        // first time any channel has seen this subscriber
+        guid = '' + nextGuid++;
+    }
+    func.observer_guid = guid;
+    f.observer_guid = guid;
+
+    // Don't add the same handler more than once.
+    if (!this.handlers[guid]) {
+        this.handlers[guid] = func;
+        this.numHandlers++;
+        if (this.numHandlers == 1) {
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+/**
+ * Unsubscribes the function with the given guid from the channel.
+ */
+Channel.prototype.unsubscribe = function(f) {
+    // need a function to unsubscribe
+    forceFunction(f);
+
+    var guid = f.observer_guid,
+        handler = this.handlers[guid];
+    if (handler) {
+        delete this.handlers[guid];
+        this.numHandlers--;
+        if (this.numHandlers === 0) {
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+/**
+ * Calls all functions subscribed to this channel.
+ */
+Channel.prototype.fire = function(e) {
+    var fail = false,
+        fireArgs = Array.prototype.slice.call(arguments);
+    // Apply stickiness.
+    if (this.state == 1) {
+        this.state = 2;
+        this.fireArgs = fireArgs;
+    }
+    if (this.numHandlers) {
+        // Copy the values first so that it is safe to modify it from within
+        // callbacks.
+        var toCall = [];
+        for (var item in this.handlers) {
+            toCall.push(this.handlers[item]);
+        }
+        for (var i = 0; i < toCall.length; ++i) {
+            toCall[i].apply(this, fireArgs);
+        }
+        if (this.state == 2 && this.numHandlers) {
+            this.numHandlers = 0;
+            this.handlers = {};
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+
+// defining them here so they are ready super fast!
+// DOM event that is received when the web page is loaded and parsed.
+channel.createSticky('onDOMContentLoaded');
+
+// Event to indicate the Cordova native side is ready.
+channel.createSticky('onNativeReady');
+
+// Event to indicate that all Cordova JavaScript objects have been created
+// and it's time to run plugin constructors.
+channel.createSticky('onCordovaReady');
+
+// Event to indicate that device properties are available
+channel.createSticky('onCordovaInfoReady');
+
+// Event to indicate that the connection property has been set.
+channel.createSticky('onCordovaConnectionReady');
+
+// Event to indicate that all automatically loaded JS plugins are loaded and ready.
+channel.createSticky('onPluginsReady');
+
+// Event to indicate that Cordova is ready
+channel.createSticky('onDeviceReady');
+
+// Event to indicate a resume lifecycle event
+channel.create('onResume');
+
+// Event to indicate a pause lifecycle event
+channel.create('onPause');
+
+// Event to indicate a destroy lifecycle event
+channel.createSticky('onDestroy');
+
+// Channels that must fire before "deviceready" is fired.
+channel.waitForInitialization('onCordovaReady');
+channel.waitForInitialization('onCordovaConnectionReady');
+channel.waitForInitialization('onDOMContentLoaded');
+
+module.exports = channel;
+
+});
+
+// file: lib\common\commandProxy.js
+define("cordova/commandProxy", function(require, exports, module) {
+
+
+// internal map of proxy function
+var CommandProxyMap = {};
+
+module.exports = {
+
+    // example: cordova.commandProxy.add("Accelerometer",{getCurrentAcceleration: function(successCallback, errorCallback, options) {...},...);
+    add:function(id,proxyObj) {
+        console.log("adding proxy for " + id);
+        CommandProxyMap[id] = proxyObj;
+        return proxyObj;
+    },
+
+    // cordova.commandProxy.remove("Accelerometer");
+    remove:function(id) {
+        var proxy = CommandProxyMap[id];
+        delete CommandProxyMap[id];
+        CommandProxyMap[id] = null;
+        return proxy;
+    },
+
+    get:function(service,action) {
+        return ( CommandProxyMap[service] ? CommandProxyMap[service][action] : null );
+    }
+};
+});
+
+// file: lib\windowsphone\exec.js
+define("cordova/exec", function(require, exports, module) {
+
+var cordova = require('cordova');
+
+/**
+ * Execute a cordova command.  It is up to the native side whether this action
+ * is synchronous or asynchronous.  The native side can return:
+ *      Synchronous: PluginResult object as a JSON string
+ *      Asynchronous: Empty string ""
+ * If async, the native side will cordova.callbackSuccess or cordova.callbackError,
+ * depending upon the result of the action.
+ *
+ * @param {Function} success    The success callback
+ * @param {Function} fail       The fail callback
+ * @param {String} service      The name of the service to use
+ * @param {String} action       Action to be run in cordova
+ * @param {String[]} [args]     Zero or more arguments to pass to the method
+
+ */
+
+module.exports = function(success, fail, service, action, args) {
+
+    var callbackId = service + cordova.callbackId++;
+    if (typeof success == "function" || typeof fail == "function") {
+        cordova.callbacks[callbackId] = {success:success, fail:fail};
+    }
+    // generate a new command string, ex. DebugConsole/log/DebugConsole23/["wtf dude?"]
+    for(var n = 0; n < args.length; n++)
+    {
+        if(typeof args[n] !== "string")
+        {
+            args[n] = JSON.stringify(args[n]);
+        }
+    }
+    var command = service + "/" + action + "/" + callbackId + "/" + JSON.stringify(args);
+    // pass it on to Notify
+    try {
+        if(window.external) {
+            window.external.Notify(command);
+        }
+        else {
+            console.log("window.external not available :: command=" + command);
+        }
+    }
+    catch(e) {
+        console.log("Exception calling native with command :: " + command + " :: exception=" + e);
+    }
+};
+
+
+});
+
+// file: lib\common\modulemapper.js
+define("cordova/modulemapper", function(require, exports, module) {
+
+var builder = require('cordova/builder'),
+    moduleMap = define.moduleMap,
+    symbolList,
+    deprecationMap;
+
+exports.reset = function() {
+    symbolList = [];
+    deprecationMap = {};
+};
+
+function addEntry(strategy, moduleName, symbolPath, opt_deprecationMessage) {
+    if (!(moduleName in moduleMap)) {
+        throw new Error('Module ' + moduleName + ' does not exist.');
+    }
+    symbolList.push(strategy, moduleName, symbolPath);
+    if (opt_deprecationMessage) {
+        deprecationMap[symbolPath] = opt_deprecationMessage;
+    }
+}
+
+// Note: Android 2.3 does have Function.bind().
+exports.clobbers = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('c', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+exports.merges = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('m', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+exports.defaults = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('d', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+function prepareNamespace(symbolPath, context) {
+    if (!symbolPath) {
+        return context;
+    }
+    var parts = symbolPath.split('.');
+    var cur = context;
+    for (var i = 0, part; part = parts[i]; ++i) {
+        cur = cur[part] = cur[part] || {};
+    }
+    return cur;
+}
+
+exports.mapModules = function(context) {
+    var origSymbols = {};
+    context.CDV_origSymbols = origSymbols;
+    for (var i = 0, len = symbolList.length; i < len; i += 3) {
+        var strategy = symbolList[i];
+        var moduleName = symbolList[i + 1];
+        var symbolPath = symbolList[i + 2];
+        var lastDot = symbolPath.lastIndexOf('.');
+        var namespace = symbolPath.substr(0, lastDot);
+        var lastName = symbolPath.substr(lastDot + 1);
+
+        var module = require(moduleName);
+        var deprecationMsg = symbolPath in deprecationMap ? 'Access made to deprecated symbol: ' + symbolPath + '. ' + deprecationMsg : null;
+        var parentObj = prepareNamespace(namespace, context);
+        var target = parentObj[lastName];
+
+        if (strategy == 'm' && target) {
+            builder.recursiveMerge(target, module);
+        } else if ((strategy == 'd' && !target) || (strategy != 'd')) {
+            if (!(symbolPath in origSymbols)) {
+                origSymbols[symbolPath] = target;
+            }
+            builder.assignOrWrapInDeprecateGetter(parentObj, lastName, module, deprecationMsg);
+        }
+    }
+};
+
+exports.getOriginalSymbol = function(context, symbolPath) {
+    var origSymbols = context.CDV_origSymbols;
+    if (origSymbols && (symbolPath in origSymbols)) {
+        return origSymbols[symbolPath];
+    }
+    var parts = symbolPath.split('.');
+    var obj = context;
+    for (var i = 0; i < parts.length; ++i) {
+        obj = obj && obj[parts[i]];
+    }
+    return obj;
+};
+
+exports.loadMatchingModules = function(matchingRegExp) {
+    for (var k in moduleMap) {
+        if (matchingRegExp.exec(k)) {
+            require(k);
+        }
+    }
+};
+
+exports.reset();
+
+
+});
+
+// file: lib\windowsphone\platform.js
+define("cordova/platform", function(require, exports, module) {
+
+var cordova = require('cordova'),
+      exec = require('cordova/exec');
+
+module.exports = {
+    id: "windowsphone",
+    initialize:function() {
+        var modulemapper = require('cordova/modulemapper');
+
+        modulemapper.loadMatchingModules(/cordova.*\/plugininit$/);
+
+        modulemapper.loadMatchingModules(/cordova.*\/symbols$/);
+
+        modulemapper.mapModules(window);
+
+        // Inject a listener for the backbutton, and tell native to override the flag (true/false) when we have 1 or more, or 0, listeners
+        var backButtonChannel = cordova.addDocumentEventHandler('backbutton');
+        backButtonChannel.onHasSubscribersChange = function() {
+            exec(null, null, "CoreEvents", "overridebackbutton", [this.numHandlers == 1]);
+        };
+    }
+};
+
+});
+
+// file: lib\common\plugin\Acceleration.js
+define("cordova/plugin/Acceleration", function(require, exports, module) {
+
+var Acceleration = function(x, y, z, timestamp) {
+    this.x = x;
+    this.y = y;
+    this.z = z;
+    this.timestamp = timestamp || (new Date()).getTime();
+};
+
+module.exports = Acceleration;
+
+});
+
+// file: lib\common\plugin\Camera.js
+define("cordova/plugin/Camera", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    Camera = require('cordova/plugin/CameraConstants'),
+    CameraPopoverHandle = require('cordova/plugin/CameraPopoverHandle');
+
+var cameraExport = {};
+
+// Tack on the Camera Constants to the base camera plugin.
+for (var key in Camera) {
+    cameraExport[key] = Camera[key];
+}
+
+/**
+ * Gets a picture from source defined by "options.sourceType", and returns the
+ * image as defined by the "options.destinationType" option.
+
+ * The defaults are sourceType=CAMERA and destinationType=FILE_URI.
+ *
+ * @param {Function} successCallback
+ * @param {Function} errorCallback
+ * @param {Object} options
+ */
+cameraExport.getPicture = function(successCallback, errorCallback, options) {
+    argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
+    options = options || {};
+    var getValue = argscheck.getValue;
+
+    var quality = getValue(options.quality, 50);
+    var destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
+    var sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
+    var targetWidth = getValue(options.targetWidth, -1);
+    var targetHeight = getValue(options.targetHeight, -1);
+    var encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG);
+    var mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE);
+    var allowEdit = !!options.allowEdit;
+    var correctOrientation = !!options.correctOrientation;
+    var saveToPhotoAlbum = !!options.saveToPhotoAlbum;
+    var popoverOptions = getValue(options.popoverOptions, null);
+    var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
+
+    var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
+                mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
+
+    exec(successCallback, errorCallback, "Camera", "takePicture", args);
+    return new CameraPopoverHandle();
+};
+
+cameraExport.cleanup = function(successCallback, errorCallback) {
+    exec(successCallback, errorCallback, "Camera", "cleanup", []);
+};
+
+module.exports = cameraExport;
+
+});
+
+// file: lib\common\plugin\CameraConstants.js
+define("cordova/plugin/CameraConstants", function(require, exports, module) {
+
+module.exports = {
+  DestinationType:{
+    DATA_URL: 0,         // Return base64 encoded string
+    FILE_URI: 1,         // Return file uri (content://media/external/images/media/2 for Android)
+    NATIVE_URI: 2        // Return native uri (eg. asset-library://... for iOS)
+  },
+  EncodingType:{
+    JPEG: 0,             // Return JPEG encoded image
+    PNG: 1               // Return PNG encoded image
+  },
+  MediaType:{
+    PICTURE: 0,          // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
+    VIDEO: 1,            // allow selection of video only, ONLY RETURNS URL
+    ALLMEDIA : 2         // allow selection from all media types
+  },
+  PictureSourceType:{
+    PHOTOLIBRARY : 0,    // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
+    CAMERA : 1,          // Take picture from camera
+    SAVEDPHOTOALBUM : 2  // Choose image from picture library (same as PHOTOLIBRARY for Android)
+  },
+  PopoverArrowDirection:{
+      ARROW_UP : 1,        // matches iOS UIPopoverArrowDirection constants to specify arrow location on popover
+      ARROW_DOWN : 2,
+      ARROW_LEFT : 4,
+      ARROW_RIGHT : 8,
+      ARROW_ANY : 15
+  },
+  Direction:{
+      BACK: 0,
+      FRONT: 1
+  }
+};
+
+});
+
+// file: lib\common\plugin\CameraPopoverHandle.js
+define("cordova/plugin/CameraPopoverHandle", function(require, exports, module) {
+
+var exec = require('cordova/exec');
+
+/**
+ * A handle to an image picker popover.
+ */
+var CameraPopoverHandle = function() {
+    this.setPosition = function(popoverOptions) {
+        console.log('CameraPopoverHandle.setPosition is only supported on iOS.');
+    };
+};
+
+module.exports = CameraPopoverHandle;
+
+});
+
+// file: lib\common\plugin\CameraPopoverOptions.js
+define("cordova/plugin/CameraPopoverOptions", function(require, exports, module) {
+
+var Camera = require('cordova/plugin/CameraConstants');
+
+/**
+ * Encapsulates options for iOS Popover image picker
+ */
+var CameraPopoverOptions = function(x,y,width,height,arrowDir){
+    // information of rectangle that popover should be anchored to
+    this.x = x || 0;
+    this.y = y || 32;
+    this.width = width || 320;
+    this.height = height || 480;
+    // The direction of the popover arrow
+    this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY;
+};
+
+module.exports = CameraPopoverOptions;
+
+});
+
+// file: lib\common\plugin\CaptureAudioOptions.js
+define("cordova/plugin/CaptureAudioOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all audio capture operation configuration options.
+ */
+var CaptureAudioOptions = function(){
+    // Upper limit of sound clips user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single sound clip in seconds.
+    this.duration = 0;
+    // The selected audio mode. Must match with one of the elements in supportedAudioModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureAudioOptions;
+
+});
+
+// file: lib\common\plugin\CaptureError.js
+define("cordova/plugin/CaptureError", function(require, exports, module) {
+
+/**
+ * The CaptureError interface encapsulates all errors in the Capture API.
+ */
+var CaptureError = function(c) {
+   this.code = c || null;
+};
+
+// Camera or microphone failed to capture image or sound.
+CaptureError.CAPTURE_INTERNAL_ERR = 0;
+// Camera application or audio capture application is currently serving other capture request.
+CaptureError.CAPTURE_APPLICATION_BUSY = 1;
+// Invalid use of the API (e.g. limit parameter has value less than one).
+CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
+// User exited camera application or audio capture application before capturing anything.
+CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
+// The requested capture operation is not supported.
+CaptureError.CAPTURE_NOT_SUPPORTED = 20;
+
+module.exports = CaptureError;
+
+});
+
+// file: lib\common\plugin\CaptureImageOptions.js
+define("cordova/plugin/CaptureImageOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all image capture operation configuration options.
+ */
+var CaptureImageOptions = function(){
+    // Upper limit of images user can take. Value must be equal or greater than 1.
+    this.limit = 1;
+    // The selected image mode. Must match with one of the elements in supportedImageModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureImageOptions;
+
+});
+
+// file: lib\common\plugin\CaptureVideoOptions.js
+define("cordova/plugin/CaptureVideoOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all video capture operation configuration options.
+ */
+var CaptureVideoOptions = function(){
+    // Upper limit of videos user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single video clip in seconds.
+    this.duration = 0;
+    // The selected video mode. Must match with one of the elements in supportedVideoModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureVideoOptions;
+
+});
+
+// file: lib\common\plugin\CompassError.js
+define("cordova/plugin/CompassError", function(require, exports, module) {
+
+/**
+ *  CompassError.
+ *  An error code assigned by an implementation when an error has occurred
+ * @constructor
+ */
+var CompassError = function(err) {
+    this.code = (err !== undefined ? err : null);
+};
+
+CompassError.COMPASS_INTERNAL_ERR = 0;
+CompassError.COMPASS_NOT_SUPPORTED = 20;
+
+module.exports = CompassError;
+
+});
+
+// file: lib\common\plugin\CompassHeading.js
+define("cordova/plugin/CompassHeading", function(require, exports, module) {
+
+var CompassHeading = function(magneticHeading, trueHeading, headingAccuracy, timestamp) {
+  this.magneticHeading = magneticHeading;
+  this.trueHeading = trueHeading;
+  this.headingAccuracy = headingAccuracy;
+  this.timestamp = timestamp || new Date().getTime();
+};
+
+module.exports = CompassHeading;
+
+});
+
+// file: lib\common\plugin\ConfigurationData.js
+define("cordova/plugin/ConfigurationData", function(require, exports, module) {
+
+/**
+ * Encapsulates a set of parameters that the capture device supports.
+ */
+function ConfigurationData() {
+    // The ASCII-encoded string in lower case representing the media type.
+    this.type = null;
+    // The height attribute represents height of the image or video in pixels.
+    // In the case of a sound clip this attribute has value 0.
+    this.height = 0;
+    // The width attribute represents width of the image or video in pixels.
+    // In the case of a sound clip this attribute has value 0
+    this.width = 0;
+}
+
+module.exports = ConfigurationData;
+
+});
+
+// file: lib\common\plugin\Connection.js
+define("cordova/plugin/Connection", function(require, exports, module) {
+
+/**
+ * Network status
+ */
+module.exports = {
+        UNKNOWN: "unknown",
+        ETHERNET: "ethernet",
+        WIFI: "wifi",
+        CELL_2G: "2g",
+        CELL_3G: "3g",
+        CELL_4G: "4g",
+        CELL:"cellular",
+        NONE: "none"
+};
+
+});
+
+// file: lib\common\plugin\Contact.js
+define("cordova/plugin/Contact", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    ContactError = require('cordova/plugin/ContactError'),
+    utils = require('cordova/utils');
+
+/**
+* Converts primitives into Complex Object
+* Currently only used for Date fields
+*/
+function convertIn(contact) {
+    var value = contact.birthday;
+    try {
+      contact.birthday = new Date(parseFloat(value));
+    } catch (exception){
+      console.log("Cordova Contact convertIn error: exception creating date.");
+    }
+    return contact;
+}
+
+/**
+* Converts Complex objects into primitives
+* Only conversion at present is for Dates.
+**/
+
+function convertOut(contact) {
+    var value = contact.birthday;
+    if (value !== null) {
+        // try to make it a Date object if it is not already
+        if (!utils.isDate(value)){
+            try {
+                value = new Date(value);
+            } catch(exception){
+                value = null;
+            }
+        }
+        if (utils.isDate(value)){
+            value = value.valueOf(); // convert to milliseconds
+        }
+        contact.birthday = value;
+    }
+    return contact;
+}
+
+/**
+* Contains information about a single contact.
+* @constructor
+* @param {DOMString} id unique identifier
+* @param {DOMString} displayName
+* @param {ContactName} name
+* @param {DOMString} nickname
+* @param {Array.<ContactField>} phoneNumbers array of phone numbers
+* @param {Array.<ContactField>} emails array of email addresses
+* @param {Array.<ContactAddress>} addresses array of addresses
+* @param {Array.<ContactField>} ims instant messaging user ids
+* @param {Array.<ContactOrganization>} organizations
+* @param {DOMString} birthday contact's birthday
+* @param {DOMString} note user notes about contact
+* @param {Array.<ContactField>} photos
+* @param {Array.<ContactField>} categories
+* @param {Array.<ContactField>} urls contact's web sites
+*/
+var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
+    ims, organizations, birthday, note, photos, categories, urls) {
+    this.id = id || null;
+    this.rawId = null;
+    this.displayName = displayName || null;
+    this.name = name || null; // ContactName
+    this.nickname = nickname || null;
+    this.phoneNumbers = phoneNumbers || null; // ContactField[]
+    this.emails = emails || null; // ContactField[]
+    this.addresses = addresses || null; // ContactAddress[]
+    this.ims = ims || null; // ContactField[]
+    this.organizations = organizations || null; // ContactOrganization[]
+    this.birthday = birthday || null;
+    this.note = note || null;
+    this.photos = photos || null; // ContactField[]
+    this.categories = categories || null; // ContactField[]
+    this.urls = urls || null; // ContactField[]
+};
+
+/**
+* Removes contact from device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.remove = function(successCB, errorCB) {
+    argscheck.checkArgs('FF', 'Contact.remove', arguments);
+    var fail = errorCB && function(code) {
+        errorCB(new ContactError(code));
+    };
+    if (this.id === null) {
+        fail(ContactError.UNKNOWN_ERROR);
+    }
+    else {
+        exec(successCB, fail, "Contacts", "remove", [this.id]);
+    }
+};
+
+/**
+* Creates a deep copy of this Contact.
+* With the contact ID set to null.
+* @return copy of this Contact
+*/
+Contact.prototype.clone = function() {
+    var clonedContact = utils.clone(this);
+    clonedContact.id = null;
+    clonedContact.rawId = null;
+
+    function nullIds(arr) {
+        if (arr) {
+            for (var i = 0; i < arr.length; ++i) {
+                arr[i].id = null;
+            }
+        }
+    }
+
+    // Loop through and clear out any id's in phones, emails, etc.
+    nullIds(clonedContact.phoneNumbers);
+    nullIds(clonedContact.emails);
+    nullIds(clonedContact.addresses);
+    nullIds(clonedContact.ims);
+    nullIds(clonedContact.organizations);
+    nullIds(clonedContact.categories);
+    nullIds(clonedContact.photos);
+    nullIds(clonedContact.urls);
+    return clonedContact;
+};
+
+/**
+* Persists contact to device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.save = function(successCB, errorCB) {
+    argscheck.checkArgs('FFO', 'Contact.save', arguments);
+    var fail = errorCB && function(code) {
+        errorCB(new ContactError(code));
+    };
+    var success = function(result) {
+        if (result) {
+            if (successCB) {
+                var fullContact = require('cordova/plugin/contacts').create(result);
+                successCB(convertIn(fullContact));
+            }
+        }
+        else {
+            // no Entry object returned
+            fail(ContactError.UNKNOWN_ERROR);
+        }
+    };
+    var dupContact = convertOut(utils.clone(this));
+    exec(success, fail, "Contacts", "save", [dupContact]);
+};
+
+
+module.exports = Contact;
+
+});
+
+// file: lib\common\plugin\ContactAddress.js
+define("cordova/plugin/ContactAddress", function(require, exports, module) {
+
+/**
+* Contact address.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code
+* @param formatted // NOTE: not a W3C standard
+* @param streetAddress
+* @param locality
+* @param region
+* @param postalCode
+* @param country
+*/
+
+var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
+    this.id = null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+    this.type = type || null;
+    this.formatted = formatted || null;
+    this.streetAddress = streetAddress || null;
+    this.locality = locality || null;
+    this.region = region || null;
+    this.postalCode = postalCode || null;
+    this.country = country || null;
+};
+
+module.exports = ContactAddress;
+
+});
+
+// file: lib\common\plugin\ContactError.js
+define("cordova/plugin/ContactError", function(require, exports, module) {
+
+/**
+ *  ContactError.
+ *  An error code assigned by an implementation when an error has occurred
+ * @constructor
+ */
+var ContactError = function(err) {
+    this.code = (typeof err != 'undefined' ? err : null);
+};
+
+/**
+ * Error codes
+ */
+ContactError.UNKNOWN_ERROR = 0;
+ContactError.INVALID_ARGUMENT_ERROR = 1;
+ContactError.TIMEOUT_ERROR = 2;
+ContactError.PENDING_OPERATION_ERROR = 3;
+ContactError.IO_ERROR = 4;
+ContactError.NOT_SUPPORTED_ERROR = 5;
+ContactError.PERMISSION_DENIED_ERROR = 20;
+
+module.exports = ContactError;
+
+});
+
+// file: lib\common\plugin\ContactField.js
+define("cordova/plugin/ContactField", function(require, exports, module) {
+
+/**
+* Generic contact field.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param type
+* @param value
+* @param pref
+*/
+var ContactField = function(type, value, pref) {
+    this.id = null;
+    this.type = (type && type.toString()) || null;
+    this.value = (value && value.toString()) || null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+};
+
+module.exports = ContactField;
+
+});
+
+// file: lib\common\plugin\ContactFindOptions.js
+define("cordova/plugin/ContactFindOptions", function(require, exports, module) {
+
+/**
+ * ContactFindOptions.
+ * @constructor
+ * @param filter used to match contacts against
+ * @param multiple boolean used to determine if more than one contact should be returned
+ */
+
+var ContactFindOptions = function(filter, multiple) {
+    this.filter = filter || '';
+    this.multiple = (typeof multiple != 'undefined' ? multiple : false);
+};
+
+module.exports = ContactFindOptions;
+
+});
+
+// file: lib\common\plugin\ContactName.js
+define("cordova/plugin/ContactName", function(require, exports, module) {
+
+/**
+* Contact name.
+* @constructor
+* @param formatted // NOTE: not part of W3C standard
+* @param familyName
+* @param givenName
+* @param middle
+* @param prefix
+* @param suffix
+*/
+var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
+    this.formatted = formatted || null;
+    this.familyName = familyName || null;
+    this.givenName = givenName || null;
+    this.middleName = middle || null;
+    this.honorificPrefix = prefix || null;
+    this.honorificSuffix = suffix || null;
+};
+
+module.exports = ContactName;
+
+});
+
+// file: lib\common\plugin\ContactOrganization.js
+define("cordova/plugin/ContactOrganization", function(require, exports, module) {
+
+/**
+* Contact organization.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param name
+* @param dept
+* @param title
+* @param startDate
+* @param endDate
+* @param location
+* @param desc
+*/
+
+var ContactOrganization = function(pref, type, name, dept, title) {
+    this.id = null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+    this.type = type || null;
+    this.name = name || null;
+    this.department = dept || null;
+    this.title = title || null;
+};
+
+module.exports = ContactOrganization;
+
+});
+
+// file: lib\common\plugin\Coordinates.js
+define("cordova/plugin/Coordinates", function(require, exports, module) {
+
+/**
+ * This class contains position information.
+ * @param {Object} lat
+ * @param {Object} lng
+ * @param {Object} alt
+ * @param {Object} acc
+ * @param {Object} head
+ * @param {Object} vel
+ * @param {Object} altacc
+ * @constructor
+ */
+var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
+    /**
+     * The latitude of the position.
+     */
+    this.latitude = lat;
+    /**
+     * The longitude of the position,
+     */
+    this.longitude = lng;
+    /**
+     * The accuracy of the position.
+     */
+    this.accuracy = acc;
+    /**
+     * The altitude of the position.
+     */
+    this.altitude = (alt !== undefined ? alt : null);
+    /**
+     * The direction the device is moving at the position.
+     */
+    this.heading = (head !== undefined ? head : null);
+    /**
+     * The velocity with which the device is moving at the position.
+     */
+    this.speed = (vel !== undefined ? vel : null);
+
+    if (this.speed === 0 || this.speed === null) {
+        this.heading = NaN;
+    }
+
+    /**
+     * The altitude accuracy of the position.
+     */
+    this.altitudeAccuracy = (altacc !== undefined) ? altacc : null;
+};
+
+module.exports = Coordinates;
+
+});
+
+// file: lib\common\plugin\DirectoryEntry.js
+define("cordova/plugin/DirectoryEntry", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    Entry = require('cordova/plugin/Entry'),
+    FileError = require('cordova/plugin/FileError'),
+    DirectoryReader = require('cordova/plugin/DirectoryReader');
+
+/**
+ * An interface representing a directory on the file system.
+ *
+ * {boolean} isFile always false (readonly)
+ * {boolean} isDirectory always true (readonly)
+ * {DOMString} name of the directory, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the directory (readonly)
+ * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly)
+ */
+var DirectoryEntry = function(name, fullPath) {
+     DirectoryEntry.__super__.constructor.call(this, false, true, name, fullPath);
+};
+
+utils.extend(DirectoryEntry, Entry);
+
+/**
+ * Creates a new DirectoryReader to read entries from this directory
+ */
+DirectoryEntry.prototype.createReader = function() {
+    return new DirectoryReader(this.fullPath);
+};
+
+/**
+ * Creates or looks up a directory
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
+ * @param {Flags} options to create or exclusively create the directory
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
+    argscheck.checkArgs('sOFF', 'DirectoryEntry.getDirectory', arguments);
+    var win = successCallback && function(result) {
+        var entry = new DirectoryEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]);
+};
+
+/**
+ * Deletes a directory and all of it's contents
+ *
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'DirectoryEntry.removeRecursively', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]);
+};
+
+/**
+ * Creates or looks up a file
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
+ * @param {Flags} options to create or exclusively create the file
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
+    argscheck.checkArgs('sOFF', 'DirectoryEntry.getFile', arguments);
+    var win = successCallback && function(result) {
+        var FileEntry = require('cordova/plugin/FileEntry');
+        var entry = new FileEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getFile", [this.fullPath, path, options]);
+};
+
+module.exports = DirectoryEntry;
+
+});
+
+// file: lib\common\plugin\DirectoryReader.js
+define("cordova/plugin/DirectoryReader", function(require, exports, module) {
+
+var exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError') ;
+
+/**
+ * An interface that lists the files and directories in a directory.
+ */
+function DirectoryReader(path) {
+    this.path = path || null;
+}
+
+/**
+ * Returns a list of entries from a directory.
+ *
+ * @param {Function} successCallback is called with a list of entries
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
+    var win = typeof successCallback !== 'function' ? null : function(result) {
+        var retVal = [];
+        for (var i=0; i<result.length; i++) {
+            var entry = null;
+            if (result[i].isDirectory) {
+                entry = new (require('cordova/plugin/DirectoryEntry'))();
+            }
+            else if (result[i].isFile) {
+                entry = new (require('cordova/plugin/FileEntry'))();
+            }
+            entry.isDirectory = result[i].isDirectory;
+            entry.isFile = result[i].isFile;
+            entry.name = result[i].name;
+            entry.fullPath = result[i].fullPath;
+            retVal.push(entry);
+        }
+        successCallback(retVal);
+    };
+    var fail = typeof errorCallback !== 'function' ? null : function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "readEntries", [this.path]);
+};
+
+module.exports = DirectoryReader;
+
+});
+
+// file: lib\common\plugin\Entry.js
+define("cordova/plugin/Entry", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError'),
+    Metadata = require('cordova/plugin/Metadata');
+
+/**
+ * Represents a file or directory on the local file system.
+ *
+ * @param isFile
+ *            {boolean} true if Entry is a file (readonly)
+ * @param isDirectory
+ *            {boolean} true if Entry is a directory (readonly)
+ * @param name
+ *            {DOMString} name of the file or directory, excluding the path
+ *            leading to it (readonly)
+ * @param fullPath
+ *            {DOMString} the absolute full path to the file or directory
+ *            (readonly)
+ */
+function Entry(isFile, isDirectory, name, fullPath, fileSystem) {
+    this.isFile = !!isFile;
+    this.isDirectory = !!isDirectory;
+    this.name = name || '';
+    this.fullPath = fullPath || '';
+    this.filesystem = fileSystem || null;
+}
+
+/**
+ * Look up the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ */
+Entry.prototype.getMetadata = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.getMetadata', arguments);
+    var success = successCallback && function(lastModified) {
+        var metadata = new Metadata(lastModified);
+        successCallback(metadata);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+
+    exec(success, fail, "File", "getMetadata", [this.fullPath]);
+};
+
+/**
+ * Set the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ * @param metadataObject
+ *            {Object} keys and values to set
+ */
+Entry.prototype.setMetadata = function(successCallback, errorCallback, metadataObject) {
+    argscheck.checkArgs('FFO', 'Entry.setMetadata', arguments);
+    exec(successCallback, errorCallback, "File", "setMetadata", [this.fullPath, metadataObject]);
+};
+
+/**
+ * Move a file or directory to a new location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to move this entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new DirectoryEntry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
+    argscheck.checkArgs('oSFF', 'Entry.moveTo', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    // source path
+    var srcPath = this.fullPath,
+        // entry name
+        name = newName || this.name,
+        success = function(entry) {
+            if (entry) {
+                if (successCallback) {
+                    // create appropriate Entry object
+                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
+                    successCallback(result);
+                }
+            }
+            else {
+                // no Entry object returned
+                fail && fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+
+    // copy
+    exec(success, fail, "File", "moveTo", [srcPath, parent.fullPath, name]);
+};
+
+/**
+ * Copy a directory to a different location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to copy the entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new Entry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
+    argscheck.checkArgs('oSFF', 'Entry.copyTo', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+
+        // source path
+    var srcPath = this.fullPath,
+        // entry name
+        name = newName || this.name,
+        // success callback
+        success = function(entry) {
+            if (entry) {
+                if (successCallback) {
+                    // create appropriate Entry object
+                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
+                    successCallback(result);
+                }
+            }
+            else {
+                // no Entry object returned
+                fail && fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+
+    // copy
+    exec(success, fail, "File", "copyTo", [srcPath, parent.fullPath, name]);
+};
+
+/**
+ * Return a URL that can be used to identify this entry.
+ */
+Entry.prototype.toURL = function() {
+    // fullPath attribute contains the full URL
+    return this.fullPath;
+};
+
+/**
+ * Returns a URI that can be used to identify this entry.
+ *
+ * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
+ * @return uri
+ */
+Entry.prototype.toURI = function(mimeType) {
+    console.log("DEPRECATED: Update your code to use 'toURL'");
+    // fullPath attribute contains the full URI
+    return this.toURL();
+};
+
+/**
+ * Remove a file or directory. It is an error to attempt to delete a
+ * directory that is not empty. It is an error to attempt to delete a
+ * root directory of a file system.
+ *
+ * @param successCallback {Function} called with no parameters
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.remove = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.remove', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, "File", "remove", [this.fullPath]);
+};
+
+/**
+ * Look up the parent DirectoryEntry of this entry.
+ *
+ * @param successCallback {Function} called with the parent DirectoryEntry object
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.getParent = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.getParent', arguments);
+    var win = successCallback && function(result) {
+        var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
+        var entry = new DirectoryEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getParent", [this.fullPath]);
+};
+
+module.exports = Entry;
+
+});
+
+// file: lib\common\plugin\File.js
+define("cordova/plugin/File", function(require, exports, module) {
+
+/**
+ * Constructor.
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+
+var File = function(name, fullPath, type, lastModifiedDate, size){
+    this.name = name || '';
+    this.fullPath = fullPath || null;
+    this.type = type || null;
+    this.lastModifiedDate = lastModifiedDate || null;
+    this.size = size || 0;
+
+    // These store the absolute start and end for slicing the file.
+    this.start = 0;
+    this.end = this.size;
+};
+
+/**
+ * Returns a "slice" of the file. Since Cordova Files don't contain the actual
+ * content, this really returns a File with adjusted start and end.
+ * Slices of slices are supported.
+ * start {Number} The index at which to start the slice (inclusive).
+ * end {Number} The index at which to end the slice (exclusive).
+ */
+File.prototype.slice = function(start, end) {
+    var size = this.end - this.start;
+    var newStart = 0;
+    var newEnd = size;
+    if (arguments.length) {
+        if (start < 0) {
+            newStart = Math.max(size + start, 0);
+        } else {
+            newStart = Math.min(size, start);
+        }
+    }
+
+    if (arguments.length >= 2) {
+        if (end < 0) {
+            newEnd = Math.max(size + end, 0);
+        } else {
+            newEnd = Math.min(end, size);
+        }
+    }
+
+    var newFile = new File(this.name, this.fullPath, this.type, this.lastModifiedData, this.size);
+    newFile.start = this.start + newStart;
+    newFile.end = this.start + newEnd;
+    return newFile;
+};
+
+
+module.exports = File;
+
+});
+
+// file: lib\common\plugin\FileEntry.js
+define("cordova/plugin/FileEntry", function(require, exports, module) {
+
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    Entry = require('cordova/plugin/Entry'),
+    FileWriter = require('cordova/plugin/FileWriter'),
+    File = require('cordova/plugin/File'),
+    FileError = require('cordova/plugin/FileError');
+
+/**
+ * An interface representing a file on the file system.
+ *
+ * {boolean} isFile always true (readonly)
+ * {boolean} isDirectory always false (readonly)
+ * {DOMString} name of the file, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the file (readonly)
+ * {FileSystem} filesystem on which the file resides (readonly)
+ */
+var FileEntry = function(name, fullPath) {
+     FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath]);
+};
+
+utils.extend(FileEntry, Entry);
+
+/**
+ * Creates a new FileWriter associated with the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new FileWriter
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.createWriter = function(successCallback, errorCallback) {
+    this.file(function(filePointer) {
+        var writer = new FileWriter(filePointer);
+
+        if (writer.fileName === null || writer.fileName === "") {
+            errorCallback && errorCallback(new FileError(FileError.INVALID_STATE_ERR));
+        } else {
+            successCallback && successCallback(writer);
+        }
+    }, errorCallback);
+};
+
+/**
+ * Returns a File that represents the current state of the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new File object
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.file = function(successCallback, errorCallback) {
+    var win = successCallback && function(f) {
+        var file = new File(f.name, f.fullPath, f.type, f.lastModifiedDate, f.size);
+        successCallback(file);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getFileMetadata", [this.fullPath]);
+};
+
+
+module.exports = FileEntry;
+
+});
+
+// file: lib\common\plugin\FileError.js
+define("cordova/plugin/FileError", function(require, exports, module) {
+
+/**
+ * FileError
+ */
+function FileError(error) {
+  this.code = error || null;
+}
+
+// File error codes
+// Found in DOMException
+FileError.NOT_FOUND_ERR = 1;
+FileError.SECURITY_ERR = 2;
+FileError.ABORT_ERR = 3;
+
+// Added by File API specification
+FileError.NOT_READABLE_ERR = 4;
+FileError.ENCODING_ERR = 5;
+FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
+FileError.INVALID_STATE_ERR = 7;
+FileError.SYNTAX_ERR = 8;
+FileError.INVALID_MODIFICATION_ERR = 9;
+FileError.QUOTA_EXCEEDED_ERR = 10;
+FileError.TYPE_MISMATCH_ERR = 11;
+FileError.PATH_EXISTS_ERR = 12;
+
+module.exports = FileError;
+
+});
+
+// file: lib\common\plugin\FileReader.js
+define("cordova/plugin/FileReader", function(require, exports, module) {
+
+var exec = require('cordova/exec'),
+    modulemapper = require('cordova/modulemapper'),
+    utils = require('cordova/utils'),
+    File = require('cordova/plugin/File'),
+    FileError = require('cordova/plugin/FileError'),
+    ProgressEvent = require('cordova/plugin/ProgressEvent'),
+    origFileReader = modulemapper.getOriginalSymbol(this, 'FileReader');
+
+/**
+ * This class reads the mobile device file system.
+ *
+ * For Android:
+ *      The root directory is the root of the file system.
+ *      To read from the SD card, the file name is "sdcard/my_file.txt"
+ * @constructor
+ */
+var FileReader = function() {
+    this._readyState = 0;
+    this._error = null;
+    this._result = null;
+    this._fileName = '';
+    this._realReader = origFileReader ? new origFileReader() : {};
+};
+
+// States
+FileReader.EMPTY = 0;
+FileReader.LOADING = 1;
+FileReader.DONE = 2;
+
+utils.defineGetter(FileReader.prototype, 'readyState', function() {
+    return this._fileName ? this._readyState : this._realReader.readyState;
+});
+
+utils.defineGetter(FileReader.prototype, 'error', function() {
+    return this._fileName ? this._error: this._realReader.error;
+});
+
+utils.defineGetter(FileReader.prototype, 'result', function() {
+    return this._fileName ? this._result: this._realReader.result;
+});
+
+function defineEvent(eventName) {
+    utils.defineGetterSetter(FileReader.prototype, eventName, function() {
+        return this._realReader[eventName] || null;
+    }, function(value) {
+        this._realReader[eventName] = value;
+    });
+}
+defineEvent('onloadstart');    // When the read starts.
+defineEvent('onprogress');     // While reading (and decoding) file or fileBlob data, and reporting partial file data (progress.loaded/progress.total)
+defineEvent('onload');         // When the read has successfully completed.
+defineEvent('onerror');        // When the read has failed (see errors).
+defineEvent('onloadend');      // When the request has completed (either in success or failure).
+defineEvent('onabort');        // When the read has been aborted. For instance, by invoking the abort() method.
+
+function initRead(reader, file) {
+    // Already loading something
+    if (reader.readyState == FileReader.LOADING) {
+      throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    reader._result = null;
+    reader._error = null;
+    reader._readyState = FileReader.LOADING;
+
+    if (typeof file == 'string') {
+        // Deprecated in Cordova 2.4.
+        console.warn('Using a string argument with FileReader.readAs functions is deprecated.');
+        reader._fileName = file;
+    } else if (typeof file.fullPath == 'string') {
+        reader._fileName = file.fullPath;
+    } else {
+        reader._fileName = '';
+        return true;
+    }
+
+    reader.onloadstart && reader.onloadstart(new ProgressEvent("loadstart", {target:reader}));
+}
+
+/**
+ * Abort reading file.
+ */
+FileReader.prototype.abort = function() {
+    if (origFileReader && !this._fileName) {
+        return this._realReader.abort();
+    }
+    this._result = null;
+
+    if (this._readyState == FileReader.DONE || this._readyState == FileReader.EMPTY) {
+      return;
+    }
+
+    this._readyState = FileReader.DONE;
+
+    // If abort callback
+    if (typeof this.onabort === 'function') {
+        this.onabort(new ProgressEvent('abort', {target:this}));
+    }
+    // If load end callback
+    if (typeof this.onloadend === 'function') {
+        this.onloadend(new ProgressEvent('loadend', {target:this}));
+    }
+};
+
+/**
+ * Read text file.
+ *
+ * @param file          {File} File object containing file properties
+ * @param encoding      [Optional] (see http://www.iana.org/assignments/character-sets)
+ */
+FileReader.prototype.readAsText = function(file, encoding) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsText(file, encoding);
+    }
+
+    // Default encoding is UTF-8
+    var enc = encoding ? encoding : "UTF-8";
+    var me = this;
+    var execArgs = [this._fileName, enc, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // Save result
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // null result
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsText", execArgs);
+};
+
+
+/**
+ * Read file and return data as a base64 encoded data url.
+ * A data url is of the form:
+ *      data:[<mediatype>][;base64],<data>
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsDataURL = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsDataURL(file);
+    }
+
+    var me = this;
+    var execArgs = [this._fileName, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // Save result
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsDataURL", execArgs);
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsBinaryString = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsBinaryString(file);
+    }
+
+    var me = this;
+    var execArgs = [this._fileName, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsBinaryString", execArgs);
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsArrayBuffer = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsArrayBuffer(file);
+    }
+
+    var me = this;
+    var execArgs = [this._fileName, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsArrayBuffer", execArgs);
+};
+
+module.exports = FileReader;
+
+});
+
+// file: lib\common\plugin\FileSystem.js
+define("cordova/plugin/FileSystem", function(require, exports, module) {
+
+var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
+
+/**
+ * An interface representing a file system
+ *
+ * @constructor
+ * {DOMString} name the unique name of the file system (readonly)
+ * {DirectoryEntry} root directory of the file system (readonly)
+ */
+var FileSystem = function(name, root) {
+    this.name = name || null;
+    if (root) {
+        this.root = new DirectoryEntry(root.name, root.fullPath);
+    }
+};
+
+module.exports = FileSystem;
+
+});
+
+// file: lib\common\plugin\FileTransfer.js
+define("cordova/plugin/FileTransfer", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    FileTransferError = require('cordova/plugin/FileTransferError'),
+    ProgressEvent = require('cordova/plugin/ProgressEvent');
+
+function newProgressEvent(result) {
+    var pe = new ProgressEvent();
+    pe.lengthComputable = result.lengthComputable;
+    pe.loaded = result.loaded;
+    pe.total = result.total;
+    return pe;
+}
+
+function getBasicAuthHeader(urlString) {
+    var header =  null;
+
+    if (window.btoa) {
+        // parse the url using the Location object
+        var url = document.createElement('a');
+        url.href = urlString;
+
+        var credentials = null;
+        var protocol = url.protocol + "//";
+        var origin = protocol + url.host;
+
+        // check whether there are the username:password credentials in the url
+        if (url.href.indexOf(origin) !== 0) { // credentials found
+            var atIndex = url.href.indexOf("@");
+            credentials = url.href.substring(protocol.length, atIndex);
+        }
+
+        if (credentials) {
+            var authHeader = "Authorization";
+            var authHeaderValue = "Basic " + window.btoa(credentials);
+
+            header = {
+                name : authHeader,
+                value : authHeaderValue
+            };
+        }
+    }
+
+    return header;
+}
+
+var idCounter = 0;
+
+/**
+ * FileTransfer uploads a file to a remote server.
+ * @constructor
+ */
+var FileTransfer = function() {
+    this._id = ++idCounter;
+    this.onprogress = null; // optional callback
+};
+
+/**
+* Given an absolute file path, uploads a file on the device to a remote server
+* using a multipart HTTP request.
+* @param filePath {String}           Full path of the file on the device
+* @param server {String}             URL of the server to receive the file
+* @param successCallback (Function}  Callback to be invoked when upload has completed
+* @param errorCallback {Function}    Callback to be invoked upon error
+* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
+* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
+*/
+FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
+    argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments);
+    // check for options
+    var fileKey = null;
+    var fileName = null;
+    var mimeType = null;
+    var params = null;
+    var chunkedMode = true;
+    var headers = null;
+    var httpMethod = null;
+    var basicAuthHeader = getBasicAuthHeader(server);
+    if (basicAuthHeader) {
+        options = options || {};
+        options.headers = options.headers || {};
+        options.headers[basicAuthHeader.name] = basicAuthHeader.value;
+    }
+
+    if (options) {
+        fileKey = options.fileKey;
+        fileName = options.fileName;
+        mimeType = options.mimeType;
+        headers = options.headers;
+        httpMethod = options.httpMethod || "POST";
+        if (httpMethod.toUpperCase() == "PUT"){
+            httpMethod = "PUT";
+        } else {
+            httpMethod = "POST";
+        }
+        if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
+            chunkedMode = options.chunkedMode;
+        }
+        if (options.params) {
+            params = options.params;
+        }
+        else {
+            params = {};
+        }
+    }
+
+    var fail = errorCallback && function(e) {
+        var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
+        errorCallback(error);
+    };
+
+    var self = this;
+    var win = function(result) {
+        if (typeof result.lengthComputable != "undefined") {
+            if (self.onprogress) {
+                self.onprogress(newProgressEvent(result));
+            }
+        } else {
+            successCallback && successCallback(result);
+        }
+    };
+    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
+};
+
+/**
+ * Downloads a file form a given URL and saves it to the specified directory.
+ * @param source {String}          URL of the server to receive the file
+ * @param target {String}         Full path of the file on the device
+ * @param successCallback (Function}  Callback to be invoked when upload has completed
+ * @param errorCallback {Function}    Callback to be invoked upon error
+ * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
+ * @param options {FileDownloadOptions} Optional parameters such as headers
+ */
+FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) {
+    argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments);
+    var self = this;
+
+    var basicAuthHeader = getBasicAuthHeader(source);
+    if (basicAuthHeader) {
+        options = options || {};
+        options.headers = options.headers || {};
+        options.headers[basicAuthHeader.name] = basicAuthHeader.value;
+    }
+
+    var headers = null;
+    if (options) {
+        headers = options.headers || null;
+    }
+
+    var win = function(result) {
+        if (typeof result.lengthComputable != "undefined") {
+            if (self.onprogress) {
+                return self.onprogress(newProgressEvent(result));
+            }
+        } else if (successCallback) {
+            var entry = null;
+            if (result.isDirectory) {
+                entry = new (require('cordova/plugin/DirectoryEntry'))();
+            }
+            else if (result.isFile) {
+                entry = new (require('cordova/plugin/FileEntry'))();
+            }
+            entry.isDirectory = result.isDirectory;
+            entry.isFile = result.isFile;
+            entry.name = result.name;
+            entry.fullPath = result.fullPath;
+            successCallback(entry);
+        }
+    };
+
+    var fail = errorCallback && function(e) {
+        var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
+        errorCallback(error);
+    };
+
+    exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id, headers]);
+};
+
+/**
+ * Aborts the ongoing file transfer on this object. The original error
+ * callback for the file trans

<TRUNCATED>

[08/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/cordova.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/cordova.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/cordova.js
new file mode 100644
index 0000000..6778e2b
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/cordova.js
@@ -0,0 +1,14 @@
+var VERSION='2.5.0';
+var scripts = document.getElementsByTagName('script');
+var cordovaPath = scripts[scripts.length - 1].src.replace('cordova.js', 'cordova-'+VERSION+'.js');
+
+document.write('<script type="text/javascript" charset="utf-8" src="' + cordovaPath + '"></script>');
+
+function backHome() {
+	if (window.device && device.platform && device.platform.toLowerCase() == 'android') {
+            navigator.app.backHistory();
+	}
+	else {
+	    window.history.go(-1);
+	}
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/events/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/events/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/events/index.html
new file mode 100644
index 0000000..2c7e8ba
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/events/index.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    function interceptBackbutton() {
+    	eventOutput("Back button intercepted");
+    }
+    function interceptMenubutton() {
+    	eventOutput("Menu button intercepted");
+    }
+    function interceptSearchbutton() {
+    	eventOutput("Search button intercepted");
+    }
+    function interceptResume() {
+      eventOutput("Resume event intercepted");
+    }
+    function interceptPause() {
+      eventOutput("Pause event intercepted");
+    }
+    function interceptOnline() {
+      eventOutput("Online event intercepted");
+    }
+    function interceptOffline() {
+      eventOutput("Offline event intercepted");
+    }
+    
+    var eventOutput = function(s) {
+        var el = document.getElementById("results");
+        el.innerHTML = el.innerHTML + s + "<br>";
+    };
+
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+                eventOutput("deviceready event: "+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Events</h1>
+    <div id="info">
+        <b>Results:</b><br>
+        <span id="results"></span>
+    </div>
+
+    <h2>Action</h2>
+    <div class="btn large" onclick="document.addEventListener('backbutton', interceptBackbutton, false);">Intercept backbutton</div>
+    <div class="btn large" onclick="document.removeEventListener('backbutton', interceptBackbutton, false);">Stop intercept of backbutton</div>
+    <div class="btn large" onclick="document.addEventListener('menubutton', interceptMenubutton, false);">Intercept menubutton</div>
+    <div class="btn large" onclick="document.removeEventListener('menubutton', interceptMenubutton, false);">Stop intercept of menubutton</div>
+    <div class="btn large" onclick="document.addEventListener('searchbutton', interceptSearchbutton, false);">Intercept searchbutton</div>
+    <div class="btn large" onclick="document.removeEventListener('searchbutton', interceptSearchbutton, false);">Stop intercept of searchbutton</div>
+    <div class="btn large" onclick="document.addEventListener('resume', interceptResume, false);">Intercept resume</div>
+    <div class="btn large" onclick="document.removeEventListener('resume', interceptResume, false);">Stop intercept of resume</div>
+    <div class="btn large" onclick="document.addEventListener('pause', interceptPause, false);">Intercept pause</div>
+    <div class="btn large" onclick="document.removeEventListener('pause', interceptPause, false);">Stop intercept of pause</div>
+    <div class="btn large" onclick="document.addEventListener('online', interceptOnline, false);">Intercept online</div>
+    <div class="btn large" onclick="document.removeEventListener('online', interceptOnline, false);">Stop intercept of online</div>
+    <div class="btn large" onclick="document.addEventListener('offline', interceptOffline, false);">Intercept offline</div>
+    <div class="btn large" onclick="document.removeEventListener('offline', interceptOffline, false);">Stop intercept of offline</div>
+
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/index.html
new file mode 100644
index 0000000..e7a77e0
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/index.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+    <title>Cordova Mobile Spec</title>
+	  <link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+	  <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
+	  <script type="text/javascript" charset="utf-8" src="main.js"></script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+    <h1>Apache Cordova Tests</h1>
+    <div id="info">
+        <h4>Platform: <span id="platform">  </span></h4>
+        <h4>Version: <span id="version"> </span></h4>
+        <h4>UUID: <span id="uuid">  </span></h4>
+        <h4>Name: <span id="name"> </span></h4>
+        <h4>Width: <span id="width">  </span>,   Height: <span id="height"> 
+                   </span>, Color Depth: <span id="colorDepth"></span></h4>
+     </div>
+    <a href="autotest/index.html" class="btn large">Automatic Test</a>
+    <a href="accelerometer/index.html" class="btn large">Accelerometer</a>
+    <a href="audio/index.html" class="btn large">Audio Play/Record</a>
+    <a href="battery/index.html" class="btn large">Battery</a>
+    <a href="camera/index.html" class="btn large">Camera</a>
+    <a href="compass/index.html" class="btn large">Compass</a>
+    <a href="contacts/index.html" class="btn large">Contacts</a>
+    <a href="events/index.html" class="btn large">Events</a>
+    <a href="location/index.html" class="btn large">Location</a>
+    <a href="misc/index.html" class="btn large">Misc Content</a>
+    <a href="network/index.html" class="btn large">Network</a>
+    <a href="notification/index.html" class="btn large">Notification</a>
+    <a href="sql/index.html" class="btn large">Web SQL</a>
+    <a href="storage/index.html" class="btn large">Local Storage</a>
+    <a href="execbenchmark/index.html" class="btn large">Benchmark exec()</a>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/location/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/location/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/location/index.html
new file mode 100644
index 0000000..8b1e681
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/location/index.html
@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    //-------------------------------------------------------------------------
+    // Location
+    //-------------------------------------------------------------------------
+    var watchLocationId = null;
+
+    /**
+     * Start watching location
+     */
+    var watchLocation = function(geo) {
+        console.log("watchLocation()");
+
+        // Success callback
+        var success = function(p){
+              console.log('watch location success');
+              setLocationDetails(p);
+        };
+
+        // Fail callback
+        var fail = function(e){
+            console.log("watchLocation fail callback with error code "+e);
+            stopLocation(geo);
+        };
+
+        // Get location
+        watchLocationId = geo.watchPosition(success, fail, {enableHighAccuracy: true});
+        setLocationStatus("Running");
+    };
+
+    /**
+     * Stop watching the location
+     */
+    var stopLocation = function(geo) {
+        setLocationStatus("Stopped");
+        if (watchLocationId) {
+            geo.clearWatch(watchLocationId);
+            watchLocationId = null;
+        }
+    };
+
+    /**
+     * Get current location
+     */
+    var getLocation = function(geo, opts) {
+        console.log("getLocation()");
+
+        // Stop location if running
+        stopLocation(geo);
+
+        // Success callback
+        var success = function(p){
+            console.log('get location success');
+            setLocationDetails(p);
+            setLocationStatus("Done");
+        };
+
+        // Fail callback
+        var fail = function(e){
+            console.log("getLocation fail callback with error code "+e.code);
+            setLocationStatus("Error: "+e.code);
+        };
+
+        // Get location
+        geo.getCurrentPosition(success, fail, opts || {enableHighAccuracy: true}); //, {timeout: 10000});
+        setLocationStatus("Retrieving location...");
+
+    };
+
+    /**
+     * Set location status
+     */
+    var setLocationStatus = function(status) {
+        document.getElementById('location_status').innerHTML = status;
+    };
+var setLocationDetails = function(p) {
+var date = (new Date(p.timestamp));
+            document.getElementById('latitude').innerHTML = p.coords.latitude;
+            document.getElementById('longitude').innerHTML = p.coords.longitude;
+            document.getElementById('altitude').innerHTML = p.coords.altitude;
+            document.getElementById('accuracy').innerHTML = p.coords.accuracy;
+            document.getElementById('heading').innerHTML = p.coords.heading;
+            document.getElementById('speed').innerHTML = p.coords.speed;
+            document.getElementById('altitude_accuracy').innerHTML = p.coords.altitudeAccuracy;
+            document.getElementById('timestamp').innerHTML =  date.toDateString() + " " + date.toTimeString();
+    }
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Location</h1>
+    <div id="info">
+        <b>Status:</b> <span id="location_status">Stopped</span>
+        <table width="100%">
+            <tr><td><b>Latitude:</b></td><td id="latitude">&nbsp;</td></tr>
+            <tr><td><b>Longitude:</b></td><td id="longitude">&nbsp;</td></tr>
+            <tr><td><b>Altitude:</b></td><td id="altitude">&nbsp;</td></tr>
+            <tr><td><b>Accuracy:</b></td><td id="accuracy">&nbsp;</td></tr>
+            <tr><td><b>Heading:</b></td><td id="heading">&nbsp;</td></tr>
+            <tr><td><b>Speed:</b></td><td id="speed">&nbsp;</td></tr>
+            <tr><td><b>Altitude Accuracy:</b></td><td id="altitude_accuracy">&nbsp;</td></tr>
+            <tr><td><b>Time:</b></td><td id="timestamp">&nbsp;</td></tr>
+        </table>
+    </div>
+    <h2>Action</h2>
+    <h3>Use Built-in WebView navigator.geolocation</h3>
+    <a href="javascript:" class="btn large" onclick="getLocation(navigator.geolocation);">Get Location</a>
+    <a href="javascript:" class="btn large" onclick="watchLocation(navigator.geolocation);">Start Watching Location</a>
+    <a href="javascript:" class="btn large" onclick="stopLocation(navigator.geolocation);">Stop Watching Location</a>
+    <a href="javascript:" class="btn large" onclick="getLocation(navigator.geolocation, {maximumAge:30000});">Get Location Up to 30 Seconds Old</a>
+    <h3>USe Cordova Geolocation Plugin</h3>
+    <a href="javascript:" class="btn large" onclick="getLocation(cordova.require('cordova/plugin/geolocation'));">Get Location</a>
+    <a href="javascript:" class="btn large" onclick="watchLocation(cordova.require('cordova/plugin/geolocation'));">Start Watching Location</a>
+    <a href="javascript:" class="btn large" onclick="stopLocation(cordova.require('cordova/plugin/geolocation'));">Stop Watching Location</a>
+    <a href="javascript:" class="btn large" onclick="getLocation(cordova.require('cordova/plugin/geolocation'), {maximumAge:30000});">Get Location Up to 30 Seconds Old</a>
+    <h2>&nbsp;</h2><a href="javascript:" class="backBtn" onclick="backHome();">Back</a>    
+  </body>
+</html>      

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/main.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/main.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/main.js
new file mode 100644
index 0000000..ae447aa
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/main.js
@@ -0,0 +1,140 @@
+var deviceInfo = function() {
+    document.getElementById("platform").innerHTML = device.platform;
+    document.getElementById("version").innerHTML = device.version;
+    document.getElementById("uuid").innerHTML = device.uuid;
+    document.getElementById("name").innerHTML = device.name;
+    document.getElementById("width").innerHTML = screen.width;
+    document.getElementById("height").innerHTML = screen.height;
+    document.getElementById("colorDepth").innerHTML = screen.colorDepth;
+};
+
+var getLocation = function() {
+    var suc = function(p) {
+        alert(p.coords.latitude + " " + p.coords.longitude);
+    };
+    var locFail = function() {
+    };
+    navigator.geolocation.getCurrentPosition(suc, locFail);
+};
+
+var beep = function() {
+    navigator.notification.beep(2);
+};
+
+var vibrate = function() {
+    navigator.notification.vibrate(0);
+};
+
+function roundNumber(num) {
+    var dec = 3;
+    var result = Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
+    return result;
+}
+
+var accelerationWatch = null;
+
+function updateAcceleration(a) {
+    document.getElementById('x').innerHTML = roundNumber(a.x);
+    document.getElementById('y').innerHTML = roundNumber(a.y);
+    document.getElementById('z').innerHTML = roundNumber(a.z);
+}
+
+var toggleAccel = function() {
+    if (accelerationWatch !== null) {
+        navigator.accelerometer.clearWatch(accelerationWatch);
+        updateAcceleration({
+            x : "",
+            y : "",
+            z : ""
+        });
+        accelerationWatch = null;
+    } else {
+        var options = {};
+        options.frequency = 1000;
+        accelerationWatch = navigator.accelerometer.watchAcceleration(
+                updateAcceleration, function(ex) {
+                    alert("accel fail (" + ex.name + ": " + ex.message + ")");
+                }, options);
+    }
+};
+
+var preventBehavior = function(e) {
+    e.preventDefault();
+};
+
+function dump_pic(data) {
+    var viewport = document.getElementById('viewport');
+    console.log(data);
+    viewport.style.display = "";
+    viewport.style.position = "absolute";
+    viewport.style.top = "10px";
+    viewport.style.left = "10px";
+    document.getElementById("test_img").src = "data:image/jpeg;base64," + data;
+}
+
+function fail(msg) {
+    alert(msg);
+}
+
+function show_pic() {
+    navigator.camera.getPicture(dump_pic, fail, {
+        quality : 50
+    });
+}
+
+function close() {
+    var viewport = document.getElementById('viewport');
+    viewport.style.position = "relative";
+    viewport.style.display = "none";
+}
+
+// This is just to do this.
+function readFile() {
+    navigator.file.read('/sdcard/phonegap.txt', fail, fail);
+}
+
+function writeFile() {
+    navigator.file.write('foo.txt', "This is a test of writing to a file",
+            fail, fail);
+}
+
+function contacts_success(contacts) {
+    alert(contacts.length
+            + ' contacts returned.'
+            + (contacts[2] && contacts[2].name ? (' Third contact is ' + contacts[2].name.formatted)
+                    : ''));
+}
+
+function get_contacts() {
+    var obj = new ContactFindOptions();
+    obj.filter = "";
+    obj.multiple = true;
+    obj.limit = 5;
+    navigator.service.contacts.find(
+            [ "displayName", "name" ], contacts_success,
+            fail, obj);
+}
+
+var networkReachableCallback = function(reachability) {
+    // There is no consistency on the format of reachability
+    var networkState = reachability.code || reachability;
+
+    var currentState = {};
+    currentState[NetworkStatus.NOT_REACHABLE] = 'No network connection';
+    currentState[NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK] = 'Carrier data connection';
+    currentState[NetworkStatus.REACHABLE_VIA_WIFI_NETWORK] = 'WiFi connection';
+
+    confirm("Connection type:\n" + currentState[networkState]);
+};
+
+function check_network() {
+    navigator.network.isReachable("www.mobiledevelopersolutions.com",
+            networkReachableCallback, {});
+}
+
+function init() {
+    // the next line makes it impossible to see Contacts on the HTC Evo since it
+    // doesn't have a scroll button
+    // document.addEventListener("touchmove", preventBehavior, false);
+    document.addEventListener("deviceready", deviceInfo, true);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/master.css
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/master.css b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/master.css
new file mode 100644
index 0000000..2408052
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/master.css
@@ -0,0 +1,136 @@
+  body {
+    background:#222 none repeat scroll 0 0;
+    color:#666;
+    font-family:Helvetica;
+    font-size:72%;
+    line-height:1.5em;
+    margin:0;
+    border-top:1px solid #393939;
+  }
+
+  #info{
+    background:#ffa;
+    border: 1px solid #ffd324;
+    -webkit-border-radius: 5px;
+    border-radius: 5px;
+    clear:both;
+    margin:15px 6px 0;
+    width:295px;
+    padding:4px 0px 2px 10px;
+  }
+  
+  #info > h4{
+    font-size:.95em;
+    margin:5px 0;
+  }
+ 	
+  #stage.theme{
+    padding-top:3px;
+  }
+
+  /* Definition List */
+  #stage.theme > dl{
+  	padding-top:10px;
+  	clear:both;
+  	margin:0;
+  	list-style-type:none;
+  	padding-left:10px;
+  	overflow:auto;
+  }
+
+  #stage.theme > dl > dt{
+  	font-weight:bold;
+  	float:left;
+  	margin-left:5px;
+  }
+
+  #stage.theme > dl > dd{
+  	width:45px;
+  	float:left;
+  	color:#a87;
+  	font-weight:bold;
+  }
+
+  /* Content Styling */
+  #stage.theme > h1, #stage.theme > h2, #stage.theme > p{
+    margin:1em 0 .5em 13px;
+  }
+
+  #stage.theme > h1{
+    color:#eee;
+    font-size:1.6em;
+    text-align:center;
+    margin:0;
+    margin-top:15px;
+    padding:0;
+  }
+
+  #stage.theme > h2{
+  	clear:both;
+    margin:0;
+    padding:3px;
+    font-size:1em;
+    text-align:center;
+  }
+
+  /* Stage Buttons */
+  #stage.theme .btn{
+  	border: 1px solid #555;
+  	-webkit-border-radius: 5px;
+  	border-radius: 5px;
+  	text-align:center;
+  	display:block;
+  	float:left;
+  	background:#444;
+  	width:150px;
+  	color:#9ab;
+  	font-size:1.1em;
+  	text-decoration:none;
+  	padding:1.2em 0;
+  	margin:3px 0px 3px 5px;
+  }
+  
+  #stage.theme .large{
+  	width:308px;
+  	padding:1.2em 0;
+  }
+  
+  #stage.theme .wide{
+    width:100%;
+    padding:1.2em 0;
+  }
+  
+  #stage.theme .backBtn{
+   border: 1px solid #555;
+   -webkit-border-radius: 5px;
+   border-radius: 5px;
+   text-align:center;
+   display:block;
+   float:right;
+   background:#666;
+   width:75px;
+   color:#9ab;
+   font-size:1.1em;
+   text-decoration:none;
+   padding:1.2em 0;
+   margin:3px 5px 3px 5px;
+  }
+  
+  #stage.theme .input{
+   border: 1px solid #555;
+   -webkit-border-radius: 5px;
+   border-radius: 5px;
+   text-align:center;
+   display:block;
+   float:light;
+   background:#888;
+   color:#9cd;
+   font-size:1.1em;
+   text-decoration:none;
+   padding:1.2em 0;
+   margin:3px 0px 3px 5px;    
+ }
+  
+  #stage.theme .numeric{
+   width:100%;
+  }

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/misc/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/misc/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/misc/index.html
new file mode 100644
index 0000000..697cb10
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/misc/index.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    function roundNumber(num) {
+        var dec = 3;
+        var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
+        return result;
+    }
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Display Other Content</h1>
+    <div id="info">
+    </div>
+    <h2>Action</h2>
+    <div class="btn large" onclick="document.location='tel:5551212';" >Call 411</div>
+    <a href="mailto:bob@abc.org?subject=My Subject&body=This is the body.%0D%0ANew line." class="btn large">Send Mail</a>
+    <a href="sms:5125551234?body=The SMS message." class="btn large">Send SMS</a>
+    <a href="http://www.google.com" class="btn large">Load Web Site</a>
+    <!--  Need new URL -->
+    <!-- a href="http://handle.library.cornell.edu/control/authBasic/authTest/" class="btn large">Basic Auth: test/this</a -->
+    <a href="page2.html" hrefbad="page2.html?me=test" class="btn large">Load another PhoneGap page</a>
+    <h2>Android Only</h2>
+    <a href="geo:0,0?q=11400 Burnet Rd, Austin, TX" class="btn large">Map IBM</a>
+    <a href="market://search?q=google" class="btn large">Search Android market</a>
+    <a href="content://media/external/images/media" class="btn large">View image app</a>
+
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/misc/page2.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/misc/page2.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/misc/page2.html
new file mode 100644
index 0000000..22026fc
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/misc/page2.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+    <title>Cordova Mobile Spec</title>
+      <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+      <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>
+      <script type="text/javascript" charset="utf-8" src="../main.js"></script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+    <h1>Page2 App</h1>
+    <h2>This is page 2 of a PhoneGap app</h2>
+    <div id="info">
+        <h4>Platform: <span id="platform">  </span></h4>
+        <h4>Version: <span id="version"> </span></h4>
+        <h4>UUID: <span id="uuid">  </span></h4>
+        <h4>Name: <span id="name"> </span></h4>
+        <h4>Width: <span id="width">  </span>,   Height: <span id="height"> 
+                   </span>, Color Depth: <span id="colorDepth"></span></h4>
+     </div>
+      <div ><button class="backBtn" onclick="backHome();">Back</button></div>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/network/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/network/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/network/index.html
new file mode 100644
index 0000000..0b2204b
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/network/index.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    var eventOutput = function(s) {
+        var el = document.getElementById("results");
+        el.innerHTML = el.innerHTML + s + "<br>";
+    };
+
+    function printNetwork() {
+        eventOutput("Current network connection type is: " + navigator.network.connection.type);
+    }
+
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+            deviceReady = true;
+            console.log("Device="+device.platform+" "+device.version);
+            eventOutput("Network Connection is: " + navigator.network.connection.type);
+            document.addEventListener('online', function() { eventOutput('Online event, connection is: ' + navigator.network.connection.type); }, false);
+            document.addEventListener('offline', function() { eventOutput('Offline event, connection is: ' + navigator.network.connection.type); }, false);
+
+        }, false);
+        window.setTimeout(function() {
+            if (!deviceReady) {
+                alert("Error: Cordova did not initialize.  Demo will not run correctly.");
+            }
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Network Events and State</h1>
+    <div id="info">
+        <b>Results:</b><br>
+        <span id="results"></span>
+    </div>
+
+    <h2>Action</h2>
+    <div class="btn large" onclick="printNetwork();">Show Network Connection</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/notification/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/notification/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/notification/index.html
new file mode 100644
index 0000000..e5c0d17
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/notification/index.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    //-------------------------------------------------------------------------
+    // Notifications
+    //-------------------------------------------------------------------------
+
+    var beep = function(){
+        navigator.notification.beep(3);
+    };
+
+    var vibrate = function(){
+      navigator.notification.vibrate(1000);
+    };
+
+    var alertDialog = function(message, title, button) {
+        console.log("alertDialog()");
+        navigator.notification.alert(message,
+            function(){
+                console.log("Alert dismissed.");
+            },
+            title, button);
+        console.log("After alert");
+    };
+
+    var confirmDialog = function(message, title, buttons) {
+        navigator.notification.confirm(message,
+            function(r) {
+                console.log("You selected " + r);
+                alert("You selected " + (buttons.split(","))[r-1]);
+            },
+            title,
+            buttons);
+    };
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Notifications and Dialogs</h1>
+    <div id="info">
+    </div>
+    
+    <h2>Action</h2>
+    <div class="btn large" onclick="beep();">Beep</div>
+    <div class="btn large" onclick="vibrate();">Vibrate</div>
+    <div class="btn large" onclick="alertDialog('You pressed alert.', 'Alert Dialog', 'Continue');">Alert Dialog</div>
+    <div class="btn large" onclick="confirmDialog('You pressed confirm.', 'Confirm Dialog', 'Yes,No,Maybe');">Confirm Dialog</div>
+    <div class="btn large" onclick="alert('You pressed alert.');">Built-in Alert Dialog</div>
+    <div class="btn large" onclick="confirm('You selected confirm');">Built-in Confirm Dialog</div>
+    <div class="btn large" onclick="prompt('This is a prompt.', 'Default value');">Built-in Prompt Dialog</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/sql/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/sql/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/sql/index.html
new file mode 100644
index 0000000..116f8d1
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/sql/index.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    //-------------------------------------------------------------------------
+    // HTML5 Database
+    //-------------------------------------------------------------------------
+    var db;
+    var callDatabase = function() {
+        db = openDatabase("mydb", "1.0", "PhoneGap Demo", 20000);
+        if (db === null) {
+            databaseOutput("Database could not be opened.");
+            return;
+        }
+        databaseOutput("Database opened.");
+        db.transaction(function (tx) {
+            tx.executeSql('DROP TABLE IF EXISTS DEMO');
+            tx.executeSql('CREATE TABLE IF NOT EXISTS DEMO (id unique, data)', [],
+                 function(tx,results) { console.log("Created table"); },
+                 function(tx,err) { alert("Error creating table: "+err.message); });
+            tx.executeSql('INSERT INTO DEMO (id, data) VALUES (1, "First row")', [],
+                 function(tx,results) { console.log("Insert row1 success"); },
+                 function(tx,err) { alert("Error adding 1st row: "+err.message); });
+            tx.executeSql('INSERT INTO DEMO (id, data) VALUES (2, "Second row")', [],
+                 function(tx,results) { console.log("Insert row2 success"); },
+                 function(tx,err) { alert("Error adding 2nd row: "+err.message); });
+            databaseOutput("Data written to DEMO table.");
+            console.log("Data written to DEMO table.");
+
+            tx.executeSql('SELECT * FROM DEMO', [], function (tx, results) {
+                var len = results.rows.length;
+                var text = "DEMO table: " + len + " rows found.<br>";
+                text = text + "<table border='1'><tr><td>Row</td><td>Data</td></tr>";
+                for (var i=0; i<len; i++){
+                    text = text + "<tr><td>" + i + "</td><td>" + results.rows.item(i).id + ", " + results.rows.item(i).data + "</td></tr>";
+                }
+                text = text + "</table>";
+                databaseOutput(text);
+            }, function(tx, err) {
+                alert("Error processing SELECT * SQL: "+err.message);
+            });
+            tx.executeSql('SELECT ID FROM DEMO', [], function (tx, results) {
+                var len = results.rows.length;
+                var text = "DEMO table: " + len + " rows found.<br>";
+                text = text + "<table border='1'><tr><td>Row</td><td>Data</td></tr>";
+                for (var i=0; i<len; i++){
+                    text = text + "<tr><td>" + i + "</td><td>" + results.rows.item(i).id + "</td></tr>";
+                }
+                text = text + "</table>";
+                databaseOutput(text);
+            }, function(tx, err) {
+                alert("Error processing SELECT ID SQL: "+err.message);
+            });
+            
+        },
+        function(err) {
+            console.log("Transaction failed: " + err.message);
+        });
+
+
+    };
+
+    var readDatabase = function() {
+    	if (!db) {
+    	    db = openDatabase("mydb", "1.0", "PhoneGap Demo", 20000);
+    	    if (db === null) {
+                databaseOutput("Database could not be opened.");
+                return;
+    	    }
+        }
+        db.transaction(function (tx) {
+            tx.executeSql('SELECT * FROM DEMO WHERE id=2', [], function (tx, results) {
+                var len = results.rows.length;
+                var text = "DEMO table: " + len + " rows found.<br>";
+                text = text + "<table border='1'><tr><td>Row</td><td>Data</td></tr>";
+                for (var i=0; i<len; i++){
+                    text = text + "<tr><td>" + i + "</td><td>" + results.rows.item(i).id + ", " + results.rows.item(i).data + "</td></tr>";
+                }
+                text = text + "</table>";
+                databaseOutput(text);
+            }, function(tx, err) {
+                alert("Error processing SELECT * WHERE id=2 SQL: "+err.message);
+            });
+        });
+    }
+
+    var databaseOutput = function(s) {
+        var el = document.getElementById("database_results");
+        el.innerHTML = el.innerHTML + s + "<br>";
+    };
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>HTML5 Database</h1>   
+    <div id="info">
+        <b>Results:</b><br>
+        <span id="database_results"></span>
+    </div>
+    <h2>Action</h2>
+    <div class="btn large" onclick="callDatabase();">Create, Add, Read Database</div>
+    <div class="btn large" onclick="readDatabase();">Read Database</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>    
+  </body>
+</html>      

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/storage/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/storage/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/storage/index.html
new file mode 100644
index 0000000..85a0dbd
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/storage/index.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Local Storage</h1>
+    <div id="info">
+        You have run this app <span id="count">an untold number of</span> time(s).
+    </div>
+
+    <script>
+    if (!localStorage.pageLoadCount) {
+        localStorage.pageLoadCount = 0;
+    }
+    localStorage.pageLoadCount = parseInt(localStorage.pageLoadCount) + 1;
+    document.getElementById('count').textContent = localStorage.pageLoadCount;
+    </script>
+
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/README.md
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/README.md b/lib/cordova-wp8/tests/README.md
new file mode 100644
index 0000000..d095927
--- /dev/null
+++ b/lib/cordova-wp8/tests/README.md
@@ -0,0 +1,7 @@
+Tests Have Moved
+===
+
+Tests now live in the MobileSpec repo at:
+https://github.com/apache/cordova-mobile-spec
+
+You will need to create a new project, and add the mobile-spec files to it to run the tests.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tooling/scripts/buildjs.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tooling/scripts/buildjs.bat b/lib/cordova-wp8/tooling/scripts/buildjs.bat
new file mode 100644
index 0000000..28d2423
--- /dev/null
+++ b/lib/cordova-wp8/tooling/scripts/buildjs.bat
@@ -0,0 +1,2 @@
+@echo off
+cscript "%~dp0\buildjs.js" %* //nologo
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tooling/scripts/buildjs.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tooling/scripts/buildjs.js b/lib/cordova-wp8/tooling/scripts/buildjs.js
new file mode 100644
index 0000000..78e213c
--- /dev/null
+++ b/lib/cordova-wp8/tooling/scripts/buildjs.js
@@ -0,0 +1,214 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject'),
+    shell = WScript.CreateObject("shell.application"),
+    wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments,
+    //Root folder of cordova-wp8 (i.e C:\Cordova\cordova-wp8)
+    ROOT = WScript.ScriptFullName.split('\\tooling\\', 1),
+    //Sub folder containing templates
+    TEMPLATES_PATH = '\\templates',
+    //Sub folder for standalone project
+    STANDALONE_PATH = TEMPLATES_PATH + '\\standalone',
+    //Sub folder for full project
+    FULL_PATH = TEMPLATES_PATH + '\\full',
+    CUSTOM_PATH = TEMPLATES_PATH + '\\custom',
+    //Sub folder containing framework
+    FRAMEWORK_PATH = '\\framework',
+    //Subfolder containing example project
+    EXAMPLE_PATH = '\\example',
+    //Git Repositories
+    CORDOVA_JS = "https://git-wip-us.apache.org/repos/asf/cordova-js.git",
+    // get version
+    VERSION = read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,''),
+    BUILD_DESTINATION;
+
+function Log(msg) {
+    WScript.StdOut.WriteLine(msg);
+}
+
+// help function
+function Usage()
+{
+    Log("");
+    Log("This Script builds the given virsion of cordova.js and injects it into this or the given cordova-wp8 ");
+    Log("");
+    Log("Usage: buildjs [ Version PathTOCordovaWP8 ]");
+    Log("    Version : The version of cordova.js to build (must already be tagged)");
+    Log("    PathTOCordovaWP8 : The path to the cordova directory where the new cordova.js will go.");
+    Log("examples:");
+    Log("    buildjs 2.5.0rc1  //Puts cordova-2.5.0rc1 as the cordova.js in the current working directory");
+    Log("    buildjs 2.4.0 C:\\Users\\anonymous\\Desktop\\cordova-wp8  //Puts cordova-2.4.0.js in the given directory");
+    Log("    buildjs //Builds the version of cordova.js from the root folder and adds it to the working directory repo");
+    Log("");
+}
+
+// returns the contents of a file
+function read(filename) {
+    //Log('Reading in ' + filename);
+    if(fso.FileExists(filename))
+    {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else
+    {
+        Log('Cannot read non-existant file : ' + filename);
+        WScript.Quit(1);
+    }
+    return null;
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        WScript.sleep(100);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    //Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if(!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if(!oShell.StdErr.AtEndOfStream)
+    {
+        var err_line = oShell.StdErr.ReadAll();
+        WScript.StdErr.WriteLine(err_line);
+        WScript.Quit(1);
+    }
+}
+
+function build_js(path)
+{
+    if(fso.FolderExists(path + '\\temp'))
+    {
+        fso.DeleteFolder(path + '\\temp', true);
+    }
+    fso.CreateFolder(path + '\\temp');
+    wscript_shell.CurrentDirectory = path + '\\temp';
+
+    Log('\tCloning js tagged with ' + VERSION + '...');
+    exec('%comspec% /c git clone ' + CORDOVA_JS + ' && cd cordova-js && git fetch && git checkout ' + VERSION );
+    if(!fso.FolderExists(path + '\\temp\\cordova-js'))
+    {
+        WScript.StdErr.WriteLine("ERROR: Failed to clone cordova-js. Aborting...");
+        WScript.Quit(1);
+    }
+    wscript_shell.CurrentDirectory = path + '\\temp\\cordova-js';
+    Log("Building Cordova.js...");
+
+    exec_verbose('%comspec% /c jake build');
+    if(!fso.FolderExists(path + '\\temp\\cordova-js\\pkg'))
+    {
+        WScript.StdErr.WriteLine("ERROR: Failed to build cordova-js. Aborting...");
+        WScript.Quit(1);
+    }
+
+    //copy the javascript wherever it needs to go.
+    wscript_shell.CurrentDirectory = path + '\\temp\\cordova-js\\pkg';
+    exec('%comspec% /c copy /Y cordova.windowsphone.js ' + path + STANDALONE_PATH + '\\www\\cordova-' + VERSION + '.js');
+    exec('%comspec% /c copy /Y cordova.windowsphone.js ' + path + FULL_PATH + '\\www\\cordova-' + VERSION + '.js');
+    exec('%comspec% /c copy /Y cordova.windowsphone.js ' + path + CUSTOM_PATH + '\\www\\cordova-' + VERSION + '.js');
+    exec('%comspec% /c copy /Y cordova.windowsphone.js ' + path + EXAMPLE_PATH + '\\www\\cordova-' + VERSION + '.js');
+
+    //TODO: Delete old cordova.js (done in reversion.js)
+
+    Log("SUCESS");
+}
+
+function set_path(some_arg)
+{
+    if(some_arg.indexOf('-p:')!= -1)
+    {
+        var path = some_arg.split('-p:')[1];
+        if(fso.FolderExists(path) && fso.FolderExists(path + '\\tooling'))
+        {
+            BUILD_DESTINATION = path;
+            return true;
+        }
+        else
+        {
+            Log("ERROR: The given path is not a cordova-wp8 repo, or");
+            Log(" does not exist. If your trying to reversion a");
+            Log(" cordova repo other then this one, please provide");
+            Log(" it's path in the form: -p:C:\\Path\\to\\repo");
+            WScript.Quit(1);
+        }
+        
+    }
+    return false;
+}
+
+Log("");
+
+if(args.Count() > 1)
+{
+    set_path(args(1));
+}
+
+if(args.Count() > 0)
+{
+    //Support help flags
+    if(args(0).indexOf("--help") > -1 ||
+         args(0).indexOf("/?") > -1 )
+    {
+        Usage();
+        WScript.Quit(1);
+    }
+
+    if(args(0).match(/(\d+)[.](\d+)[.](\d+)(rc\d)?/))
+    {
+        VERSION = args(0);
+        if(args.Count()  == 1)
+        {
+            BUILD_DESTINATION = ROOT;
+        }
+    }
+    else if(set_path(arg(0))) {} //do nothing
+    else
+    {
+        Log("The provided version number is invalid, please provide");
+        Log(" a version number in the format Major.Minor.Fix[rc#]");
+        Usage();
+        WScript.Quit(1);
+    }
+}
+else
+{
+    BUILD_DESTINATION = ROOT;
+}
+
+//If we haven't quit by here, build the damn javascript!
+Log("Creating js for " + BUILD_DESTINATION);
+build_js(BUILD_DESTINATION);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tooling/scripts/dist.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tooling/scripts/dist.bat b/lib/cordova-wp8/tooling/scripts/dist.bat
new file mode 100644
index 0000000..a1c0e1d
--- /dev/null
+++ b/lib/cordova-wp8/tooling/scripts/dist.bat
@@ -0,0 +1,2 @@
+@echo off
+cscript "%~dp0\dist.js" %* //nologo

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tooling/scripts/dist.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tooling/scripts/dist.js b/lib/cordova-wp8/tooling/scripts/dist.js
new file mode 100644
index 0000000..9dd93e6
--- /dev/null
+++ b/lib/cordova-wp8/tooling/scripts/dist.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.
+*/
+
+
+/*************************************************/
+/****************  REQUIREMENTS  *****************/
+/*************************************************/
+/*
+Paths:
+  - path to git.exe  -> C:\msysgit\bin
+  - path to msbuild -> C:\Windows\Microsoft.NET\Framework\v4.0.30319
+Famework
+  - .NET 4.0
+  - Windows phone SDKs
+
+
+/************ Globals ********/
+
+var fso = WScript.CreateObject('Scripting.FileSystemObject'),
+    wscript_shell = WScript.CreateObject("WScript.Shell");
+
+//Replace root directory or create new directory?
+var REPLACE = false;
+
+//Set up directory structure of current release
+    //arguments passed in
+var args = WScript.Arguments,
+    //Root folder of cordova-wp8 (i.e C:\Cordova\cordova-wp8)
+    ROOT = WScript.ScriptFullName.split('\\tooling\\', 1),
+    // tooling scripts
+    SCRIPTS = '\\tooling\\scripts';
+    //Get version number
+    VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
+
+//Destination to build to
+var BUILD_DESTINATION;
+//current script that is running
+var current_script = "dist";
+
+
+/*************************************************/
+/****************  FUNCTIONS  ********************/
+/*************************************************/
+
+function Log(msg) {
+    WScript.StdOut.WriteLine(msg);
+}
+
+// help function
+function Usage()
+{
+  Log("");
+  Log("This is a command line tool for building new releases. It will package a new release");
+  Log(" of a cordova-wp8 project, reversioning it to match the VERSION file in the root directory.");
+  Log("Usage: dist [ <NEW_PATH_FOR_BUILD> | -f ] ");
+  Log("                       -f : force tool to reversion the current repositoy.");
+  Log("     <NEW_PATH_FOR_BUILD> : path to create the new reversioned repositoy in.");
+  Log("");
+}
+
+
+// returns the contents of a file
+function read(filename) {
+    //WScript.Echo('Reading in ' + filename);
+    if(fso.FileExists(filename))
+    {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else
+    {
+        Log('Cannot read non-existant file : ' + filename);
+        WScript.Quit(1);
+    }
+    return null;
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if(!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if(!oShell.StdErr.AtEndOfStream)
+    {
+        var err_line = oShell.StdErr.ReadAll();
+        WScript.StdErr.WriteLine(err_line);
+        WScript.StdErr.WriteLine("ERROR: Could not complete distribution, failed while running: " + current_script);
+        WScript.Quit(1);
+    }
+}
+
+function space()
+{
+    Log("");
+    Log("*****************************************************");
+    Log("");
+}
+
+
+/*************************************************/
+/**************  MAIN SCRIPT  ********************/
+/*************************************************/
+
+if(REPLACE)
+{
+    BUILD_DESTINATION = ROOT;
+}
+else if(args.Count() > 0)
+{
+    if(args(0) == '-f') {
+        REPLACE = true;
+        BUILD_DESTINATION = ROOT;
+    } else {
+       BUILD_DESTINATION = args(0);
+      //Support help flags
+      if(BUILD_DESTINATION.indexOf("--help") > -1 ||
+           BUILD_DESTINATION.indexOf("/?") > -1 )
+      {
+          Usage();
+          WScript.Quit(1);
+      }
+    }
+
+}
+else
+{
+    Usage();
+    WScript.Quit(1);
+}
+
+
+/*************************************************/
+/******************  Step 1  *********************/
+/*************************************************/
+/** - Copy source code to new directory         **/
+/*************************************************/
+if (!REPLACE) {
+  current_script = "new.js";
+  exec('cscript ' + ROOT + SCRIPTS + '\\new.js ' + BUILD_DESTINATION + ' //nologo');
+  space();
+}
+
+/*************************************************/
+/******************  Step 2  *********************/
+/*************************************************/
+/** - Retag everything with new version numbers **/
+/** - Delete any generated files and cordova.js **/
+/** - Rebuild dll                               **/
+/*************************************************/
+current_script = "reversion.js";
+exec('cscript ' + BUILD_DESTINATION + SCRIPTS + '\\reversion.js ' + VERSION + ' //nologo');
+space();
+
+/*************************************************/
+/******************  Step 3  *********************/
+/*************************************************/
+/** - Download tagged version of cordova.js     **/
+/** - build cordova.js                          **/
+/** - windows.cordova.js -> templates + example **/
+/*************************************************/
+current_script = "buildjs.js";
+exec('cscript ' + BUILD_DESTINATION + SCRIPTS + '\\buildjs.js //nologo');
+space();
+
+/*************************************************/
+/******************  Step 5  *********************/
+/*************************************************/
+/** - Build templates                           **/
+/** - Zip templates                             **/
+/** - inject into Visual Studio                 **/
+/*************************************************/
+current_script = "package.js";
+exec('cscript ' + BUILD_DESTINATION + SCRIPTS + '\\package.js //nologo');
+space();
+Log("Distribution Complete.");
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tooling/scripts/new.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tooling/scripts/new.bat b/lib/cordova-wp8/tooling/scripts/new.bat
new file mode 100644
index 0000000..cb08e2e
--- /dev/null
+++ b/lib/cordova-wp8/tooling/scripts/new.bat
@@ -0,0 +1,2 @@
+@echo off
+cscript "%~dp0\new.js" %* //nologo
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tooling/scripts/new.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tooling/scripts/new.js b/lib/cordova-wp8/tooling/scripts/new.js
new file mode 100644
index 0000000..7462965
--- /dev/null
+++ b/lib/cordova-wp8/tooling/scripts/new.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 fso = WScript.CreateObject('Scripting.FileSystemObject'),
+    shell = WScript.CreateObject("shell.application"),
+    wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments,
+    //Root folder of cordova-wp8 (i.e C:\Cordova\cordova-wp8)
+    ROOT = WScript.ScriptFullName.split('\\tooling\\', 1),
+    //Sub folder containing templates
+    TEMPLATES_PATH = '\\templates',
+    //Sub folder containing framework
+    FRAMEWORK_PATH = '\\framework',
+    //Subfolder containing example project
+    EXAMPLE_PATH = '\\example';
+// git repo for cordova-wp8
+var CORDOVA_WP8 = 'https://git-wip-us.apache.org/repos/asf/cordova-wp8.git';
+//Destination to build to
+var BUILD_DESTINATION;
+// pull the project down from github?
+var GET_NEW = false;
+
+// help function
+function Usage()
+{
+    WScript.StdOut.WriteLine("");
+    WScript.StdOut.WriteLine("Usage: new [ PathToDestinationFolder ]");
+    WScript.StdOut.WriteLine("    PathToDestinationFolder : Folder you wish to be created for a new cordova-wp8 repo");
+    WScript.StdOut.WriteLine("examples:");
+    WScript.StdOut.WriteLine("    new C:\\Users\\anonymous\\Desktop\\cordova-wp8");
+    WScript.StdOut.WriteLine("");
+}
+
+// returns the contents of a file
+function read(filename) {
+    //WScript.StdOut.WriteLine('Reading in ' + filename);
+    if(fso.FileExists(filename))
+    {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else
+    {
+        WScript.StdErr.WriteLine('Cannot read non-existant file : ' + filename);
+        WScript.Quit(1);
+    }
+    return null;
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        WScript.sleep(100);
+    }
+}
+
+function copy_to(path)
+{
+    //Copy everything over to BUILD_DESTINATION
+    var dest = shell.NameSpace(path);
+    WScript.StdOut.WriteLine("Copying files to build directory...");
+
+    /** copy by file instead? (just what we need)**/
+    dest.CopyHere(ROOT + "\\bin", 4|20);
+    dest.CopyHere(ROOT + EXAMPLE_PATH, 4|20);      //Should mostly be copied from standalone
+    dest.CopyHere(ROOT + FRAMEWORK_PATH, 4|20);
+    dest.CopyHere(ROOT + TEMPLATES_PATH, 4|20);
+    dest.CopyHere(ROOT + "\\tests", 4|20);
+    dest.CopyHere(ROOT + "\\tooling", 4|20);
+    dest.CopyHere(ROOT + "\\.gitignore", 4|20);
+    dest.CopyHere(ROOT + "\\LICENSE", 4|20);
+    dest.CopyHere(ROOT + "\\NOTICE", 4|20);
+    dest.CopyHere(ROOT + "\\README.md", 4|20);
+    dest.CopyHere(ROOT + "\\VERSION", 4|20);
+}
+
+WScript.StdOut.WriteLine("");
+
+if(args.Count() > 0)
+{
+    if(fso.FolderExists(args(0)))
+    {
+        WScript.StdErr.WriteLine("The given directory already exists!");
+        Usage();
+        WScript.Quit(1);
+    }
+    else
+    {
+        BUILD_DESTINATION = args(0);
+
+    }
+
+    if(!GET_NEW) {
+
+        if(fso.FolderExists(BUILD_DESTINATION))
+        {
+            WScript.StdErr.WriteLine("The given directory already exists!");
+            Usage();
+            WScript.Quit(1);
+        }
+        else
+        {
+            BUILD_DESTINATION = args(0);
+        }
+
+        // set up file structure
+        fso.CreateFolder(BUILD_DESTINATION);
+        // copy nessisary files
+        copy_to(BUILD_DESTINATION);
+    }
+    else
+    {
+        wscript_shell.CurrentDirectory = arg(0) + '\\..';
+        BUILD_DESTINATION = wscript_shell.CurrentDirectory + '\\cordova-wp8';
+
+        WScript.StdOut.WriteLine('Cloning cordova-wp8 from git, build destination now ' + BUILD_DESTINATION);
+        if(fso.FolderExists(BUILD_DESTINATION))
+        {
+            WScript.StdErr.WriteLine("Could not clone cordova-wp8 from git because it's directory already exists!");
+            WScript.Quit(1);
+        }
+
+        exec('git clone ' + CORDOVA_WP8); //git fetch --tags && git checkout?
+
+    }
+}
+else
+{
+    Usage();
+    WScript.Quit(1);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tooling/scripts/package.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tooling/scripts/package.bat b/lib/cordova-wp8/tooling/scripts/package.bat
new file mode 100644
index 0000000..c00551e
--- /dev/null
+++ b/lib/cordova-wp8/tooling/scripts/package.bat
@@ -0,0 +1,2 @@
+@echo off
+cscript "%~dp0\package.js" %* //nologo
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tooling/scripts/package.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tooling/scripts/package.js b/lib/cordova-wp8/tooling/scripts/package.js
new file mode 100644
index 0000000..57a7340
--- /dev/null
+++ b/lib/cordova-wp8/tooling/scripts/package.js
@@ -0,0 +1,213 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject'),
+    shell = WScript.CreateObject("shell.application"),
+    wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments,
+    // root folder of cordova-wp8 (i.e C:\Cordova\cordova-wp8)
+    ROOT = WScript.ScriptFullName.split('\\tooling\\', 1),
+    // sub folder containing templates
+    TEMPLATES_PATH = '\\templates',
+    // sub folder for standalone project
+    STANDALONE_PATH = TEMPLATES_PATH + '\\standalone',
+    // sub folder containing framework
+    FRAMEWORK_PATH = '\\framework',
+    // subfolder containing example project
+    EXAMPLE_PATH = '\\example',
+    // get version number
+    VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,''),
+    BASE_VERSION = VERSION.split('rc', 1) + ".0";
+
+// destination directory to package
+var BUILD_DESTINATION;
+// add templates to visual studio?
+var ADD_TO_VS = false;
+// build the dll?
+var BUILD_DLL = false;
+
+function Log(msg) { WScript.StdOut.WriteLine(msg); }
+
+// help function
+function Usage()
+{
+    Log("");
+    Log("Usage: package [ PathToCordovaWP8 ]");
+    Log("    PathToCordovaWP8 : Cordova-wp8 repo you wish to package for release");
+    Log("examples:");
+    Log("    package C:\\Users\\anonymous\\Desktop\\cordova-wp8");
+    Log("    package     // packages current cordova directory");
+    Log("");
+}
+
+// returns the contents of a file
+function read(filename) {
+    //Log('Reading in ' + filename);
+    if(fso.FileExists(filename))
+    {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else
+    {
+        Log('ERROR: Cannot read non-existant file : ' + filename);
+        WScript.Quit(1);
+    }
+    return null;
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        WScript.sleep(100);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if(!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if(!oShell.StdErr.AtEndOfStream)
+    {
+        var err_line = oShell.StdErr.ReadAll();
+        WScript.StdErr.WriteLine(err_line);
+        WScript.Quit(1);
+    }
+}
+
+// packages templates into .zip
+function package_templates()
+{
+    Log("Creating template .zip files ...");
+
+    var template_path = BUILD_DESTINATION + '\\CordovaWP8_' + VERSION.replace(/\./g, '_') + '.zip';
+    if(fso.FileExists(template_path))
+    {
+      fso.DeleteFile(template_path);
+    }
+
+    exec('%comspec% /c copy /Y ' + BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\MyTemplateStandAlone.vstemplate ' + BUILD_DESTINATION + STANDALONE_PATH + '\\MyTemplate.vstemplate');
+    exec('%comspec% /c copy /Y ' + BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\pg_templateIcon.png ' + BUILD_DESTINATION + STANDALONE_PATH + '\\__TemplateIcon.png');
+    exec('%comspec% /c copy /Y ' + BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\pg_templatePreview.jpg ' + BUILD_DESTINATION + STANDALONE_PATH + '\\__PreviewImage.jpg');
+    exec('%comspec% /c copy /Y ' + BUILD_DESTINATION + '\\VERSION ' + BUILD_DESTINATION + STANDALONE_PATH);
+
+    exec_verbose('cscript ' + BUILD_DESTINATION + '\\tooling\\scripts\\win-zip.js ' + template_path + ' ' + BUILD_DESTINATION + STANDALONE_PATH + '\\');
+
+
+    if(ADD_TO_VS)
+    {
+        var template_dir = wscript_shell.ExpandEnvironmentStrings("%USERPROFILE%") + '\\Documents\\Visual Studio 2012\\Templates\\ProjectTemplates';
+        if(fso.FolderExists(template_dir ))
+        {
+            dest = shell.NameSpace(template_dir);
+            dest.CopyHere(template_path, 4|20);
+        }
+        else
+        {
+            Log("WARNING: Could not find template directory in Visual Studio,\n you can manually copy over the template .zip files.");
+        }
+  }
+}
+
+// builds the new cordova dll and copys it to the full template (only done because of the version referance in Device.cs)
+function build_dll()
+{
+    Log("Packaging .dll ...");
+    // move to framework directory
+    wscript_shell.CurrentDirectory = BUILD_DESTINATION + FRAMEWORK_PATH;
+    // build .dll in Release
+    exec_verbose('msbuild /p:Configuration=Release;VersionNumber=' + VERSION + ';BaseVersionNumber=' + BASE_VERSION);
+    //Check if file dll was created
+    if(!fso.FileExists(BUILD_DESTINATION + FRAMEWORK_PATH + '\\Bin\\Release\\WPCordovaClassLib.dll'))
+    {
+        WScript.StdErr.WriteLine('ERROR: MSBuild failed to create .dll.');
+        WScript.Quit(1);
+    }
+
+    Log("SUCESS");
+}
+
+// delete any unnessisary files when finished
+function cleanUp() {
+
+  if(fso.FileExists(BUILD_DESTINATION + STANDALONE_PATH + '\\MyTemplate.vstemplate')) {
+      fso.DeleteFile(BUILD_DESTINATION + STANDALONE_PATH + '\\MyTemplate.vstemplate');
+  }
+  if(fso.FileExists(BUILD_DESTINATION + STANDALONE_PATH + '\\__PreviewImage.jpg')) {
+      fso.DeleteFile(BUILD_DESTINATION + STANDALONE_PATH + '\\__PreviewImage.jpg');
+  }
+  if(fso.FileExists(BUILD_DESTINATION + STANDALONE_PATH + '\\__TemplateIcon.png')) {
+      fso.DeleteFile(BUILD_DESTINATION + STANDALONE_PATH + '\\__TemplateIcon.png');
+  }
+  //Add any other cleanup here
+}
+
+
+Log("");
+
+if(args.Count() > 0)
+{
+    //Support help flags
+    if(args(0).indexOf("--help") > -1 ||
+         args(0).indexOf("/?") > -1 )
+    {
+        Usage();
+        WScript.Quit(1);
+    }
+
+    if(fso.FolderExists(args(0)) && fso.FolderExists(args(0) + '\\tooling'))
+    {
+        BUILD_DESTINATION = args(0);
+    }
+    else
+    {
+        Log("ERROR: The given directory is not a cordova-wp8 repo.");
+        Usage();
+        WScript.Quit(1);
+
+    }
+}
+else
+{
+    BUILD_DESTINATION = ROOT;
+}
+
+// build dll for full template
+if (BUILD_DLL) {
+  build_dll();
+}
+
+// build/package the templates
+package_templates(BUILD_DESTINATION);
+
+cleanUp();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tooling/scripts/reversion.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tooling/scripts/reversion.bat b/lib/cordova-wp8/tooling/scripts/reversion.bat
new file mode 100644
index 0000000..b35ab08
--- /dev/null
+++ b/lib/cordova-wp8/tooling/scripts/reversion.bat
@@ -0,0 +1,2 @@
+@echo off
+cscript "%~dp0\reversion.js" %* //nologo
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tooling/scripts/reversion.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tooling/scripts/reversion.js b/lib/cordova-wp8/tooling/scripts/reversion.js
new file mode 100644
index 0000000..a726817
--- /dev/null
+++ b/lib/cordova-wp8/tooling/scripts/reversion.js
@@ -0,0 +1,280 @@
+/*
+       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.
+*/
+
+
+/************ Globals ********/
+
+var fso = WScript.CreateObject('Scripting.FileSystemObject'),
+    shell = WScript.CreateObject("shell.application"),
+    wscript_shell = WScript.CreateObject("WScript.Shell");
+
+//Get new version from git or build off this version?
+var GET_NEW = false;
+
+//Set up directory structure of current release
+    //arguments passed in
+var args = WScript.Arguments,
+    //Root folder of cordova-wp8 (i.e C:\Cordova\cordova-wp8)
+    ROOT = WScript.ScriptFullName.split('\\tooling\\', 1),
+    //Sub folder containing templates
+    TEMPLATES_PATH = '\\templates',
+    //Sub folder for standalone project
+    STANDALONE_PATH = TEMPLATES_PATH + '\\standalone',
+    //Sub folder containing framework
+    FRAMEWORK_PATH = '\\framework',
+    //Subfolder containing example project
+    EXAMPLE_PATH = '\\example',
+    //Path to cordovalib folder, containing source for .dll
+    CORDOVA_LIB = STANDALONE_PATH + '\\cordovalib',
+    //Get version number
+    VERSION='',
+    BASE_VERSION = '';
+
+//Destination to build to
+var BUILD_DESTINATION;
+
+// help function
+function Usage()
+{
+    WScript.StdOut.WriteLine("");
+    WScript.StdOut.WriteLine("Usage: reversion [ Version PathTOCordovaWP8 ]");
+    WScript.StdOut.WriteLine("    Version : The new version for codova-wp8");
+    WScript.StdOut.WriteLine("    PathTOCordovaWP8 : The path to the cordova directory being reversioned.");
+    WScript.StdOut.WriteLine("examples:");
+    WScript.StdOut.WriteLine("    reversion 2.5.0rc1  //Reversions the current working directory");
+    WScript.StdOut.WriteLine("    reversion 2.5.0 C :\\Users\\anonymous\\Desktop\\cordova-wp8");
+    WScript.StdOut.WriteLine("");
+}
+
+var ForReading = 1, ForWriting = 2, ForAppending = 8;
+var TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0;
+
+// returns the contents of a file
+function read(filename) {
+    //WScript.Echo('Reading in ' + filename);
+    if(fso.FileExists(filename))
+    {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else
+    {
+        WScript.StdErr.WriteLine('Cannot read non-existant file : ' + filename);
+        WScript.Quit(1);
+    }
+    return null;
+}
+
+// writes the contents to the specified file
+function write(filename, contents) {
+    var f=fso.OpenTextFile(filename, ForWriting, TristateTrue);
+    f.Write(contents);
+    f.Close();
+}
+
+// replaces the matches of regexp with replacement
+function replaceInFile(filename, regexp, replacement) {
+    //WScript.Echo("Replaceing with "+replacement+ " in:");
+    var text = read(filename).replace(regexp,replacement);
+    //WScript.Echo(text);
+    write(filename,text);
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        WScript.sleep(100);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    //WScript.StdOut.WriteLine("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if(!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            WScript.StdOut.WriteLine(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if(!oShell.StdErr.AtEndOfStream)
+    {
+        var err_line = oShell.StdErr.ReadAll();
+        WScript.StdErr.WriteLine(err_line);
+        WScript.Quit(1);
+    }
+}
+
+function updateVersionNumbers() {
+    WScript.StdOut.WriteLine("Updating version numbers....");
+    var version_regex = /(\d+)[.](\d+)[.](\d+)(rc\d)?/;
+    replaceInFile(BUILD_DESTINATION + '\\VERSION', version_regex,  VERSION);
+    // replace assembaly versions in framework
+    var framework_regex = /Description\(\"(\d+)[.](\d+)[.](\d+)(rc\d)?\"\)\]/; //Will match ("x.x.x[rcx]")]
+    replaceInFile(BUILD_DESTINATION + FRAMEWORK_PATH + "\\Properties\\AssemblyInfo.cs", framework_regex, "Description(\"" + VERSION + "\")]");
+    framework_regex = /Version\(\"(\d+)[.](\d+)[.](\d+)[.](\d+)\"\)\]/g;
+    replaceInFile(BUILD_DESTINATION + FRAMEWORK_PATH + "\\Properties\\AssemblyInfo.cs", framework_regex, "Version(\"" + BASE_VERSION + "\")]");
+
+    // update standalone project
+    exec('%comspec% /c copy /Y /V ' + BUILD_DESTINATION + "\\VERSION " + BUILD_DESTINATION + STANDALONE_PATH + "\\VERSION");
+    var cordova_regex = /cordova-(\d+)[.](\d+)[.](\d+)(rc\d)?/g; //Matches *first* cordova-x.x.x[rcx] (just ad g at end to make global)
+    replaceInFile(BUILD_DESTINATION + STANDALONE_PATH + '\\CordovaAppProj.csproj', cordova_regex,  "cordova-" + VERSION);
+    replaceInFile(BUILD_DESTINATION + STANDALONE_PATH + '\\www\\index.html', cordova_regex,  "cordova-" + VERSION);
+    version_regex = /return\s*\"(\d+)[.](\d+)[.](\d+)(rc\d)?/; //Matches return "x.x.x[rcx]
+
+    WScript.StdOut.WriteLine("path = " + BUILD_DESTINATION + CORDOVA_LIB + '\\Plugins\\Device.cs');
+    replaceInFile(BUILD_DESTINATION + CORDOVA_LIB + '\\..\\Plugins\\Device.cs', version_regex,  "return \"" + VERSION);
+
+    // update example proj
+    replaceInFile(BUILD_DESTINATION + EXAMPLE_PATH + '\\CordovaExample.csproj', cordova_regex,  "cordova-" + VERSION);
+    version_regex = /VERSION\s*\=\s*\'(\d+)[.](\d+)[.](\d+)(rc\d)?/;  //Matches VERSION = x.x.x[rcx]
+    replaceInFile(BUILD_DESTINATION + EXAMPLE_PATH + '\\www\\cordova-current.js', version_regex,  "VERSION = \'" + VERSION);
+
+    // update template discription
+    version_regex = /Cordova\s*(\d+)[.](\d+)[.](\d+)(rc\d)?\s*Windows/g; //Matches version: x.x.x[rcx]
+    replaceInFile(BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\description.txt', version_regex,  "Cordova " + VERSION + " Windows");
+
+    // update .vstemplate files for the template zips.
+    var name_regex = /CordovaWP8[_](\d+)[_](\d+)[_](\d+)(rc\d)?/g;
+    var discript_regex = /Cordova\s*(\d+)[.](\d+)[.](\d+)(rc\d)?/;
+
+    replaceInFile(BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\MyTemplateStandAlone.vstemplate', name_regex,  'CordovaWP8_' + VERSION.replace(/\./g, '_'));
+    replaceInFile(BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\MyTemplateStandAlone.vstemplate', discript_regex,  "Cordova " + VERSION);
+    replaceInFile(BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\MyTemplateStandAlone.vstemplate', cordova_regex,  "cordova-" + VERSION);
+}
+
+// delete all cordova.js and generated files (templates) from old version numbers
+function cleanup()
+{
+    WScript.StdOut.WriteLine("Cleanup");
+    // remove old template .zip files
+    if(fso.FileExists(BUILD_DESTINATION + '\\*.zip'))
+    {
+        fso.DeleteFile(BUILD_DESTINATION + '\\*.zip');
+    }
+    // remove any generated framework files
+    if(fso.FolderExists(BUILD_DESTINATION + FRAMEWORK_PATH + '\\Bin'))
+    {
+        fso.DeleteFolder(BUILD_DESTINATION + FRAMEWORK_PATH + '\\Bin');
+    }
+    if(fso.FolderExists(BUILD_DESTINATION + FRAMEWORK_PATH + '\\obj'))
+    {
+        fso.DeleteFolder(BUILD_DESTINATION + FRAMEWORK_PATH + '\\obj');
+    }
+    // remove any generated CordovaDeploy
+    if(fso.FolderExists(BUILD_DESTINATION + '\\tooling\\CordovaDeploy\\CordovaDeploy\\bin'))
+    {
+        fso.DeleteFolder(BUILD_DESTINATION + '\\tooling\\CordovaDeploy\\CordovaDeploy\\bin');
+    }
+    if(fso.FolderExists(BUILD_DESTINATION + '\\tooling\\CordovaDeploy\\CordovaDeploy\\obj'))
+    {
+        fso.DeleteFolder(BUILD_DESTINATION + '\\tooling\\CordovaDeploy\\CordovaDeploy\\obj');
+    }
+    //remove old template .zip files
+    WScript.Echo(BUILD_DESTINATION);
+    var root_folder = shell.NameSpace(BUILD_DESTINATION + '\\').Items();
+    for(var i = 0; i < root_folder.Count; i++)
+    {
+        if(root_folder.Item(i).Name.match(/CordovaWP8[_](\d+)[_](\d+)[_](\d+)(rc\d)?/))
+        {
+            fso.DeleteFile(BUILD_DESTINATION + '\\' + root_folder.Item(i).Name);
+        }
+    }
+    // remove old cordova.js
+    var example_www = shell.NameSpace(BUILD_DESTINATION + EXAMPLE_PATH + '\\www').Items();
+    for(i = 0; i < example_www.Count; i++)
+    {
+        if(example_www.Item(i).Name.match(/cordova\-(\d+)[.](\d+)[.](\d+)(rc\d)?[.]js/))
+        {
+            fso.DeleteFile(BUILD_DESTINATION + EXAMPLE_PATH + '\\www\\' + example_www.Item(i).Name);
+        }
+    }
+
+    var standalone_www = shell.NameSpace(BUILD_DESTINATION + STANDALONE_PATH + '\\www').Items();
+    for(i = 0; i < standalone_www.Count; i++)
+    {
+        if(standalone_www.Item(i).Name.match(/cordova\-(\d+)[.](\d+)[.](\d+)(rc\d)?[.]js/))
+        {
+            fso.DeleteFile(BUILD_DESTINATION + STANDALONE_PATH + '\\www\\' + standalone_www.Item(i).Name);
+        }
+    }
+}
+
+
+WScript.StdOut.WriteLine("");
+
+if(args.Count() > 1)
+{
+    if(fso.FolderExists(args(1)) && fso.FolderExists(args(1) + '\\tooling'))
+    {
+        BUILD_DESTINATION = args(1);
+    }
+    else
+    {
+        WScript.StdErr.WriteLine("The given path is not a cordova-wp8 repo, if");
+        WScript.StdErr.WriteLine(" your trying to reversion a cordova-wp8 repo");
+        WScript.StdErr.WriteLine(" other then this one, please provide its path.");
+        Usage();
+        WScript.Quit(1);
+    }
+}
+
+if(args.Count() > 0)
+{
+    //Support help flags
+    if(args(0).indexOf("--help") > -1 ||
+         args(0).indexOf("/?") > -1 )
+    {
+        Usage();
+        WScript.Quit(1);
+    }
+
+    if(args(0).match(/(\d+)[.](\d+)[.](\d+)(rc\d)?/))
+    {
+        VERSION = args(0);
+        BASE_VERSION = VERSION.split('rc', 1) + ".0";
+        if(args.Count() < 2)
+        {
+          BUILD_DESTINATION = ROOT;
+        }
+        // remove old cordova.js files and any generated files
+        cleanup();
+        // update version numbers
+        updateVersionNumbers();
+    }
+    else
+    {
+        WScript.StdOut.WriteLine("The  version number is invalid, please provide");
+        WScript.StdOut.WriteLine(" a version number in the format Major.Minor.Fix[rc#]");
+        Usage();
+        WScript.Quit(1);
+    }
+}
+else
+{
+    Usage();
+    WScript.Quit(1);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tooling/scripts/win-zip.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tooling/scripts/win-zip.js b/lib/cordova-wp8/tooling/scripts/win-zip.js
new file mode 100644
index 0000000..7a79f43
--- /dev/null
+++ b/lib/cordova-wp8/tooling/scripts/win-zip.js
@@ -0,0 +1,43 @@
+/*
+ * Script for zipping the contents of a directory.
+ */
+
+// get commman line arguments
+var objArgs = WScript.Arguments;
+var zipPath = objArgs(0);
+var sourcePath = objArgs(1);
+
+
+// create empty ZIP file and open for adding
+var fso = new ActiveXObject("Scripting.FileSystemObject");
+var file = fso.CreateTextFile(zipPath, true);
+
+// create twenty-two byte "fingerprint" for .zip
+file.write("PK");
+file.write(String.fromCharCode(5));
+file.write(String.fromCharCode(6));
+file.write('\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0');
+file.Close();
+
+// open .zip foder and copy contents of sourcePath
+var objShell = new ActiveXObject("shell.application");
+var zipFolder = objShell.NameSpace(zipPath);
+var sourceItems = objShell.NameSpace(sourcePath).items();
+if (zipFolder !== null) {
+    zipFolder.CopyHere(sourceItems, 4|20|16);
+    var maxTime = 5000;
+    while(zipFolder.items().Count < sourceItems.Count)
+    {
+        maxTime -= 100;
+        if(maxTime > 0 ) {    
+            WScript.Sleep(100);
+        }
+        else {
+            WScript.StdErr.WriteLine('Failed to create .zip file.');
+            break;
+        }
+    }
+}
+else {
+	WScript.StdErr.WriteLine('Failed to create .zip file.');
+}
\ No newline at end of file


[23/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/Camera.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/Camera.cs b/lib/cordova-wp8/templates/standalone/Plugins/Camera.cs
new file mode 100644
index 0000000..5ff8045
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/Camera.cs
@@ -0,0 +1,490 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Collections.Generic;
+using Microsoft.Phone.Tasks;
+using System.Runtime.Serialization;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Windows.Media.Imaging;
+using Microsoft.Phone;
+using Microsoft.Xna.Framework.Media;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public class Camera : BaseCommand
+    {
+
+        /// <summary>
+        /// Return base64 encoded string
+        /// </summary>
+        private const int DATA_URL = 0;
+
+        /// <summary>
+        /// Return file uri
+        /// </summary>
+        private const int FILE_URI = 1;
+
+        /// <summary>
+        /// Choose image from picture library
+        /// </summary>
+        private const int PHOTOLIBRARY = 0;
+
+        /// <summary>
+        /// Take picture from camera
+        /// </summary>
+
+        private const int CAMERA = 1;
+
+        /// <summary>
+        /// Choose image from picture library
+        /// </summary>
+        private const int SAVEDPHOTOALBUM = 2;
+
+        /// <summary>
+        /// Take a picture of type JPEG
+        /// </summary>
+        private const int JPEG = 0;
+
+        /// <summary>
+        /// Take a picture of type PNG
+        /// </summary>
+        private const int PNG = 1;
+
+        /// <summary>
+        /// Folder to store captured images
+        /// </summary>
+        private const string isoFolder = "CapturedImagesCache";
+
+        /// <summary>
+        /// Represents captureImage action options.
+        /// </summary>
+        [DataContract]
+        public class CameraOptions
+        {
+            /// <summary>
+            /// Source to getPicture from.
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "sourceType")]
+            public int PictureSourceType { get; set; }
+
+            /// <summary>
+            /// Format of image that returned from getPicture.
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "destinationType")]
+            public int DestinationType { get; set; }
+
+            /// <summary>
+            /// Quality of saved image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "quality")]
+            public int Quality { get; set; }
+
+            /// <summary>
+            /// Controls whether or not the image is also added to the device photo album.
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "saveToPhotoAlbum")]
+            public bool SaveToPhotoAlbum { get; set; }
+
+            /// <summary>
+            /// Ignored
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "correctOrientation")]
+            public bool CorrectOrientation { get; set; }
+
+            
+
+            /// <summary>
+            /// Ignored
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "allowEdit")]
+            public bool AllowEdit { get; set; }
+
+                        /// <summary>
+            /// Height in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "encodingType")]
+            public int EncodingType { get; set; }
+
+                        /// <summary>
+            /// Height in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "mediaType")]
+            public int MediaType { get; set; }
+
+
+            /// <summary>
+            /// Height in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "targetHeight")]
+            public int TargetHeight { get; set; }
+
+
+            /// <summary>
+            /// Width in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "targetWidth")]
+            public int TargetWidth { get; set; }
+
+            /// <summary>
+            /// Creates options object with default parameters
+            /// </summary>
+            public CameraOptions()
+            {
+                this.SetDefaultValues(new StreamingContext());
+            }
+
+            /// <summary>
+            /// Initializes default values for class fields.
+            /// Implemented in separate method because default constructor is not invoked during deserialization.
+            /// </summary>
+            /// <param name="context"></param>
+            [OnDeserializing()]
+            public void SetDefaultValues(StreamingContext context)
+            {
+                PictureSourceType = CAMERA;
+                DestinationType = FILE_URI;
+                Quality = 80;
+                TargetHeight = -1;
+                TargetWidth = -1;
+                SaveToPhotoAlbum = false;
+                CorrectOrientation = true;
+                AllowEdit = false;
+                MediaType = -1;
+                EncodingType = -1;
+            }
+        }
+
+        /// <summary>
+        /// Used to open photo library
+        /// </summary>
+        PhotoChooserTask photoChooserTask;
+
+        /// <summary>
+        /// Used to open camera application
+        /// </summary>
+        CameraCaptureTask cameraTask;
+
+        /// <summary>
+        /// Camera options
+        /// </summary>
+        CameraOptions cameraOptions;
+
+        public void takePicture(string options)
+        {
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                // ["quality", "destinationType", "sourceType", "targetWidth", "targetHeight", "encodingType",
+                //     "mediaType", "allowEdit", "correctOrientation", "saveToPhotoAlbum" ]
+                this.cameraOptions = new CameraOptions();
+                this.cameraOptions.Quality = int.Parse(args[0]);
+                this.cameraOptions.DestinationType = int.Parse(args[1]);
+                this.cameraOptions.PictureSourceType = int.Parse(args[2]);
+                this.cameraOptions.TargetWidth = int.Parse(args[3]);
+                this.cameraOptions.TargetHeight = int.Parse(args[4]);
+                this.cameraOptions.EncodingType = int.Parse(args[5]);
+                this.cameraOptions.MediaType = int.Parse(args[6]);
+                this.cameraOptions.AllowEdit = bool.Parse(args[7]);
+                this.cameraOptions.CorrectOrientation = bool.Parse(args[8]);
+                this.cameraOptions.SaveToPhotoAlbum = bool.Parse(args[9]);
+                
+                //this.cameraOptions = String.IsNullOrEmpty(options) ?
+                //        new CameraOptions() : JSON.JsonHelper.Deserialize<CameraOptions>(options);
+            }
+            catch (Exception ex)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                return;
+            }
+
+            //TODO Check if all the options are acceptable
+
+
+            if (cameraOptions.PictureSourceType == CAMERA)
+            {
+                cameraTask = new CameraCaptureTask();
+                cameraTask.Completed += onCameraTaskCompleted;
+                cameraTask.Show();
+            }
+            else
+            {
+                if ((cameraOptions.PictureSourceType == PHOTOLIBRARY) || (cameraOptions.PictureSourceType == SAVEDPHOTOALBUM))
+                {
+                    photoChooserTask = new PhotoChooserTask();
+                    photoChooserTask.Completed += onPickerTaskCompleted;
+                    photoChooserTask.Show();
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
+                }
+            }
+
+        }
+
+        public void onCameraTaskCompleted(object sender, PhotoResult e)
+        {
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        string imagePathOrContent = string.Empty;
+
+                        if (cameraOptions.DestinationType == FILE_URI)
+                        {
+                            // Save image in media library
+                            if (cameraOptions.SaveToPhotoAlbum)
+                            {
+                                MediaLibrary library = new MediaLibrary();
+                                Picture pict = library.SavePicture(e.OriginalFileName, e.ChosenPhoto); // to save to photo-roll ...
+                            }
+
+                            int orient = ImageExifHelper.getImageOrientationFromStream(e.ChosenPhoto);
+                            int newAngle = 0;
+                            switch (orient)
+                            {
+                                case ImageExifOrientation.LandscapeLeft:
+                                    newAngle = 90;
+                                    break;
+                                case ImageExifOrientation.PortraitUpsideDown:
+                                    newAngle = 180;
+                                    break;
+                                case ImageExifOrientation.LandscapeRight:
+                                    newAngle = 270;
+                                    break;
+                                case ImageExifOrientation.Portrait:
+                                default: break; // 0 default already set
+                            }
+
+                            Stream rotImageStream = ImageExifHelper.RotateStream(e.ChosenPhoto, newAngle);
+
+                            // we should return stream position back after saving stream to media library
+                            rotImageStream.Seek(0, SeekOrigin.Begin);
+
+                            WriteableBitmap image = PictureDecoder.DecodeJpeg(rotImageStream);
+
+                            imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
+
+
+                        }
+                        else if (cameraOptions.DestinationType == DATA_URL)
+                        {
+                            imagePathOrContent = this.GetImageContent(e.ChosenPhoto);
+                        }
+                        else
+                        {
+                            // TODO: shouldn't this happen before we launch the camera-picker?
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
+                            return;
+                        }
+
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
+
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
+                    break;
+
+                default:
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
+                    break;
+            }
+
+        }
+
+        public void onPickerTaskCompleted(object sender, PhotoResult e)
+        {
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        string imagePathOrContent = string.Empty;
+
+                        if (cameraOptions.DestinationType == FILE_URI)
+                        {
+                            WriteableBitmap image = PictureDecoder.DecodeJpeg(e.ChosenPhoto);
+                            imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
+                        }
+                        else if (cameraOptions.DestinationType == DATA_URL)
+                        {
+                            imagePathOrContent = this.GetImageContent(e.ChosenPhoto);
+
+                        }
+                        else
+                        {
+                            // TODO: shouldn't this happen before we launch the camera-picker?
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
+                            return;
+                        }
+
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
+
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
+                    break;
+
+                default:
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Returns image content in a form of base64 string
+        /// </summary>
+        /// <param name="stream">Image stream</param>
+        /// <returns>Base64 representation of the image</returns>
+        private string GetImageContent(Stream stream)
+        {
+            int streamLength = (int)stream.Length;
+            byte[] fileData = new byte[streamLength + 1];
+            stream.Read(fileData, 0, streamLength);
+
+            //use photo's actual width & height if user doesn't provide width & height
+            if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
+            {
+                stream.Close();
+                return Convert.ToBase64String(fileData);
+            }
+            else
+            {
+                // resize photo
+                byte[] resizedFile = ResizePhoto(stream, fileData);
+                stream.Close();
+                return Convert.ToBase64String(resizedFile);
+            }
+        }
+
+        /// <summary>
+        /// Resize image
+        /// </summary>
+        /// <param name="stream">Image stream</param>
+        /// <param name="fileData">File data</param>
+        /// <returns>resized image</returns>
+        private byte[] ResizePhoto(Stream stream, byte[] fileData)
+        {
+            int streamLength = (int)stream.Length;
+            int intResult = 0;
+
+            byte[] resizedFile;
+
+            stream.Read(fileData, 0, streamLength);
+
+            BitmapImage objBitmap = new BitmapImage();
+            MemoryStream objBitmapStream = new MemoryStream(fileData);
+            MemoryStream objBitmapStreamResized = new MemoryStream();
+            WriteableBitmap objWB;
+            objBitmap.SetSource(stream);
+            objWB = new WriteableBitmap(objBitmap);
+
+            // resize the photo with user defined TargetWidth & TargetHeight
+            Extensions.SaveJpeg(objWB, objBitmapStreamResized, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
+
+            //Convert the resized stream to a byte array. 
+            streamLength = (int)objBitmapStreamResized.Length;
+            resizedFile = new Byte[streamLength]; //-1 
+            objBitmapStreamResized.Position = 0;
+            //for some reason we have to set Position to zero, but we don't have to earlier when we get the bytes from the chosen photo... 
+            intResult = objBitmapStreamResized.Read(resizedFile, 0, streamLength);
+
+            return resizedFile;
+        }
+
+        /// <summary>
+        /// Saves captured image in isolated storage
+        /// </summary>
+        /// <param name="imageFileName">image file name</param>
+        /// <returns>Image path</returns>
+        private string SaveImageToLocalStorage(WriteableBitmap image, string imageFileName)
+        {
+
+            if (image == null)
+            {
+                throw new ArgumentNullException("imageBytes");
+            }
+            try
+            {
+
+
+                var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+
+                if (!isoFile.DirectoryExists(isoFolder))
+                {
+                    isoFile.CreateDirectory(isoFolder);
+                }
+
+                string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName);
+
+                using (var stream = isoFile.CreateFile(filePath))
+                {
+                    // resize image if Height and Width defined via options 
+                    if (cameraOptions.TargetHeight > 0 && cameraOptions.TargetWidth > 0)
+                    {
+                        image.SaveJpeg(stream, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
+                    }
+                    else
+                    {
+                        image.SaveJpeg(stream, image.PixelWidth, image.PixelHeight, 0, cameraOptions.Quality);
+                    }
+                }
+
+                return new Uri(filePath, UriKind.Relative).ToString();
+            }
+            catch (Exception)
+            {
+                //TODO: log or do something else
+                throw;
+            }
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/Capture.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/Capture.cs b/lib/cordova-wp8/templates/standalone/Plugins/Capture.cs
new file mode 100644
index 0000000..5e14a16
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/Capture.cs
@@ -0,0 +1,736 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Runtime.Serialization;
+using System.Windows.Media.Imaging;
+using Microsoft.Phone;
+using Microsoft.Phone.Tasks;
+using Microsoft.Xna.Framework.Media;
+using WPCordovaClassLib.Cordova.UI;
+using AudioResult = WPCordovaClassLib.Cordova.UI.AudioCaptureTask.AudioResult;
+using VideoResult = WPCordovaClassLib.Cordova.UI.VideoCaptureTask.VideoResult;
+using System.Windows;
+using System.Diagnostics;
+using Microsoft.Phone.Controls;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Provides access to the audio, image, and video capture capabilities of the device
+    /// </summary>
+    public class Capture : BaseCommand
+    {
+        #region Internal classes (options and resultant objects)
+
+        /// <summary>
+        /// Represents captureImage action options.
+        /// </summary>
+        [DataContract]
+        public class CaptureImageOptions
+        {
+            /// <summary>
+            /// The maximum number of images the device user can capture in a single capture operation. The value must be greater than or equal to 1 (defaults to 1).
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "limit")]
+            public int Limit { get; set; }
+
+            public static CaptureImageOptions Default
+            {
+                get { return new CaptureImageOptions() { Limit = 1 }; }
+            }
+        }
+
+        /// <summary>
+        /// Represents captureAudio action options.
+        /// </summary>
+        [DataContract]
+        public class CaptureAudioOptions
+        {
+            /// <summary>
+            /// The maximum number of audio files the device user can capture in a single capture operation. The value must be greater than or equal to 1 (defaults to 1).
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "limit")]
+            public int Limit { get; set; }
+
+            public static CaptureAudioOptions Default
+            {
+                get { return new CaptureAudioOptions() { Limit = 1 }; }
+            }
+        }
+
+        /// <summary>
+        /// Represents captureVideo action options.
+        /// </summary>
+        [DataContract]
+        public class CaptureVideoOptions
+        {
+            /// <summary>
+            /// The maximum number of video files the device user can capture in a single capture operation. The value must be greater than or equal to 1 (defaults to 1).
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "limit")]
+            public int Limit { get; set; }
+
+            public static CaptureVideoOptions Default
+            {
+                get { return new CaptureVideoOptions() { Limit = 1 }; }
+            }
+        }
+
+        /// <summary>
+        /// Represents getFormatData action options.
+        /// </summary>
+        [DataContract]
+        public class MediaFormatOptions
+        {
+            /// <summary>
+            /// File path
+            /// </summary>
+            [DataMember(IsRequired = true, Name = "fullPath")]
+            public string FullPath { get; set; }
+
+            /// <summary>
+            /// File mime type
+            /// </summary>
+            [DataMember(Name = "type")]
+            public string Type { get; set; }
+
+        }
+
+        /// <summary>
+        /// Stores image info
+        /// </summary>
+        [DataContract]
+        public class MediaFile
+        {
+
+            [DataMember(Name = "name")]
+            public string FileName { get; set; }
+
+            [DataMember(Name = "fullPath")]
+            public string FilePath { get; set; }
+
+            [DataMember(Name = "type")]
+            public string Type { get; set; }
+
+            [DataMember(Name = "lastModifiedDate")]
+            public string LastModifiedDate { get; set; }
+
+            [DataMember(Name = "size")]
+            public long Size { get; set; }
+
+            public MediaFile(string filePath, Picture image)
+            {
+                this.FilePath = filePath;
+                this.FileName = System.IO.Path.GetFileName(this.FilePath);
+                this.Type = MimeTypeMapper.GetMimeType(FileName);
+                this.Size = image.GetImage().Length;
+
+                using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    this.LastModifiedDate = storage.GetLastWriteTime(filePath).DateTime.ToString();
+                }
+
+            }
+
+            public MediaFile(string filePath, Stream stream)
+            {
+                this.FilePath = filePath;
+                this.FileName = System.IO.Path.GetFileName(this.FilePath);
+                this.Type = MimeTypeMapper.GetMimeType(FileName);
+                this.Size = stream.Length;
+
+                using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    this.LastModifiedDate = storage.GetLastWriteTime(filePath).DateTime.ToString();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Stores additional media file data
+        /// </summary>
+        [DataContract]
+        public class MediaFileData
+        {
+            [DataMember(Name = "height")]
+            public int Height { get; set; }
+
+            [DataMember(Name = "width")]
+            public int Width { get; set; }
+
+            [DataMember(Name = "bitrate")]
+            public int Bitrate { get; set; }
+
+            [DataMember(Name = "duration")]
+            public int Duration { get; set; }
+
+            [DataMember(Name = "codecs")]
+            public string Codecs { get; set; }
+
+            public MediaFileData(WriteableBitmap image)
+            {
+                this.Height = image.PixelHeight;
+                this.Width = image.PixelWidth;
+                this.Bitrate = 0;
+                this.Duration = 0;
+                this.Codecs = "";
+            }
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Folder to store captured images
+        /// </summary>
+        private string isoFolder = "CapturedImagesCache";
+
+        /// <summary>
+        /// Capture Image options
+        /// </summary>
+        protected CaptureImageOptions captureImageOptions;
+
+        /// <summary>
+        /// Capture Audio options
+        /// </summary>
+        protected CaptureAudioOptions captureAudioOptions;
+
+        /// <summary>
+        /// Capture Video options
+        /// </summary>
+        protected CaptureVideoOptions captureVideoOptions;
+
+        /// <summary>
+        /// Used to open camera application
+        /// </summary>
+        private CameraCaptureTask cameraTask;
+
+        /// <summary>
+        /// Used for audio recording
+        /// </summary>
+        private AudioCaptureTask audioCaptureTask;
+
+        /// <summary>
+        /// Used for video recording
+        /// </summary>
+        private VideoCaptureTask videoCaptureTask;
+
+        /// <summary>
+        /// Stores information about captured files
+        /// </summary>
+        List<MediaFile> files = new List<MediaFile>();
+
+        /// <summary>
+        /// Launches default camera application to capture image
+        /// </summary>
+        /// <param name="options">may contains limit or mode parameters</param>
+        public void captureImage(string options)
+        {
+            try
+            {
+                try
+                {
+
+                    string args = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                    this.captureImageOptions = String.IsNullOrEmpty(args) ? CaptureImageOptions.Default : JSON.JsonHelper.Deserialize<CaptureImageOptions>(args);
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+
+                cameraTask = new CameraCaptureTask();
+                cameraTask.Completed += this.cameraTask_Completed;
+                cameraTask.Show();
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Launches our own audio recording control to capture audio
+        /// </summary>
+        /// <param name="options">may contains additional parameters</param>
+        public void captureAudio(string options)
+        {
+            try
+            {
+                try
+                {
+                    string args = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                    this.captureAudioOptions = String.IsNullOrEmpty(args) ? CaptureAudioOptions.Default : JSON.JsonHelper.Deserialize<CaptureAudioOptions>(args);
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                audioCaptureTask = new AudioCaptureTask();
+                audioCaptureTask.Completed += audioRecordingTask_Completed;
+                audioCaptureTask.Show();
+
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Launches our own video recording control to capture video
+        /// </summary>
+        /// <param name="options">may contains additional parameters</param>
+        public void captureVideo(string options)
+        {
+            try
+            {
+                try
+                {
+                    string args = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                    this.captureVideoOptions = String.IsNullOrEmpty(args) ? CaptureVideoOptions.Default : JSON.JsonHelper.Deserialize<CaptureVideoOptions>(args);
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                videoCaptureTask = new VideoCaptureTask();
+                videoCaptureTask.Completed += videoRecordingTask_Completed;
+                videoCaptureTask.Show();
+
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Retrieves the format information of the media file.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getFormatData(string options)
+        {
+            try
+            {
+                MediaFormatOptions mediaFormatOptions;
+                try
+                {
+                    mediaFormatOptions = new MediaFormatOptions();
+                    string[] optionStrings = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaFormatOptions.FullPath = optionStrings[0];
+                    mediaFormatOptions.Type = optionStrings[1];
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                if (string.IsNullOrEmpty(mediaFormatOptions.FullPath))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                }
+
+                string mimeType = mediaFormatOptions.Type;
+
+                if (string.IsNullOrEmpty(mimeType))
+                {
+                    mimeType = MimeTypeMapper.GetMimeType(mediaFormatOptions.FullPath);
+                }
+
+                if (mimeType.Equals("image/jpeg"))
+                {
+                    Deployment.Current.Dispatcher.BeginInvoke(() =>
+                    {
+                        WriteableBitmap image = ExtractImageFromLocalStorage(mediaFormatOptions.FullPath);
+
+                        if (image == null)
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "File not found"));
+                            return;
+                        }
+
+                        MediaFileData mediaData = new MediaFileData(image);
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, mediaData));
+                    });
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                }
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+            }
+        }
+
+        /// <summary>
+        /// Opens specified file in media player
+        /// </summary>
+        /// <param name="options">MediaFile to play</param>
+        public void play(string options)
+        {
+            try
+            {
+                MediaFile file;
+
+                try
+                {
+                    file = String.IsNullOrEmpty(options) ? null : JSON.JsonHelper.Deserialize<MediaFile[]>(options)[0];
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                if (file == null || String.IsNullOrEmpty(file.FilePath))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "File path is missing"));
+                    return;
+                }
+
+                // if url starts with '/' media player throws FileNotFound exception
+                Uri fileUri = new Uri(file.FilePath.TrimStart(new char[] { '/', '\\' }), UriKind.Relative);
+
+                MediaPlayerLauncher player = new MediaPlayerLauncher();
+                player.Media = fileUri;
+                player.Location = MediaLocationType.Data;
+                player.Show();
+
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+
+        /// <summary>
+        /// Handles result of capture to save image information 
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e">stores information about current captured image</param>
+        private void cameraTask_Completed(object sender, PhotoResult e)
+        {
+
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        string fileName = System.IO.Path.GetFileName(e.OriginalFileName);
+
+                        // Save image in media library
+                        MediaLibrary library = new MediaLibrary();
+                        Picture image = library.SavePicture(fileName, e.ChosenPhoto);
+
+                        int orient = ImageExifHelper.getImageOrientationFromStream(e.ChosenPhoto);
+                        int newAngle = 0;
+                        switch (orient)
+                        {
+                            case ImageExifOrientation.LandscapeLeft:
+                                newAngle = 90;
+                                break;
+                            case ImageExifOrientation.PortraitUpsideDown:
+                                newAngle = 180;
+                                break;
+                            case ImageExifOrientation.LandscapeRight:
+                                newAngle = 270;
+                                break;
+                            case ImageExifOrientation.Portrait:
+                            default: break; // 0 default already set
+                        }
+
+                        Stream rotImageStream = ImageExifHelper.RotateStream(e.ChosenPhoto, newAngle);
+
+                        // Save image in isolated storage    
+
+                        // we should return stream position back after saving stream to media library
+                        rotImageStream.Seek(0, SeekOrigin.Begin);
+
+                        byte[] imageBytes = new byte[rotImageStream.Length];
+                        rotImageStream.Read(imageBytes, 0, imageBytes.Length);
+                        rotImageStream.Dispose();
+                        string pathLocalStorage = this.SaveImageToLocalStorage(fileName, isoFolder, imageBytes);
+                        imageBytes = null;
+                        // Get image data
+                        MediaFile data = new MediaFile(pathLocalStorage, image);
+
+                        this.files.Add(data);
+
+                        if (files.Count < this.captureImageOptions.Limit)
+                        {
+                            cameraTask.Show();
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                            files.Clear();
+                        }
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error capturing image."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    if (files.Count > 0)
+                    {
+                        // User canceled operation, but some images were made
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Canceled."));
+                    }
+                    break;
+
+                default:
+                    if (files.Count > 0)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Did not complete!"));
+                    }
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Handles result of audio recording tasks 
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e">stores information about current captured audio</param>
+        private void audioRecordingTask_Completed(object sender, AudioResult e)
+        {
+
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        // Get image data
+                        MediaFile data = new MediaFile(e.AudioFileName, e.AudioFile);
+
+                        this.files.Add(data);
+
+                        if (files.Count < this.captureAudioOptions.Limit)
+                        {
+                            audioCaptureTask.Show();
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                            files.Clear();
+                        }
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error capturing audio."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    if (files.Count > 0)
+                    {
+                        // User canceled operation, but some audio clips were made
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Canceled."));
+                    }
+                    break;
+
+                default:
+                    if (files.Count > 0)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Did not complete!"));
+                    }
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Handles result of video recording tasks 
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e">stores information about current captured video</param>
+        private void videoRecordingTask_Completed(object sender, VideoResult e)
+        {
+
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        // Get image data
+                        MediaFile data = new MediaFile(e.VideoFileName, e.VideoFile);
+
+                        this.files.Add(data);
+
+                        if (files.Count < this.captureVideoOptions.Limit)
+                        {
+                            videoCaptureTask.Show();
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                            files.Clear();
+                        }
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error capturing video."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    if (files.Count > 0)
+                    {
+                        // User canceled operation, but some video clips were made
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Canceled."));
+                    }
+                    break;
+
+                default:
+                    if (files.Count > 0)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Did not complete!"));
+                    }
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Extract file from Isolated Storage as WriteableBitmap object
+        /// </summary>
+        /// <param name="filePath"></param>
+        /// <returns></returns>
+        private WriteableBitmap ExtractImageFromLocalStorage(string filePath)
+        {
+            try
+            {
+
+                var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+
+                using (var imageStream = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read))
+                {
+                    var imageSource = PictureDecoder.DecodeJpeg(imageStream);
+                    return imageSource;
+                }
+            }
+            catch (Exception)
+            {
+                return null;
+            }
+        }
+
+
+        /// <summary>
+        /// Saves captured image in isolated storage
+        /// </summary>
+        /// <param name="imageFileName">image file name</param>
+        /// <param name="imageFolder">folder to store images</param>
+        /// <returns>Image path</returns>
+        private string SaveImageToLocalStorage(string imageFileName, string imageFolder, byte[] imageBytes)
+        {
+            if (imageBytes == null)
+            {
+                throw new ArgumentNullException("imageBytes");
+            }
+            try
+            {
+                var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+
+                if (!isoFile.DirectoryExists(imageFolder))
+                {
+                    isoFile.CreateDirectory(imageFolder);
+                }
+                string filePath = System.IO.Path.Combine("/" + imageFolder + "/", imageFileName);
+
+                using (IsolatedStorageFileStream stream = isoFile.CreateFile(filePath))
+                {
+                    stream.Write(imageBytes, 0, imageBytes.Length);
+                }
+
+                return filePath;
+            }
+            catch (Exception)
+            {
+                //TODO: log or do something else
+                throw;
+            }
+        }
+
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/Compass.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/Compass.cs b/lib/cordova-wp8/templates/standalone/Plugins/Compass.cs
new file mode 100644
index 0000000..c9e1c4d
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/Compass.cs
@@ -0,0 +1,362 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using DeviceCompass = Microsoft.Devices.Sensors.Compass;
+using System.Windows.Threading;
+using System.Runtime.Serialization;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Threading;
+using Microsoft.Devices.Sensors;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+
+    public class Compass : BaseCommand
+    {
+        #region Static members
+
+        /// <summary>
+        /// Status of listener
+        /// </summary>
+        private static int currentStatus;
+
+        /// <summary>
+        /// Id for get getCompass method
+        /// </summary>
+        private static string getCompassId = "getCompassId";
+
+        /// <summary>
+        /// Compass
+        /// </summary>
+        private static DeviceCompass compass = new DeviceCompass();
+
+        /// <summary>
+        /// Listeners for callbacks
+        /// </summary>
+        private static Dictionary<string, Compass> watchers = new Dictionary<string, Compass>();
+
+        #endregion
+
+        #region Status codes
+
+        public const int Stopped = 0;
+        public const int Starting = 1;
+        public const int Running = 2;
+        public const int ErrorFailedToStart = 4;
+        public const int Not_Supported = 20;
+
+        /*
+         *   // Capture error codes
+            CompassError.COMPASS_INTERNAL_ERR = 0;
+            CompassError.COMPASS_NOT_SUPPORTED = 20;
+         * */
+
+        #endregion
+
+        #region CompassOptions class
+        /// <summary>
+        /// Represents Accelerometer options.
+        /// </summary>
+        [DataContract]
+        public class CompassOptions
+        {
+            /// <summary>
+            /// How often to retrieve the Acceleration in milliseconds
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "frequency")]
+            public int Frequency { get; set; }
+
+            /// <summary>
+            /// The change in degrees required to initiate a watchHeadingFilter success callback.
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "filter")]
+            public int Filter { get; set; }
+
+            /// <summary>
+            /// Watcher id
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "id")]
+            public string Id { get; set; }
+
+        }
+        #endregion
+
+
+        /// <summary>
+        /// Time the value was last changed
+        /// </summary>
+        //private DateTime lastValueChangedTime;
+
+        /// <summary>
+        /// Accelerometer options
+        /// </summary>
+        private CompassOptions compassOptions;
+
+        //bool isDataValid;
+
+        //bool calibrating = false;
+
+        public Compass()
+        {
+
+        }
+
+        /// <summary>
+        /// Formats current coordinates into JSON format
+        /// </summary>
+        /// <returns>Coordinates in JSON format</returns>
+        private string GetHeadingFormatted(CompassReading reading)
+        {   
+            // NOTE: timestamp is generated on the JS side, to avoid issues with format conversions
+            string result = String.Format("\"magneticHeading\":{0},\"headingAccuracy\":{1},\"trueHeading\":{2}",
+                            reading.MagneticHeading.ToString("0.0", CultureInfo.InvariantCulture),
+                            reading.HeadingAccuracy.ToString("0.0", CultureInfo.InvariantCulture),
+                            reading.TrueHeading.ToString("0.0", CultureInfo.InvariantCulture));
+            return "{" + result + "}";
+        }
+
+        public void getHeading(string options)
+        {
+            if (!DeviceCompass.IsSupported)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "{code:" + Not_Supported + "}"));
+            }
+            else
+            {
+                //if (compass == null)
+                //{
+                //    // Instantiate the compass.
+                //    compass = new DeviceCompass();
+                //    compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(40);
+                //    compass.CurrentValueChanged += new EventHandler<Microsoft.Devices.Sensors.SensorReadingEventArgs<Microsoft.Devices.Sensors.CompassReading>>(compass_CurrentValueChanged);
+                //    compass.Calibrate += new EventHandler<Microsoft.Devices.Sensors.CalibrationEventArgs>(compass_Calibrate);
+                //}
+
+
+                //compass.Start();
+
+            }
+
+            try
+            {
+                if (currentStatus != Running)
+                {
+                    lock (compass)
+                    {
+                        compass.CurrentValueChanged += compass_SingleHeadingValueChanged;
+                        compass.Start();
+                        this.SetStatus(Starting);
+                    }
+
+                    long timeout = 2000;
+                    while ((currentStatus == Starting) && (timeout > 0))
+                    {
+                        timeout = timeout - 100;
+                        Thread.Sleep(100);
+                    }
+
+                    if (currentStatus != Running)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, ErrorFailedToStart));
+                        return;
+                    }
+                }
+                lock (compass)
+                {
+                    compass.CurrentValueChanged -= compass_SingleHeadingValueChanged;
+                    if (watchers.Count < 1)
+                    {
+                        compass.Stop();
+                        this.SetStatus(Stopped);
+                    }
+                }
+            }
+            catch (UnauthorizedAccessException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION, ErrorFailedToStart));
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ErrorFailedToStart));
+            }
+        }
+
+        void compass_SingleHeadingValueChanged(object sender, Microsoft.Devices.Sensors.SensorReadingEventArgs<CompassReading> e)
+        {
+            this.SetStatus(Running);
+            if (compass.IsDataValid)
+            {
+                // trueHeading :: The heading in degrees from 0 - 359.99 at a single moment in time.
+                //  magneticHeading:: The heading relative to the geographic North Pole in degrees 0 - 359.99 at a single moment in time. 
+                //  A negative value indicates that the true heading could not be determined.
+                // headingAccuracy :: The deviation in degrees between the reported heading and the true heading.
+                //rawMagnetometerReading = e.SensorReading.MagnetometerReading;
+
+                //Debug.WriteLine("Compass Result :: " + GetHeadingFormatted(e.SensorReading));
+
+                PluginResult result = new PluginResult(PluginResult.Status.OK, GetHeadingFormatted(e.SensorReading));
+
+                DispatchCommandResult(result);
+            }
+        }
+
+        /// <summary>
+        /// Starts listening for compass sensor
+        /// </summary>
+        /// <returns>status of listener</returns>
+        private int start()
+        {
+            if ((currentStatus == Running) || (currentStatus == Starting))
+            {
+                return currentStatus;
+            }
+            try
+            {
+                lock (compass)
+                {
+                    watchers.Add(getCompassId, this);
+                    compass.CurrentValueChanged += watchers[getCompassId].compass_CurrentValueChanged;
+                    compass.Start();
+                    this.SetStatus(Starting);
+                }
+            }
+            catch (Exception)
+            {
+                this.SetStatus(ErrorFailedToStart);
+            }
+            return currentStatus;
+        }
+
+        public void startWatch(string options)
+        {
+            if (!DeviceCompass.IsSupported)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, Not_Supported));
+            }
+
+            try
+            {
+                compassOptions = JSON.JsonHelper.Deserialize<CompassOptions>(options);
+            }
+            catch (Exception ex)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                return;
+            }
+
+            if (string.IsNullOrEmpty(compassOptions.Id))
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                lock (compass)
+                {
+                    watchers.Add(compassOptions.Id, this);
+                    compass.CurrentValueChanged += watchers[compassOptions.Id].compass_CurrentValueChanged;
+                    compass.Start();
+                    this.SetStatus(Starting);
+                }
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ErrorFailedToStart));
+                return;
+            }
+        }
+
+        public void stopWatch(string options)
+        {
+            try
+            {
+                compassOptions = JSON.JsonHelper.Deserialize<CompassOptions>(options);
+            }
+            catch (Exception ex)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                return;
+            }
+
+            if (string.IsNullOrEmpty(compassOptions.Id))
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            if (currentStatus != Stopped)
+            {
+                lock (compass)
+                {
+                    Compass watcher = watchers[compassOptions.Id];
+                    compass.CurrentValueChanged -= watcher.compass_CurrentValueChanged;
+                    watchers.Remove(compassOptions.Id);
+                    watcher.Dispose();
+                }
+            }
+            this.SetStatus(Stopped);
+
+            this.DispatchCommandResult();
+        }
+
+        void compass_Calibrate(object sender, Microsoft.Devices.Sensors.CalibrationEventArgs e)
+        {
+            //throw new NotImplementedException();
+            // TODO: pass calibration error to JS
+        }
+
+        void compass_CurrentValueChanged(object sender, Microsoft.Devices.Sensors.SensorReadingEventArgs<CompassReading> e)
+        {
+            this.SetStatus(Running);
+            if (compass.IsDataValid)
+            {
+                // trueHeading :: The heading in degrees from 0 - 359.99 at a single moment in time.
+                //  magneticHeading:: The heading relative to the geographic North Pole in degrees 0 - 359.99 at a single moment in time. 
+                //  A negative value indicates that the true heading could not be determined.
+                // headingAccuracy :: The deviation in degrees between the reported heading and the true heading.
+                //rawMagnetometerReading = e.SensorReading.MagnetometerReading;
+
+                //Debug.WriteLine("Compass Result :: " + GetHeadingFormatted(e.SensorReading));
+
+                PluginResult result = new PluginResult(PluginResult.Status.OK, GetHeadingFormatted(e.SensorReading));
+                result.KeepCallback = true;
+
+                DispatchCommandResult(result);
+            }
+        }
+
+        /// <summary>
+        /// Sets current status
+        /// </summary>
+        /// <param name="status">current status</param>
+        private void SetStatus(int status)
+        {
+            currentStatus = status;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/Contacts.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/Contacts.cs b/lib/cordova-wp8/templates/standalone/Plugins/Contacts.cs
new file mode 100644
index 0000000..af78942
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/Contacts.cs
@@ -0,0 +1,664 @@
+/*  
+	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.
+*/
+
+using Microsoft.Phone.Tasks;
+using Microsoft.Phone.UserData;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Windows;
+using DeviceContacts = Microsoft.Phone.UserData.Contacts;
+
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    [DataContract]
+    public class SearchOptions
+    {
+        [DataMember]
+        public string filter { get; set; }
+        [DataMember]
+        public bool multiple { get; set; }
+    }
+
+    [DataContract]
+    public class ContactSearchParams
+    {
+        [DataMember]
+        public string[] fields { get; set; }
+        [DataMember]
+        public SearchOptions options { get; set; }
+    }
+
+    [DataContract]
+    public class JSONContactAddress
+    {
+        [DataMember]
+        public string formatted { get; set; }
+        [DataMember]
+        public string type { get; set; }
+        [DataMember]
+        public string streetAddress { get; set; }
+        [DataMember]
+        public string locality { get; set; }
+        [DataMember]
+        public string region { get; set; }
+        [DataMember]
+        public string postalCode { get; set; }
+        [DataMember]
+        public string country { get; set; }
+        [DataMember]
+        public bool pref { get; set; }
+    }
+
+    [DataContract]
+    public class JSONContactName
+    {
+        [DataMember]
+        public string formatted { get; set; }
+        [DataMember]
+        public string familyName { get; set; }
+        [DataMember]
+        public string givenName { get; set; }
+        [DataMember]
+        public string middleName { get; set; }
+        [DataMember]
+        public string honorificPrefix { get; set; }
+        [DataMember]
+        public string honorificSuffix { get; set; }
+    }
+
+    [DataContract]
+    public class JSONContactField
+    {
+        [DataMember]
+        public string type { get; set; }
+        [DataMember]
+        public string value { get; set; }
+        [DataMember]
+        public bool pref { get; set; }
+    }
+
+    [DataContract]
+    public class JSONContactOrganization
+    {
+        [DataMember]
+        public string type { get; set; }
+        [DataMember]
+        public string name { get; set; }
+        [DataMember]
+        public bool pref { get; set; }
+        [DataMember]
+        public string department { get; set; }
+        [DataMember]
+        public string title { get; set; }
+    }
+
+    [DataContract]
+    public class JSONContact
+    {
+        [DataMember]
+        public string id { get; set; }
+        [DataMember]
+        public string rawId { get; set; }
+        [DataMember]
+        public string displayName { get; set; }
+        [DataMember]
+        public string nickname { get; set; }
+        [DataMember]
+        public string note { get; set; }
+
+        [DataMember]
+        public JSONContactName name { get; set; }
+
+        [DataMember]
+        public JSONContactField[] emails { get; set; }
+
+        [DataMember]
+        public JSONContactField[] phoneNumbers { get; set; }
+
+        [DataMember]
+        public JSONContactField[] ims { get; set; }
+
+        [DataMember]
+        public JSONContactField[] photos { get; set; }
+
+        [DataMember]
+        public JSONContactField[] categories { get; set; }
+
+        [DataMember]
+        public JSONContactField[] urls { get; set; }
+
+        [DataMember]
+        public JSONContactOrganization[] organizations { get; set; }
+
+        [DataMember]
+        public JSONContactAddress[] addresses { get; set; }
+    }
+
+
+    public class Contacts : BaseCommand
+    {
+
+        public const int UNKNOWN_ERROR = 0;
+        public const int INVALID_ARGUMENT_ERROR = 1;
+        public const int TIMEOUT_ERROR = 2;
+        public const int PENDING_OPERATION_ERROR = 3;
+        public const int IO_ERROR = 4;
+        public const int NOT_SUPPORTED_ERROR = 5;
+        public const int PERMISSION_DENIED_ERROR = 20;
+        public const int SYNTAX_ERR = 8;
+
+        public Contacts()
+        {
+
+        }
+
+        // refer here for contact properties we can access: http://msdn.microsoft.com/en-us/library/microsoft.phone.tasks.savecontacttask_members%28v=VS.92%29.aspx
+        public void save(string jsonContact)
+        {
+
+            // jsonContact is actually an array of 1 {contact}
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(jsonContact);
+
+
+            JSONContact contact = JSON.JsonHelper.Deserialize<JSONContact>(args[0]);
+
+            SaveContactTask contactTask = new SaveContactTask();
+
+            if (contact.nickname != null)
+            {
+                contactTask.Nickname = contact.nickname;
+            }
+            if (contact.urls != null && contact.urls.Length > 0)
+            {
+                contactTask.Website = contact.urls[0].value;
+            }
+            if (contact.note != null)
+            {
+                contactTask.Notes = contact.note;
+            }
+
+            #region contact.name
+            if (contact.name != null)
+            {
+                if (contact.name.givenName != null)
+                    contactTask.FirstName = contact.name.givenName;
+                if (contact.name.familyName != null)
+                    contactTask.LastName = contact.name.familyName;
+                if (contact.name.middleName != null)
+                    contactTask.MiddleName = contact.name.middleName;
+                if (contact.name.honorificSuffix != null)
+                    contactTask.Suffix = contact.name.honorificSuffix;
+                if (contact.name.honorificPrefix != null)
+                    contactTask.Title = contact.name.honorificPrefix;
+            }
+            #endregion
+
+            #region contact.org
+            if (contact.organizations != null && contact.organizations.Count() > 0)
+            {
+                contactTask.Company = contact.organizations[0].name;
+                contactTask.JobTitle = contact.organizations[0].title;
+            }
+            #endregion
+
+            #region contact.phoneNumbers
+            if (contact.phoneNumbers != null && contact.phoneNumbers.Length > 0)
+            {
+                foreach (JSONContactField field in contact.phoneNumbers)
+                {
+                    string fieldType = field.type.ToLower();
+                    if (fieldType == "work")
+                    {
+                        contactTask.WorkPhone = field.value;
+                    }
+                    else if (fieldType == "home")
+                    {
+                        contactTask.HomePhone = field.value;
+                    }
+                    else if (fieldType == "mobile")
+                    {
+                        contactTask.MobilePhone = field.value;
+                    }
+                }
+            }
+            #endregion
+
+            #region contact.emails
+
+            if (contact.emails != null && contact.emails.Length > 0)
+            {
+
+                // set up different email types if they are not explicitly defined
+                foreach (string type in new string[] { "personal", "work", "other" })
+                {
+                    foreach (JSONContactField field in contact.emails)
+                    {
+                        if (field != null && String.IsNullOrEmpty(field.type))
+                        {
+                            field.type = type;
+                            break;
+                        }
+                    }
+                }
+
+                foreach (JSONContactField field in contact.emails)
+                {
+                    if (field != null)
+                    {
+                        if (field.type != null && field.type != "other")
+                        {
+                            string fieldType = field.type.ToLower();
+                            if (fieldType == "work")
+                            {
+                                contactTask.WorkEmail = field.value;
+                            }
+                            else if (fieldType == "home" || fieldType == "personal")
+                            {
+                                contactTask.PersonalEmail = field.value;
+                            }
+                        }
+                        else
+                        {
+                            contactTask.OtherEmail = field.value;
+                        }
+                    }
+
+                }
+            }
+            #endregion
+
+            if (contact.note != null && contact.note.Length > 0)
+            {
+                contactTask.Notes = contact.note;
+            }
+
+            #region contact.addresses
+            if (contact.addresses != null && contact.addresses.Length > 0)
+            {
+                foreach (JSONContactAddress address in contact.addresses)
+                {
+                    if (address.type == null)
+                    {
+                        address.type = "home"; // set a default
+                    }
+                    string fieldType = address.type.ToLower();
+                    if (fieldType == "work")
+                    {
+                        contactTask.WorkAddressCity = address.locality;
+                        contactTask.WorkAddressCountry = address.country;
+                        contactTask.WorkAddressState = address.region;
+                        contactTask.WorkAddressStreet = address.streetAddress;
+                        contactTask.WorkAddressZipCode = address.postalCode;
+                    }
+                    else if (fieldType == "home" || fieldType == "personal")
+                    {
+                        contactTask.HomeAddressCity = address.locality;
+                        contactTask.HomeAddressCountry = address.country;
+                        contactTask.HomeAddressState = address.region;
+                        contactTask.HomeAddressStreet = address.streetAddress;
+                        contactTask.HomeAddressZipCode = address.postalCode;
+                    }
+                    else
+                    {
+                        // no other address fields available ...
+                        Debug.WriteLine("Creating contact with unsupported address type :: " + address.type);
+                    }
+                }
+            }
+            #endregion
+
+
+            contactTask.Completed += new EventHandler<SaveContactResult>(ContactSaveTaskCompleted);
+            contactTask.Show();
+        }
+
+        void ContactSaveTaskCompleted(object sender, SaveContactResult e)
+        {
+            SaveContactTask task = sender as SaveContactTask;
+
+            if (e.TaskResult == TaskResult.OK)
+            {
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    DeviceContacts deviceContacts = new DeviceContacts();
+                    deviceContacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(postAdd_SearchCompleted);
+
+                    string displayName = String.Format("{0}{2}{1}", task.FirstName, task.LastName, String.IsNullOrEmpty(task.FirstName) ? "" : " ");
+
+                    deviceContacts.SearchAsync(displayName, FilterKind.DisplayName, task);
+                });
+
+
+            }
+            else if (e.TaskResult == TaskResult.Cancel)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Operation cancelled."));
+            }
+        }
+
+        void postAdd_SearchCompleted(object sender, ContactsSearchEventArgs e)
+        {
+            if (e.Results.Count() > 0)
+            {
+                List<Contact> foundContacts = new List<Contact>();
+
+                int n = (from Contact contact in e.Results select contact.GetHashCode()).Max();
+                Contact newContact = (from Contact contact in e.Results
+                                      where contact.GetHashCode() == n
+                                      select contact).First();
+
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, FormatJSONContact(newContact, null)));
+            }
+            else
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
+            }
+        }
+
+
+
+        public void remove(string id)
+        {
+            // note id is wrapped in [] and always has exactly one string ...
+            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "{\"code\":" + NOT_SUPPORTED_ERROR + "}"));
+        }
+
+        public void search(string searchCriteria)
+        {
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(searchCriteria);
+
+            ContactSearchParams searchParams = new ContactSearchParams();
+            try
+            {
+                searchParams.fields = JSON.JsonHelper.Deserialize<string[]>(args[0]);
+                searchParams.options = JSON.JsonHelper.Deserialize<SearchOptions>(args[1]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_ARGUMENT_ERROR));
+                return;
+            }
+
+            if (searchParams.options == null)
+            {
+                searchParams.options = new SearchOptions();
+                searchParams.options.filter = "";
+                searchParams.options.multiple = true;
+            }
+
+            DeviceContacts deviceContacts = new DeviceContacts();
+            deviceContacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(contacts_SearchCompleted);
+
+            // default is to search all fields
+            FilterKind filterKind = FilterKind.None;
+            // if only one field is specified, we will try the 3 available DeviceContact search filters
+            if (searchParams.fields.Count() == 1)
+            {
+                if (searchParams.fields.Contains("name"))
+                {
+                    filterKind = FilterKind.DisplayName;
+                }
+                else if (searchParams.fields.Contains("emails"))
+                {
+                    filterKind = FilterKind.EmailAddress;
+                }
+                else if (searchParams.fields.Contains("phoneNumbers"))
+                {
+                    filterKind = FilterKind.PhoneNumber;
+                }
+            }
+
+            try
+            {
+
+                deviceContacts.SearchAsync(searchParams.options.filter, filterKind, searchParams);
+            }
+            catch (Exception ex)
+            {
+                Debug.WriteLine("search contacts exception :: " + ex.Message);
+            }
+        }
+
+        private void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
+        {
+            ContactSearchParams searchParams = (ContactSearchParams)e.State;
+
+            List<Contact> foundContacts = null;
+
+            // if we have multiple search fields
+            if (searchParams.options.filter.Length > 0 && searchParams.fields.Count() > 1)
+            {
+                foundContacts = new List<Contact>();
+                if (searchParams.fields.Contains("emails"))
+                {
+                    foundContacts.AddRange(from Contact con in e.Results
+                                           from ContactEmailAddress a in con.EmailAddresses
+                                           where a.EmailAddress.Contains(searchParams.options.filter)
+                                           select con);
+                }
+                if (searchParams.fields.Contains("displayName"))
+                {
+                    foundContacts.AddRange(from Contact con in e.Results
+                                           where con.DisplayName.Contains(searchParams.options.filter)
+                                           select con);
+                }
+                if (searchParams.fields.Contains("name"))
+                {
+                    foundContacts.AddRange(from Contact con in e.Results
+                                           where con.CompleteName != null && con.CompleteName.ToString().Contains(searchParams.options.filter)
+                                           select con);
+                }
+                if (searchParams.fields.Contains("phoneNumbers"))
+                {
+                    foundContacts.AddRange(from Contact con in e.Results
+                                           from ContactPhoneNumber a in con.PhoneNumbers
+                                           where a.PhoneNumber.Contains(searchParams.options.filter)
+                                           select con);
+                }
+                if (searchParams.fields.Contains("urls"))
+                {
+                    foundContacts.AddRange(from Contact con in e.Results
+                                           from string a in con.Websites
+                                           where a.Contains(searchParams.options.filter)
+                                           select con);
+                }
+            }
+            else
+            {
+                foundContacts = new List<Contact>(e.Results);
+            }
+
+            //List<string> contactList = new List<string>();
+
+            string strResult = "";
+
+            IEnumerable<Contact> distinctContacts = foundContacts.Distinct();
+
+            foreach (Contact contact in distinctContacts)
+            {
+                strResult += FormatJSONContact(contact, null) + ",";
+                //contactList.Add(FormatJSONContact(contact, null));
+                if (!searchParams.options.multiple)
+                {
+                    break; // just return the first item
+                }
+            }
+            PluginResult result = new PluginResult(PluginResult.Status.OK);
+            result.Message = "[" + strResult.TrimEnd(',') + "]";
+            DispatchCommandResult(result);
+
+        }
+
+        private string FormatJSONPhoneNumbers(Contact con)
+        {
+            string retVal = "";
+            string contactFieldFormat = "\"type\":\"{0}\",\"value\":\"{1}\",\"pref\":\"false\"";
+            foreach (ContactPhoneNumber number in con.PhoneNumbers)
+            {
+
+                string contactField = string.Format(contactFieldFormat,
+                                                    number.Kind.ToString(),
+                                                    number.PhoneNumber);
+
+                retVal += "{" + contactField + "},";
+            }
+            return retVal.TrimEnd(',');
+        }
+
+        private string FormatJSONEmails(Contact con)
+        {
+            string retVal = "";
+            string contactFieldFormat = "\"type\":\"{0}\",\"value\":\"{1}\",\"pref\":\"false\"";
+            foreach (ContactEmailAddress address in con.EmailAddresses)
+            {
+                string contactField = string.Format(contactFieldFormat,
+                                                    address.Kind.ToString(),
+                                                    address.EmailAddress);
+
+                retVal += "{" + contactField + "},";
+            }
+            return retVal.TrimEnd(',');
+        }
+
+        private string getFormattedJSONAddress(ContactAddress address, bool isPrefered)
+        {
+
+            string addressFormatString = "\"pref\":{0}," + // bool
+                          "\"type\":\"{1}\"," +
+                          "\"formatted\":\"{2}\"," +
+                          "\"streetAddress\":\"{3}\"," +
+                          "\"locality\":\"{4}\"," +
+                          "\"region\":\"{5}\"," +
+                          "\"postalCode\":\"{6}\"," +
+                          "\"country\":\"{7}\"";
+
+            string formattedAddress = address.PhysicalAddress.AddressLine1 + " "
+                                    + address.PhysicalAddress.AddressLine2 + " "
+                                    + address.PhysicalAddress.City + " "
+                                    + address.PhysicalAddress.StateProvince + " "
+                                    + address.PhysicalAddress.CountryRegion + " "
+                                    + address.PhysicalAddress.PostalCode;
+
+            string jsonAddress = string.Format(addressFormatString,
+                                               isPrefered ? "\"true\"" : "\"false\"",
+                                               address.Kind.ToString(),
+                                               formattedAddress,
+                                               address.PhysicalAddress.AddressLine1 + " " + address.PhysicalAddress.AddressLine2,
+                                               address.PhysicalAddress.City,
+                                               address.PhysicalAddress.StateProvince,
+                                               address.PhysicalAddress.PostalCode,
+                                               address.PhysicalAddress.CountryRegion);
+
+            //Debug.WriteLine("getFormattedJSONAddress returning :: " + jsonAddress);
+
+            return "{" + jsonAddress + "}";
+        }
+
+        private string FormatJSONAddresses(Contact con)
+        {
+            string retVal = "";
+            foreach (ContactAddress address in con.Addresses)
+            {
+                retVal += this.getFormattedJSONAddress(address, false) + ",";
+            }
+
+            //Debug.WriteLine("FormatJSONAddresses returning :: " + retVal);
+            return retVal.TrimEnd(',');
+        }
+
+        private string FormatJSONWebsites(Contact con)
+        {
+            string retVal = "";
+            foreach (string website in con.Websites)
+            {
+                retVal += "\"" + website + "\",";
+            }
+            return retVal.TrimEnd(',');
+        }
+
+        /*
+         *  formatted: The complete name of the contact. (DOMString)
+            familyName: The contacts family name. (DOMString)
+            givenName: The contacts given name. (DOMString)
+            middleName: The contacts middle name. (DOMString)
+            honorificPrefix: The contacts prefix (example Mr. or Dr.) (DOMString)
+            honorificSuffix: The contacts suffix (example Esq.). (DOMString)
+         */
+        private string FormatJSONName(Contact con)
+        {
+            string retVal = "";
+            string formatStr = "\"formatted\":\"{0}\"," +
+                                "\"familyName\":\"{1}\"," +
+                                "\"givenName\":\"{2}\"," +
+                                "\"middleName\":\"{3}\"," +
+                                "\"honorificPrefix\":\"{4}\"," +
+                                "\"honorificSuffix\":\"{5}\"";
+
+            if (con.CompleteName != null)
+            {
+                retVal = string.Format(formatStr,
+                                   con.CompleteName.FirstName + " " + con.CompleteName.LastName, // TODO: does this need suffix? middlename?
+                                   con.CompleteName.LastName,
+                                   con.CompleteName.FirstName,
+                                   con.CompleteName.MiddleName,
+                                   con.CompleteName.Title,
+                                   con.CompleteName.Suffix);
+            }
+            else
+            {
+                retVal = string.Format(formatStr,"","","","","","");
+            }
+
+            return "{" + retVal + "}";
+        }
+
+        private string FormatJSONContact(Contact con, string[] fields)
+        {
+
+            string contactFormatStr = "\"id\":\"{0}\"," +
+                                      "\"displayName\":\"{1}\"," +
+                                      "\"nickname\":\"{2}\"," +
+                                      "\"phoneNumbers\":[{3}]," +
+                                      "\"emails\":[{4}]," +
+                                      "\"addresses\":[{5}]," +
+                                      "\"urls\":[{6}]," +
+                                      "\"name\":{7}," +
+                                      "\"note\":\"{8}\"," +
+                                      "\"birthday\":\"{9}\"";
+
+
+            string jsonContact = String.Format(contactFormatStr,
+                                               con.GetHashCode(),
+                                               con.DisplayName,
+                                               con.CompleteName != null ? con.CompleteName.Nickname : "",
+                                               FormatJSONPhoneNumbers(con),
+                                               FormatJSONEmails(con),
+                                               FormatJSONAddresses(con),
+                                               FormatJSONWebsites(con),
+                                               FormatJSONName(con),
+                                               con.Notes.FirstOrDefault(),
+                                               con.Birthdays.FirstOrDefault());
+
+            //Debug.WriteLine("jsonContact = " + jsonContact);
+            // JSON requires new line characters be escaped
+            return "{" + jsonContact.Replace("\n", "\\n") + "}";
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/DebugConsole.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/DebugConsole.cs b/lib/cordova-wp8/templates/standalone/Plugins/DebugConsole.cs
new file mode 100644
index 0000000..fa9863a
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/DebugConsole.cs
@@ -0,0 +1,49 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+
+    public class DebugConsole : BaseCommand
+    {
+        // warn, error
+        public void log(string msg)
+        {
+            Debug.WriteLine("Log:" + msg);
+        }
+
+        public void error(string msg)
+        {
+            Debug.WriteLine("Error:" + msg);
+        }
+
+        public void warn(string msg)
+        {
+            Debug.WriteLine("Warn:" + msg);
+        }
+
+    }
+}


[31/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/File.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/File.cs b/lib/cordova-wp7/templates/standalone/Plugins/File.cs
new file mode 100644
index 0000000..cde7a1c
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/File.cs
@@ -0,0 +1,1676 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Runtime.Serialization;
+using System.Security;
+using System.Text;
+using System.Windows;
+using System.Windows.Resources;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Provides access to isolated storage
+    /// </summary>
+    public class File : BaseCommand
+    {
+        // Error codes
+        public const int NOT_FOUND_ERR = 1;
+        public const int SECURITY_ERR = 2;
+        public const int ABORT_ERR = 3;
+        public const int NOT_READABLE_ERR = 4;
+        public const int ENCODING_ERR = 5;
+        public const int NO_MODIFICATION_ALLOWED_ERR = 6;
+        public const int INVALID_STATE_ERR = 7;
+        public const int SYNTAX_ERR = 8;
+        public const int INVALID_MODIFICATION_ERR = 9;
+        public const int QUOTA_EXCEEDED_ERR = 10;
+        public const int TYPE_MISMATCH_ERR = 11;
+        public const int PATH_EXISTS_ERR = 12;
+
+        // File system options
+        public const int TEMPORARY = 0;
+        public const int PERSISTENT = 1;
+        public const int RESOURCE = 2;
+        public const int APPLICATION = 3;
+
+        /// <summary>
+        /// Temporary directory name
+        /// </summary>
+        private readonly string TMP_DIRECTORY_NAME = "tmp";
+
+        /// <summary>
+        /// Represents error code for callback
+        /// </summary>
+        [DataContract]
+        public class ErrorCode
+        {
+            /// <summary>
+            /// Error code
+            /// </summary>
+            [DataMember(IsRequired = true, Name = "code")]
+            public int Code { get; set; }
+
+            /// <summary>
+            /// Creates ErrorCode object
+            /// </summary>
+            public ErrorCode(int code)
+            {
+                this.Code = code;
+            }
+        }
+
+        /// <summary>
+        /// Represents File action options.
+        /// </summary>
+        [DataContract]
+        public class FileOptions
+        {
+            /// <summary>
+            /// File path
+            /// </summary>
+            /// 
+            private string _fileName;
+            [DataMember(Name = "fileName")]
+            public string FilePath
+            {
+                get
+                {
+                    return this._fileName;
+                }
+
+                set
+                {
+                    int index = value.IndexOfAny(new char[] { '#', '?' });
+                    this._fileName = index > -1 ? value.Substring(0, index) : value;
+                }
+            }
+
+            /// <summary>
+            /// Full entryPath
+            /// </summary>
+            [DataMember(Name = "fullPath")]
+            public string FullPath { get; set; }
+
+            /// <summary>
+            /// Directory name
+            /// </summary>
+            [DataMember(Name = "dirName")]
+            public string DirectoryName { get; set; }
+
+            /// <summary>
+            /// Path to create file/directory
+            /// </summary>
+            [DataMember(Name = "path")]
+            public string Path { get; set; }
+
+            /// <summary>
+            /// The encoding to use to encode the file's content. Default is UTF8.
+            /// </summary>
+            [DataMember(Name = "encoding")]
+            public string Encoding { get; set; }
+
+            /// <summary>
+            /// Uri to get file
+            /// </summary>
+            /// 
+            private string _uri;
+            [DataMember(Name = "uri")]
+            public string Uri
+            {
+                get
+                {
+                    return this._uri;
+                }
+
+                set
+                {
+                    int index = value.IndexOfAny(new char[] { '#', '?' });
+                    this._uri = index > -1 ? value.Substring(0, index) : value;
+                }
+            }
+
+            /// <summary>
+            /// Size to truncate file
+            /// </summary>
+            [DataMember(Name = "size")]
+            public long Size { get; set; }
+
+            /// <summary>
+            /// Data to write in file
+            /// </summary>
+            [DataMember(Name = "data")]
+            public string Data { get; set; }
+
+            /// <summary>
+            /// Position the writing starts with
+            /// </summary>
+            [DataMember(Name = "position")]
+            public int Position { get; set; }
+
+            /// <summary>
+            /// Type of file system requested
+            /// </summary>
+            [DataMember(Name = "type")]
+            public int FileSystemType { get; set; }
+
+            /// <summary>
+            /// New file/directory name
+            /// </summary>
+            [DataMember(Name = "newName")]
+            public string NewName { get; set; }
+
+            /// <summary>
+            /// Destination directory to copy/move file/directory
+            /// </summary>
+            [DataMember(Name = "parent")]
+            public string Parent { get; set; }
+
+            /// <summary>
+            /// Options for getFile/getDirectory methods
+            /// </summary>
+            [DataMember(Name = "options")]
+            public CreatingOptions CreatingOpt { get; set; }
+
+            /// <summary>
+            /// Creates options object with default parameters
+            /// </summary>
+            public FileOptions()
+            {
+                this.SetDefaultValues(new StreamingContext());
+            }
+
+            /// <summary>
+            /// Initializes default values for class fields.
+            /// Implemented in separate method because default constructor is not invoked during deserialization.
+            /// </summary>
+            /// <param name="context"></param>
+            [OnDeserializing()]
+            public void SetDefaultValues(StreamingContext context)
+            {
+                this.Encoding = "UTF-8";
+                this.FilePath = "";
+                this.FileSystemType = -1; 
+            }
+        }
+
+        /// <summary>
+        /// Stores image info
+        /// </summary>
+        [DataContract]
+        public class FileMetadata
+        {
+            [DataMember(Name = "fileName")]
+            public string FileName { get; set; }
+
+            [DataMember(Name = "fullPath")]
+            public string FullPath { get; set; }
+
+            [DataMember(Name = "type")]
+            public string Type { get; set; }
+
+            [DataMember(Name = "lastModifiedDate")]
+            public string LastModifiedDate { get; set; }
+
+            [DataMember(Name = "size")]
+            public long Size { get; set; }
+
+            public FileMetadata(string filePath)
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    if (string.IsNullOrEmpty(filePath))
+                    {
+                        throw new FileNotFoundException("File doesn't exist");
+                    }
+                    else if (!isoFile.FileExists(filePath))
+                    {
+                        // attempt to get it from the resources
+                        if (filePath.IndexOf("www") == 0)
+                        {
+                            Uri fileUri = new Uri(filePath, UriKind.Relative);
+                            StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri);
+                            if (streamInfo != null)
+                            {
+                                this.Size = streamInfo.Stream.Length;
+                                this.FileName = filePath.Substring(filePath.LastIndexOf("/") + 1);
+                                this.FullPath = filePath;
+                            }
+                        }
+                        else
+                        {
+                            throw new FileNotFoundException("File doesn't exist");
+                        }
+                    }
+                    else
+                    {
+                        //TODO get file size the other way if possible                
+                        using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.Read, isoFile))
+                        {
+                            this.Size = stream.Length;
+                        }
+                        this.FullPath = filePath;
+                        this.FileName = System.IO.Path.GetFileName(filePath);
+                        this.LastModifiedDate = isoFile.GetLastWriteTime(filePath).DateTime.ToString();
+                    }
+                    this.Type = MimeTypeMapper.GetMimeType(this.FileName);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Represents file or directory modification metadata
+        /// </summary>
+        [DataContract]
+        public class ModificationMetadata
+        {
+            /// <summary>
+            /// Modification time
+            /// </summary>
+            [DataMember]
+            public string modificationTime { get; set; }
+        }
+
+        /// <summary>
+        /// Represents file or directory entry
+        /// </summary>
+        [DataContract]
+        public class FileEntry
+        {
+
+            /// <summary>
+            /// File type
+            /// </summary>
+            [DataMember(Name = "isFile")]
+            public bool IsFile { get; set; }
+
+            /// <summary>
+            /// Directory type
+            /// </summary>
+            [DataMember(Name = "isDirectory")]
+            public bool IsDirectory { get; set; }
+
+            /// <summary>
+            /// File/directory name
+            /// </summary>
+            [DataMember(Name = "name")]
+            public string Name { get; set; }
+
+            /// <summary>
+            /// Full path to file/directory
+            /// </summary>
+            [DataMember(Name = "fullPath")]
+            public string FullPath { get; set; }
+
+            public bool IsResource { get; set; }
+
+            public static FileEntry GetEntry(string filePath, bool bIsRes=false)
+            {
+                FileEntry entry = null;
+                try
+                {
+                    entry = new FileEntry(filePath, bIsRes);
+
+                }
+                catch (Exception ex)
+                {
+                    Debug.WriteLine("Exception in GetEntry for filePath :: " + filePath + " " + ex.Message);
+                }
+                return entry;
+            }
+
+            /// <summary>
+            /// Creates object and sets necessary properties
+            /// </summary>
+            /// <param name="filePath"></param>
+            public FileEntry(string filePath, bool bIsRes = false)
+            {
+                if (string.IsNullOrEmpty(filePath))
+                {
+                    throw new ArgumentException();
+                }
+
+                if(filePath.Contains(" ")) 
+                {
+                    Debug.WriteLine("FilePath with spaces :: " +  filePath);
+                }
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    IsResource = bIsRes;
+                    IsFile = isoFile.FileExists(filePath);
+                    IsDirectory = isoFile.DirectoryExists(filePath);
+                    if (IsFile)
+                    {
+                        this.Name = Path.GetFileName(filePath);
+                    }
+                    else if (IsDirectory)
+                    {
+                        this.Name = this.GetDirectoryName(filePath);
+                        if (string.IsNullOrEmpty(Name))
+                        {
+                            this.Name = "/";
+                        }
+                    }
+                    else
+                    {
+                        if (IsResource)
+                        {
+                            this.Name = Path.GetFileName(filePath);
+                        }
+                        else
+                        {
+                            throw new FileNotFoundException();
+                        }
+                    }
+
+                    try
+                    {
+                        this.FullPath = filePath.Replace('\\', '/'); // new Uri(filePath).LocalPath;
+                    }
+                    catch (Exception)
+                    {
+                        this.FullPath = filePath;
+                    }
+                }
+            }
+
+            /// <summary>
+            /// Extracts directory name from path string
+            /// Path should refer to a directory, for example \foo\ or /foo.
+            /// </summary>
+            /// <param name="path"></param>
+            /// <returns></returns>
+            private string GetDirectoryName(string path)
+            {
+                if (String.IsNullOrEmpty(path))
+                {
+                    return path;
+                }
+
+                string[] split = path.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
+                if (split.Length < 1)
+                {
+                    return null;
+                }
+                else
+                {
+                    return split[split.Length - 1];
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Represents info about requested file system
+        /// </summary>
+        [DataContract]
+        public class FileSystemInfo
+        {
+            /// <summary>
+            /// file system type
+            /// </summary>
+            [DataMember(Name = "name", IsRequired = true)]
+            public string Name { get; set; }
+
+            /// <summary>
+            /// Root directory entry
+            /// </summary>
+            [DataMember(Name = "root", EmitDefaultValue = false)]
+            public FileEntry Root { get; set; }
+
+            /// <summary>
+            /// Creates class instance
+            /// </summary>
+            /// <param name="name"></param>
+            /// <param name="rootEntry"> Root directory</param>
+            public FileSystemInfo(string name, FileEntry rootEntry = null)
+            {
+                Name = name;
+                Root = rootEntry;
+            }
+        }
+
+        [DataContract]
+        public class CreatingOptions
+        {
+            /// <summary>
+            /// Create file/directory if is doesn't exist
+            /// </summary>
+            [DataMember(Name = "create")]
+            public bool Create { get; set; }
+
+            /// <summary>
+            /// Generate an exception if create=true and file/directory already exists
+            /// </summary>
+            [DataMember(Name = "exclusive")]
+            public bool Exclusive { get; set; }
+
+
+        }
+
+        // returns null value if it fails.
+        private string[] getOptionStrings(string options)
+        {
+            string[] optStings = null;
+            try
+            {
+                optStings = JSON.JsonHelper.Deserialize<string[]>(options);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), CurrentCommandCallbackId);
+            }
+            return optStings;
+        }
+
+        /// <summary>
+        /// Gets amount of free space available for Isolated Storage
+        /// </summary>
+        /// <param name="options">No options is needed for this method</param>
+        public void getFreeDiskSpace(string options)
+        {
+            string callbackId = getOptionStrings(options)[0];
+
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isoFile.AvailableFreeSpace), callbackId);
+                }
+            }
+            catch (IsolatedStorageException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Check if file exists
+        /// </summary>
+        /// <param name="options">File path</param>
+        public void testFileExists(string options)
+        {
+            IsDirectoryOrFileExist(options, false);
+        }
+
+        /// <summary>
+        /// Check if directory exists
+        /// </summary>
+        /// <param name="options">directory name</param>
+        public void testDirectoryExists(string options)
+        {
+            IsDirectoryOrFileExist(options, true);
+        }
+
+        /// <summary>
+        /// Check if file or directory exist
+        /// </summary>
+        /// <param name="options">File path/Directory name</param>
+        /// <param name="isDirectory">Flag to recognize what we should check</param>
+        public void IsDirectoryOrFileExist(string options, bool isDirectory)
+        {
+            string[] args = getOptionStrings(options);
+            string callbackId = args[1];
+            FileOptions fileOptions = JSON.JsonHelper.Deserialize<FileOptions>(args[0]);
+            string filePath = args[0];
+
+            if (fileOptions == null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId);
+            }
+
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    bool isExist;
+                    if (isDirectory)
+                    {
+                        isExist = isoFile.DirectoryExists(fileOptions.DirectoryName);
+                    }
+                    else
+                    {
+                        isExist = isoFile.FileExists(fileOptions.FilePath);
+                    }
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isExist), callbackId);
+                }
+            }
+            catch (IsolatedStorageException) // default handler throws INVALID_MODIFICATION_ERR
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                }
+            }
+
+        }
+
+        public void readAsDataURL(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+            string filePath = optStrings[0];
+            int startPos = int.Parse(optStrings[1]);
+            int endPos = int.Parse(optStrings[2]);
+            string callbackId = optStrings[3];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    string base64URL = null;
+
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (!isoFile.FileExists(filePath))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                            return;
+                        }
+                        string mimeType = MimeTypeMapper.GetMimeType(filePath);
+
+                        using (IsolatedStorageFileStream stream = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read))
+                        {
+                            string base64String = GetFileContent(stream);
+                            base64URL = "data:" + mimeType + ";base64," + base64String;
+                        }
+                    }
+
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, base64URL), callbackId);
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                    }
+                }
+            }
+        }
+
+        public void readAsArrayBuffer(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+            string filePath = optStrings[0];
+            int startPos = int.Parse(optStrings[1]);
+            int endPos = int.Parse(optStrings[2]);
+            string callbackId = optStrings[3];
+            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR), callbackId);
+        }
+
+        public void readAsBinaryString(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+            string filePath = optStrings[0];
+            int startPos = int.Parse(optStrings[1]);
+            int endPos = int.Parse(optStrings[2]);
+            string callbackId = optStrings[3];
+            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR), callbackId);
+        }
+
+        public void readAsText(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+            string filePath = optStrings[0];
+            string encStr = optStrings[1];
+            int startPos = int.Parse(optStrings[2]);
+            int endPos = int.Parse(optStrings[3]);
+            string callbackId = optStrings[4];
+
+            try
+            {
+                string text = "";
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    if (!isoFile.FileExists(filePath))
+                    {
+                        readResourceAsText(options);
+                        return;
+                    }
+                    Encoding encoding = Encoding.GetEncoding(encStr);
+
+                    using (TextReader reader = new StreamReader(isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read), encoding))
+                    {
+                        text = reader.ReadToEnd();
+                        if (startPos < 0)
+                        {
+                            startPos = Math.Max(text.Length + startPos, 0);
+                        }
+                        else if (startPos > 0)
+                        {
+                            startPos = Math.Min(text.Length, startPos);
+                        }
+
+                        if (endPos > 0)
+                        {
+                            endPos = Math.Min(text.Length, endPos);
+                        }
+                        else if (endPos < 0)
+                        {
+                            endPos = Math.Max(endPos + text.Length, 0);
+                        }
+                        
+                        
+                        text = text.Substring(startPos, endPos - startPos);
+                        
+                    }
+                }
+
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, text), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex, callbackId))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Reads application resource as a text
+        /// </summary>
+        /// <param name="options">Path to a resource</param>
+        public void readResourceAsText(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+            string pathToResource = optStrings[0];
+            string encStr = optStrings[1];
+            int start = int.Parse(optStrings[2]);
+            int endMarker = int.Parse(optStrings[3]);
+            string callbackId = optStrings[4];
+
+            try
+            {
+                if (pathToResource.StartsWith("/"))
+                {
+                    pathToResource = pathToResource.Remove(0, 1);
+                }
+                
+                var resource = Application.GetResourceStream(new Uri(pathToResource, UriKind.Relative));
+                
+                if (resource == null)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    return;
+                }
+
+                string text;
+                StreamReader streamReader = new StreamReader(resource.Stream);
+                text = streamReader.ReadToEnd();
+
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, text), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex, callbackId))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+            }
+        }
+
+        public void truncate(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+
+            string filePath = optStrings[0];
+            int size = int.Parse(optStrings[1]);
+            string callbackId = optStrings[2];
+
+            try
+            {
+                long streamLength = 0;
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    if (!isoFile.FileExists(filePath))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                        return;
+                    }
+
+                    using (FileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.ReadWrite, isoFile))
+                    {
+                        if (0 <= size && size <= stream.Length)
+                        {
+                            stream.SetLength(size);
+                        }
+                        streamLength = stream.Length;
+                    }
+                }
+
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, streamLength), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex, callbackId))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+            }
+        }
+
+        //write:["filePath","data","position"],
+        public void write(string options)
+        {
+            // TODO: try/catch
+            string[] optStrings = getOptionStrings(options);
+
+            string filePath = optStrings[0];
+            string data = optStrings[1];
+            int position = int.Parse(optStrings[2]);
+            string callbackId = optStrings[3];
+
+            try
+            {
+                if (string.IsNullOrEmpty(data))
+                {
+                    Debug.WriteLine("Expected some data to be send in the write command to {0}", filePath);
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId);
+                    return;
+                }
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    // create the file if not exists
+                    if (!isoFile.FileExists(filePath))
+                    {
+                        var file = isoFile.CreateFile(filePath);
+                        file.Close();
+                    }
+
+                    using (FileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.ReadWrite, isoFile))
+                    {
+                        if (0 <= position && position <= stream.Length)
+                        {
+                            stream.SetLength(position);
+                        }
+                        using (BinaryWriter writer = new BinaryWriter(stream))
+                        {
+                            writer.Seek(0, SeekOrigin.End);
+                            writer.Write(data.ToCharArray());
+                        }
+                    }
+                }
+
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, data.Length), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex, callbackId))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Look up metadata about this entry.
+        /// </summary>
+        /// <param name="options">filePath to entry</param>   
+        public void getMetadata(string options)
+        {
+            string[] optStings = getOptionStrings(options);
+            string filePath = optStings[0];
+            string callbackId = optStings[1];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (isoFile.FileExists(filePath))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK,
+                                new ModificationMetadata() { modificationTime = isoFile.GetLastWriteTime(filePath).DateTime.ToString() }), callbackId);
+                        }
+                        else if (isoFile.DirectoryExists(filePath))
+                        {
+                            string modTime = isoFile.GetLastWriteTime(filePath).DateTime.ToString();
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new ModificationMetadata() { modificationTime = modTime }), callbackId);
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                        }
+
+                    }
+                }
+                catch (IsolatedStorageException)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                    }
+                }
+            }
+
+        }
+
+
+        /// <summary>
+        /// Returns a File that represents the current state of the file that this FileEntry represents.
+        /// </summary>
+        /// <param name="filePath">filePath to entry</param>
+        /// <returns></returns>
+        public void getFileMetadata(string options)
+        {
+            string[] optStings = getOptionStrings(options);
+            string filePath = optStings[0];
+            string callbackId = optStings[1];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    FileMetadata metaData = new FileMetadata(filePath);
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, metaData), callbackId);
+                }
+                catch (IsolatedStorageException)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Look up the parent DirectoryEntry containing this Entry. 
+        /// If this Entry is the root of IsolatedStorage, its parent is itself.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getParent(string options)
+        {
+            string[] optStings = getOptionStrings(options);
+            string filePath = optStings[0];
+            string callbackId = optStings[1];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    if (string.IsNullOrEmpty(filePath))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId);
+                        return;
+                    }
+
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        FileEntry entry;
+
+                        if (isoFile.FileExists(filePath) || isoFile.DirectoryExists(filePath))
+                        {
+                           
+                             
+                            string path = this.GetParentDirectory(filePath);
+                            entry = FileEntry.GetEntry(path);
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry),callbackId);
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
+                        }
+
+                    }
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
+                    }
+                }
+            }
+        }
+
+        public void remove(string options)
+        {
+            string[] args = getOptionStrings(options);
+            string filePath = args[0];
+            string callbackId = args[1];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    if (filePath == "/" || filePath == "" || filePath == @"\")
+                    {
+                        throw new Exception("Cannot delete root file system") ;
+                    }
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (isoFile.FileExists(filePath))
+                        {
+                            isoFile.DeleteFile(filePath);
+                        }
+                        else
+                        {
+                            if (isoFile.DirectoryExists(filePath))
+                            {
+                                isoFile.DeleteDirectory(filePath);
+                            }
+                            else
+                            {
+                                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
+                                return;
+                            }
+                        }
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK),callbackId);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
+                    }
+                }
+            }
+        }
+
+        public void removeRecursively(string options)
+        {
+            string[] args = getOptionStrings(options);
+            string filePath = args[0];
+            string callbackId = args[1];
+
+            if (filePath != null)
+            {
+                if (string.IsNullOrEmpty(filePath))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId);
+                }
+                else
+                {
+                    if (removeDirRecursively(filePath, callbackId))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK), callbackId);
+                    }
+                }
+            }
+        }
+
+        public void readEntries(string options)
+        {
+            string[] args = getOptionStrings(options);
+            string filePath = args[0];
+            string callbackId = args[1];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    if (string.IsNullOrEmpty(filePath))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId);
+                        return;
+                    }
+
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (isoFile.DirectoryExists(filePath))
+                        {
+                            string path = File.AddSlashToDirectory(filePath);
+                            List<FileEntry> entries = new List<FileEntry>();
+                            string[] files = isoFile.GetFileNames(path + "*");
+                            string[] dirs = isoFile.GetDirectoryNames(path + "*");
+                            foreach (string file in files)
+                            {
+                                entries.Add(FileEntry.GetEntry(path + file));
+                            }
+                            foreach (string dir in dirs)
+                            {
+                                entries.Add(FileEntry.GetEntry(path + dir + "/"));
+                            }
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entries),callbackId);
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
+                        }
+                    }
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
+                    }
+                }
+            }
+        }
+
+        public void requestFileSystem(string options)
+        {
+            // TODO: try/catch
+            string[] optVals = getOptionStrings(options);
+            //FileOptions fileOptions = new FileOptions();
+            int fileSystemType = int.Parse(optVals[0]);
+            double size = double.Parse(optVals[1]);
+            string callbackId = optVals[2];
+
+
+            IsolatedStorageFile.GetUserStoreForApplication();
+
+            if (size > (10 * 1024 * 1024)) // 10 MB, compier will clean this up!
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, QUOTA_EXCEEDED_ERR), callbackId);
+                return;
+            }
+
+            try
+            {
+                if (size != 0)
+                {
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        long availableSize = isoFile.AvailableFreeSpace;
+                        if (size > availableSize)
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, QUOTA_EXCEEDED_ERR), callbackId);
+                            return;
+                        }
+                    }
+                }
+
+                if (fileSystemType == PERSISTENT)
+                {
+                    // TODO: this should be in it's own folder to prevent overwriting of the app assets, which are also in ISO
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("persistent", FileEntry.GetEntry("/"))), callbackId);
+                }
+                else if (fileSystemType == TEMPORARY)
+                {
+                    using (IsolatedStorageFile isoStorage = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (!isoStorage.FileExists(TMP_DIRECTORY_NAME))
+                        {
+                            isoStorage.CreateDirectory(TMP_DIRECTORY_NAME);
+                        }
+                    }
+
+                    string tmpFolder = "/" + TMP_DIRECTORY_NAME + "/";
+
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("temporary", FileEntry.GetEntry(tmpFolder))), callbackId);
+                }
+                else if (fileSystemType == RESOURCE)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("resource")), callbackId);
+                }
+                else if (fileSystemType == APPLICATION)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("application")), callbackId);
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
+                }
+
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
+                }
+            }
+        }
+
+        public void resolveLocalFileSystemURI(string options)
+        {
+
+            string[] optVals = getOptionStrings(options);
+            string uri = optVals[0].Split('?')[0];
+            string callbackId = optVals[1];
+
+            if (uri != null)
+            {
+                // a single '/' is valid, however, '/someDir' is not, but '/tmp//somedir' and '///someDir' are valid
+                if (uri.StartsWith("/") && uri.IndexOf("//") < 0 && uri != "/")
+                {
+                     DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
+                     return;
+                }
+                try
+                {
+                    // fix encoded spaces
+                    string path = Uri.UnescapeDataString(uri);
+
+                    FileEntry uriEntry = FileEntry.GetEntry(path);
+                    if (uriEntry != null)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, uriEntry), callbackId);
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex, callbackId))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
+                    }
+                }
+            }
+        }
+
+        public void copyTo(string options)
+        {
+            TransferTo(options, false);
+        }
+
+        public void moveTo(string options)
+        {
+            TransferTo(options, true);
+        }
+
+        public void getFile(string options)
+        {
+            GetFileOrDirectory(options, false);
+        }
+
+        public void getDirectory(string options)
+        {
+            GetFileOrDirectory(options, true);
+        }
+
+        #region internal functionality
+
+        /// <summary>
+        /// Retrieves the parent directory name of the specified path,
+        /// </summary>
+        /// <param name="path">Path</param>
+        /// <returns>Parent directory name</returns>
+        private string GetParentDirectory(string path)
+        {
+            if (String.IsNullOrEmpty(path) || path == "/")
+            {
+                return "/";
+            }
+
+            if (path.EndsWith(@"/") || path.EndsWith(@"\"))
+            {
+                return this.GetParentDirectory(Path.GetDirectoryName(path));
+            }
+
+            string result = Path.GetDirectoryName(path);
+            if (result == null)
+            {
+                result = "/";
+            }
+
+            return result;
+        }
+
+        private bool removeDirRecursively(string fullPath,string callbackId)
+        {
+            try
+            {
+                if (fullPath == "/")
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
+                    return false;
+                }
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    if (isoFile.DirectoryExists(fullPath))
+                    {
+                        string tempPath = File.AddSlashToDirectory(fullPath);
+                        string[] files = isoFile.GetFileNames(tempPath + "*");
+                        if (files.Length > 0)
+                        {
+                            foreach (string file in files)
+                            {
+                                isoFile.DeleteFile(tempPath + file);
+                            }
+                        }
+                        string[] dirs = isoFile.GetDirectoryNames(tempPath + "*");
+                        if (dirs.Length > 0)
+                        {
+                            foreach (string dir in dirs)
+                            {
+                                if (!removeDirRecursively(tempPath + dir, callbackId))
+                                {
+                                    return false;
+                                }
+                            }
+                        }
+                        isoFile.DeleteDirectory(fullPath);
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private bool CanonicalCompare(string pathA, string pathB)
+        {
+            string a = pathA.Replace("//", "/");
+            string b = pathB.Replace("//", "/");
+
+            return a.Equals(b, StringComparison.OrdinalIgnoreCase);
+        }
+
+        /*
+         *  copyTo:["fullPath","parent", "newName"],
+         *  moveTo:["fullPath","parent", "newName"],
+         */
+        private void TransferTo(string options, bool move)
+        {
+            // TODO: try/catch
+            string[] optStrings = getOptionStrings(options);
+            string fullPath = optStrings[0];
+            string parent = optStrings[1];
+            string newFileName = optStrings[2];
+            string callbackId = optStrings[3];
+
+            char[] invalids = Path.GetInvalidPathChars();
+            
+            if (newFileName.IndexOfAny(invalids) > -1 || newFileName.IndexOf(":") > -1 )
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
+                return;
+            }
+
+            try
+            {
+                if ((parent == null) || (string.IsNullOrEmpty(parent)) || (string.IsNullOrEmpty(fullPath)))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    return;
+                }
+
+                string parentPath = File.AddSlashToDirectory(parent);
+                string currentPath = fullPath;
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    bool isFileExist = isoFile.FileExists(currentPath);
+                    bool isDirectoryExist = isoFile.DirectoryExists(currentPath);
+                    bool isParentExist = isoFile.DirectoryExists(parentPath);
+
+                    if ( ( !isFileExist && !isDirectoryExist ) || !isParentExist )
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                        return;
+                    }
+                    string newName;
+                    string newPath;
+                    if (isFileExist)
+                    {
+                        newName = (string.IsNullOrEmpty(newFileName))
+                                    ? Path.GetFileName(currentPath)
+                                    : newFileName;
+
+                        newPath = Path.Combine(parentPath, newName);
+                        
+                        // sanity check ..
+                        // cannot copy file onto itself
+                        if (CanonicalCompare(newPath,currentPath)) //(parent + newFileName))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId);
+                            return;
+                        }
+                        else if (isoFile.DirectoryExists(newPath)) 
+                        {
+                            // there is already a folder with the same name, operation is not allowed
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId);
+                            return;
+                        }
+                        else if (isoFile.FileExists(newPath))
+                        {   // remove destination file if exists, in other case there will be exception
+                            isoFile.DeleteFile(newPath);
+                        }
+
+                        if (move)
+                        {
+                            isoFile.MoveFile(currentPath, newPath);
+                        }
+                        else
+                        {
+                            isoFile.CopyFile(currentPath, newPath, true);
+                        }
+                    }
+                    else
+                    {
+                        newName = (string.IsNullOrEmpty(newFileName))
+                                    ? currentPath
+                                    : newFileName;
+
+                        newPath = Path.Combine(parentPath, newName);
+
+                        if (move)
+                        {
+                            // remove destination directory if exists, in other case there will be exception
+                            // target directory should be empty
+                            if (!newPath.Equals(currentPath) && isoFile.DirectoryExists(newPath))
+                            {
+                                isoFile.DeleteDirectory(newPath);
+                            }
+
+                            isoFile.MoveDirectory(currentPath, newPath);
+                        }
+                        else
+                        {
+                            CopyDirectory(currentPath, newPath, isoFile);
+                        }
+                    }
+                    FileEntry entry = FileEntry.GetEntry(newPath);
+                    if (entry != null)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId);
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    }
+                }
+
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex, callbackId))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
+                }
+            }
+        }
+
+        private bool HandleException(Exception ex, string cbId="")
+        {
+            bool handled = false;
+            string callbackId = String.IsNullOrEmpty(cbId) ? this.CurrentCommandCallbackId : cbId;
+            if (ex is SecurityException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR), callbackId);
+                handled = true;
+            }
+            else if (ex is FileNotFoundException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                handled = true;
+            }
+            else if (ex is ArgumentException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
+                handled = true;
+            }
+            else if (ex is IsolatedStorageException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId);
+                handled = true;
+            }
+            else if (ex is DirectoryNotFoundException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                handled = true;
+            }
+            return handled;
+        }
+
+        private void CopyDirectory(string sourceDir, string destDir, IsolatedStorageFile isoFile)
+        {
+            string path = File.AddSlashToDirectory(sourceDir);
+
+            bool bExists = isoFile.DirectoryExists(destDir);
+
+            if (!bExists)
+            {
+                isoFile.CreateDirectory(destDir);
+            }
+
+            destDir = File.AddSlashToDirectory(destDir);
+               
+            string[] files = isoFile.GetFileNames(path + "*");
+                
+            if (files.Length > 0)
+            {
+                foreach (string file in files)
+                {
+                    isoFile.CopyFile(path + file, destDir + file,true);
+                }
+            }
+            string[] dirs = isoFile.GetDirectoryNames(path + "*");
+            if (dirs.Length > 0)
+            {
+                foreach (string dir in dirs)
+                {
+                    CopyDirectory(path + dir, destDir + dir, isoFile);
+                }
+            }
+        }
+
+        private void GetFileOrDirectory(string options, bool getDirectory)
+        {
+            FileOptions fOptions = new FileOptions();
+            string[] args = getOptionStrings(options);
+
+            fOptions.FullPath = args[0];
+            fOptions.Path = args[1];
+
+            string callbackId = args[3];
+
+            try
+            {
+                fOptions.CreatingOpt = JSON.JsonHelper.Deserialize<CreatingOptions>(args[2]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId);
+                return;
+            }
+
+            try
+            {
+                if ((string.IsNullOrEmpty(fOptions.Path)) || (string.IsNullOrEmpty(fOptions.FullPath)))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    return;
+                }
+
+                string path;
+
+                if (fOptions.Path.Split(':').Length > 2)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
+                    return;
+                }
+
+                try
+                {
+                    path = Path.Combine(fOptions.FullPath + "/", fOptions.Path);
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
+                    return;
+                }        
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    bool isFile = isoFile.FileExists(path);
+                    bool isDirectory = isoFile.DirectoryExists(path);
+                    bool create = (fOptions.CreatingOpt == null) ? false : fOptions.CreatingOpt.Create;
+                    bool exclusive = (fOptions.CreatingOpt == null) ? false : fOptions.CreatingOpt.Exclusive;
+                    if (create)
+                    {
+                        if (exclusive && (isoFile.FileExists(path) || isoFile.DirectoryExists(path)))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, PATH_EXISTS_ERR), callbackId);
+                            return;
+                        }
+
+                        // need to make sure the parent exists
+                        // it is an error to create a directory whose immediate parent does not yet exist
+			            // see issue: https://issues.apache.org/jira/browse/CB-339
+                        string[] pathParts = path.Split('/');
+                        string builtPath = pathParts[0];
+                        for (int n = 1; n < pathParts.Length - 1; n++)
+                        {
+                            builtPath += "/" + pathParts[n];
+                            if (!isoFile.DirectoryExists(builtPath))
+                            {
+                                Debug.WriteLine(String.Format("Error :: Parent folder \"{0}\" does not exist, when attempting to create \"{1}\"",builtPath,path));
+                                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                                return;
+                            }
+                        }
+
+                        if ((getDirectory) && (!isDirectory))
+                        {
+                            isoFile.CreateDirectory(path);
+                        }
+                        else
+                        {
+                            if ((!getDirectory) && (!isFile))
+                            {
+
+                                IsolatedStorageFileStream fileStream = isoFile.CreateFile(path);
+                                fileStream.Close();
+                            }
+                        }
+                    }
+                    else // (not create)
+                    {
+                        if ((!isFile) && (!isDirectory))
+                        {
+                            if (path.IndexOf("//www") == 0)
+                            {
+                                Uri fileUri = new Uri(path.Remove(0,2), UriKind.Relative);
+                                StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri);
+                                if (streamInfo != null)
+                                {
+                                    FileEntry _entry = FileEntry.GetEntry(fileUri.OriginalString,true);
+
+                                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, _entry), callbackId);
+
+                                    //using (BinaryReader br = new BinaryReader(streamInfo.Stream))
+                                    //{
+                                    //    byte[] data = br.ReadBytes((int)streamInfo.Stream.Length);
+                                   
+                                    //}
+
+                                }
+                                else
+                                {
+                                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                                }
+
+
+                            }
+                            else
+                            {
+                                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                            }
+                            return;
+                        }
+                        if (((getDirectory) && (!isDirectory)) || ((!getDirectory) && (!isFile)))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, TYPE_MISMATCH_ERR), callbackId);
+                            return;
+                        }
+                    }
+                    FileEntry entry = FileEntry.GetEntry(path);
+                    if (entry != null)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId);
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
+                }
+            }
+        }
+
+        private static string AddSlashToDirectory(string dirPath)
+        {
+            if (dirPath.EndsWith("/"))
+            {
+                return dirPath;
+            }
+            else
+            {
+                return dirPath + "/";
+            }
+        }
+
+        /// <summary>
+        /// Returns file content in a form of base64 string
+        /// </summary>
+        /// <param name="stream">File stream</param>
+        /// <returns>Base64 representation of the file</returns>
+        private string GetFileContent(Stream stream)
+        {
+            int streamLength = (int)stream.Length;
+            byte[] fileData = new byte[streamLength + 1];
+            stream.Read(fileData, 0, streamLength);
+            stream.Close();
+            return Convert.ToBase64String(fileData);
+        }
+
+        #endregion
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/FileTransfer.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/FileTransfer.cs b/lib/cordova-wp7/templates/standalone/Plugins/FileTransfer.cs
new file mode 100644
index 0000000..e585895
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/FileTransfer.cs
@@ -0,0 +1,526 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Net;
+using System.Runtime.Serialization;
+using System.Windows;
+using System.Security;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public class FileTransfer : BaseCommand
+    {
+        public class DownloadRequestState
+        {
+            // This class stores the State of the request.
+            public HttpWebRequest request;
+            public DownloadOptions options;
+
+            public DownloadRequestState()
+            {
+                request = null;
+                options = null;
+            }
+        }
+
+        /// <summary>
+        /// Boundary symbol
+        /// </summary>       
+        private string Boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");
+
+        // Error codes
+        public const int FileNotFoundError = 1;
+        public const int InvalidUrlError = 2;
+        public const int ConnectionError = 3;
+
+        /// <summary>
+        /// Options for downloading file
+        /// </summary>
+        [DataContract]
+        public class DownloadOptions
+        {
+            /// <summary>
+            /// File path to download to
+            /// </summary>
+            [DataMember(Name = "filePath", IsRequired = true)]
+            public string FilePath { get; set; }
+
+            /// <summary>
+            /// Server address to the file to download
+            /// </summary>
+            [DataMember(Name = "url", IsRequired = true)]
+            public string Url { get; set; }
+        }
+
+        /// <summary>
+        /// Options for uploading file
+        /// </summary>
+        [DataContract]
+        public class UploadOptions
+        {
+            /// <summary>
+            /// File path to upload
+            /// </summary>
+            [DataMember(Name = "filePath", IsRequired = true)]
+            public string FilePath { get; set; }
+
+            /// <summary>
+            /// Server address
+            /// </summary>
+            [DataMember(Name = "server", IsRequired = true)]
+            public string Server { get; set; }
+
+            /// <summary>
+            /// File key
+            /// </summary>
+            [DataMember(Name = "fileKey")]
+            public string FileKey { get; set; }
+
+            /// <summary>
+            /// File name on the server
+            /// </summary>
+            [DataMember(Name = "fileName")]
+            public string FileName { get; set; }
+
+            /// <summary>
+            /// File Mime type
+            /// </summary>
+            [DataMember(Name = "mimeType")]
+            public string MimeType { get; set; }
+
+
+            /// <summary>
+            /// Additional options
+            /// </summary>
+            [DataMember(Name = "params")]
+            public string Params { get; set; }
+
+            /// <summary>
+            /// Flag to recognize if we should trust every host (only in debug environments)
+            /// </summary>
+            [DataMember(Name = "debug")]
+            public bool Debug { get; set; }
+
+            /// <summary>
+            /// Creates options object with default parameters
+            /// </summary>
+            public UploadOptions()
+            {
+                this.SetDefaultValues(new StreamingContext());
+            }
+
+            /// <summary>
+            /// Initializes default values for class fields.
+            /// Implemented in separate method because default constructor is not invoked during deserialization.
+            /// </summary>
+            /// <param name="context"></param>
+            [OnDeserializing()]
+            public void SetDefaultValues(StreamingContext context)
+            {
+                this.FileKey = "file";
+                this.FileName = "image.jpg";
+                this.MimeType = "image/jpeg";
+            }
+
+        }
+
+        /// <summary>
+        /// Uploading response info
+        /// </summary>
+        [DataContract]
+        public class FileUploadResult
+        {
+            /// <summary>
+            /// Amount of sent bytes
+            /// </summary>
+            [DataMember(Name = "bytesSent")]
+            public long BytesSent { get; set; }
+
+            /// <summary>
+            /// Server response code
+            /// </summary>
+            [DataMember(Name = "responseCode")]
+            public long ResponseCode { get; set; }
+
+            /// <summary>
+            /// Server response
+            /// </summary>
+            [DataMember(Name = "response", EmitDefaultValue = false)]
+            public string Response { get; set; }
+
+            /// <summary>
+            /// Creates FileUploadResult object with response values
+            /// </summary>
+            /// <param name="bytesSent">Amount of sent bytes</param>
+            /// <param name="responseCode">Server response code</param>
+            /// <param name="response">Server response</param>
+            public FileUploadResult(long bytesSent, long responseCode, string response)
+            {
+                this.BytesSent = bytesSent;
+                this.ResponseCode = responseCode;
+                this.Response = response;
+            }
+        }
+
+        /// <summary>
+        /// Represents transfer error codes for callback
+        /// </summary>
+        [DataContract]
+        public class FileTransferError
+        {
+            /// <summary>
+            /// Error code
+            /// </summary>
+            [DataMember(Name = "code", IsRequired = true)]
+            public int Code { get; set; }
+
+            /// <summary>
+            /// The source URI
+            /// </summary>
+            [DataMember(Name = "source", IsRequired = true)]
+            public string Source { get; set; }
+
+            /// <summary>
+            /// The target URI
+            /// </summary>
+            [DataMember(Name = "target", IsRequired = true)]
+            public string Target { get; set; }
+
+            /// <summary>
+            /// The http status code response from the remote URI
+            /// </summary>
+            [DataMember(Name = "http_status", IsRequired = true)]
+            public int HttpStatus { get; set; }
+
+            /// <summary>
+            /// Creates FileTransferError object
+            /// </summary>
+            /// <param name="errorCode">Error code</param>
+            public FileTransferError(int errorCode)
+            {
+                this.Code = errorCode;
+                this.Source = null;
+                this.Target = null;
+                this.HttpStatus = 0;
+            }
+            public FileTransferError(int errorCode, string source, string target, int status)
+            {
+                this.Code = errorCode;
+                this.Source = source;
+                this.Target = target;
+                this.HttpStatus = status;
+            }
+        }
+
+        /// <summary>
+        /// Upload options
+        /// </summary>
+        private UploadOptions uploadOptions;
+
+        /// <summary>
+        /// Bytes sent
+        /// </summary>
+        private long bytesSent;
+
+        /// <summary>
+        /// sends a file to a server
+        /// </summary>
+        /// <param name="options">Upload options</param>
+        public void upload(string options)
+        {
+            Debug.WriteLine("options = " + options);
+            options = options.Replace("{}", "null");
+
+            try 
+            {
+                try 
+                {
+                    string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                    uploadOptions = JSON.JsonHelper.Deserialize<UploadOptions>(args[0]);
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                Uri serverUri;
+                try
+                {
+                    serverUri = new Uri(uploadOptions.Server);
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(InvalidUrlError, uploadOptions.Server, null, 0)));
+                    return;
+                }
+                HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(serverUri);
+                webRequest.ContentType = "multipart/form-data;boundary=" + Boundary;
+                webRequest.Method = "POST";
+                webRequest.BeginGetRequestStream(WriteCallback, webRequest);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)));
+            }
+        }
+
+        public void download(string options)
+        {
+            DownloadOptions downloadOptions = null;
+            HttpWebRequest webRequest = null;
+
+            try
+            {
+                string[] optionStrings = JSON.JsonHelper.Deserialize<string[]>(options);
+
+                downloadOptions = new DownloadOptions();// JSON.JsonHelper.Deserialize<DownloadOptions>(options);
+                downloadOptions.Url = optionStrings[0];
+                downloadOptions.FilePath = optionStrings[1];
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                webRequest = (HttpWebRequest)WebRequest.Create(downloadOptions.Url);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(InvalidUrlError, downloadOptions.Url, null, 0)));
+                return;
+            }
+
+            if (downloadOptions != null && webRequest != null)
+            {
+                DownloadRequestState state = new DownloadRequestState();
+                state.options = downloadOptions;
+                state.request = webRequest;
+                webRequest.BeginGetResponse(new AsyncCallback(downloadCallback), state);
+            }
+
+
+
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="asynchronousResult"></param>
+        private void downloadCallback(IAsyncResult asynchronousResult)
+        {
+            DownloadRequestState reqState = (DownloadRequestState)asynchronousResult.AsyncState;
+            HttpWebRequest request = reqState.request;
+
+            try
+            {
+                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    // create the file if not exists
+                    if (!isoFile.FileExists(reqState.options.FilePath))
+                    {
+                        var file = isoFile.CreateFile(reqState.options.FilePath);
+                        file.Close();
+                    }
+
+                    using (FileStream fileStream = new IsolatedStorageFileStream(reqState.options.FilePath, FileMode.Open, FileAccess.Write, isoFile))
+                    {
+                        long totalBytes = response.ContentLength;
+                        int bytesRead = 0;
+                        using (BinaryReader reader = new BinaryReader(response.GetResponseStream()))
+                        {
+
+                            using (BinaryWriter writer = new BinaryWriter(fileStream))
+                            {
+                                int BUFFER_SIZE = 1024;
+                                byte[] buffer;
+
+                                while (true)
+                                {
+                                    buffer = reader.ReadBytes(BUFFER_SIZE);
+                                    // fire a progress event ?
+                                    bytesRead += buffer.Length;
+                                    if (buffer.Length > 0)
+                                    {
+                                        writer.Write(buffer);
+                                    }
+                                    else
+                                    {
+                                        writer.Close();
+                                        reader.Close();
+                                        fileStream.Close();
+                                        break;
+                                    }
+                                }
+                            }
+
+                        }
+
+
+                    }
+                }
+                WPCordovaClassLib.Cordova.Commands.File.FileEntry entry = new WPCordovaClassLib.Cordova.Commands.File.FileEntry(reqState.options.FilePath);
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry));
+            }
+            catch (IsolatedStorageException)
+            {
+                // Trying to write the file somewhere within the IsoStorage.
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)));
+            }
+            catch (SecurityException)
+            {
+                // Trying to write the file somewhere not allowed.
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)));
+            }
+            catch (WebException webex)
+            {
+                // TODO: probably need better work here to properly respond with all http status codes back to JS
+                // Right now am jumping through hoops just to detect 404.
+                if ((webex.Status == WebExceptionStatus.ProtocolError && ((HttpWebResponse)webex.Response).StatusCode == HttpStatusCode.NotFound) || webex.Status == WebExceptionStatus.UnknownError)
+                {
+                    // Weird MSFT detection of 404... seriously... just give us the f(*&#$@ status code as a number ffs!!!
+                    // "Numbers for HTTP status codes? Nah.... let's create our own set of enums/structs to abstract that stuff away."
+                    // FACEPALM
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError, null, null, 404)));
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)));
+                }
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)));
+            }
+        }
+
+
+
+        /// <summary>
+        /// Read file from Isolated Storage and sends it to server
+        /// </summary>
+        /// <param name="asynchronousResult"></param>
+        private void WriteCallback(IAsyncResult asynchronousResult)
+        {
+            try
+            {
+                HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
+                using (Stream requestStream = (webRequest.EndGetRequestStream(asynchronousResult)))
+                {
+                    string lineStart = "--";
+                    string lineEnd = Environment.NewLine;
+                    byte[] boundaryBytes = System.Text.Encoding.UTF8.GetBytes(lineStart + Boundary + lineEnd);
+                    string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"" + lineEnd + lineEnd + "{1}" + lineEnd;
+
+                    if (uploadOptions.Params != null)
+                    {
+
+                        string[] arrParams = uploadOptions.Params.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
+
+                        foreach (string param in arrParams)
+                        {
+                            string[] split = param.Split('=');
+                            string key = split[0];
+                            string val = split[1];
+                            requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
+                            string formItem = string.Format(formdataTemplate, key, val);
+                            byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes(formItem);
+                            requestStream.Write(formItemBytes, 0, formItemBytes.Length);
+                        }
+                        requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
+                    }
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (!isoFile.FileExists(uploadOptions.FilePath))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError, uploadOptions.Server, uploadOptions.FilePath, 0)));
+                            return;
+                        }
+
+                        using (FileStream fileStream = new IsolatedStorageFileStream(uploadOptions.FilePath, FileMode.Open, isoFile))
+                        {
+                            string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + lineEnd + "Content-Type: {2}" + lineEnd + lineEnd;
+                            string header = string.Format(headerTemplate, uploadOptions.FileKey, uploadOptions.FileName, uploadOptions.MimeType);
+                            byte[] headerBytes = System.Text.Encoding.UTF8.GetBytes(header);
+                            requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
+                            requestStream.Write(headerBytes, 0, headerBytes.Length);
+                            byte[] buffer = new byte[4096];
+                            int bytesRead = 0;
+
+                            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
+                            {
+                                requestStream.Write(buffer, 0, bytesRead);
+                                bytesSent += bytesRead;
+                            }
+                        }
+                        byte[] endRequest = System.Text.Encoding.UTF8.GetBytes(lineEnd + lineStart + Boundary + lineStart + lineEnd);
+                        requestStream.Write(endRequest, 0, endRequest.Length);
+                    }
+                }
+                webRequest.BeginGetResponse(ReadCallback, webRequest);
+            }
+            catch (Exception)
+            {
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)));
+                });
+            }
+        }
+
+        /// <summary>
+        /// Reads response into FileUploadResult
+        /// </summary>
+        /// <param name="asynchronousResult"></param>
+        private void ReadCallback(IAsyncResult asynchronousResult)
+        {
+            try
+            {
+                HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
+                using (HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult))
+                {
+                    using (Stream streamResponse = response.GetResponseStream())
+                    {
+                        using (StreamReader streamReader = new StreamReader(streamResponse))
+                        {
+                            string responseString = streamReader.ReadToEnd();
+                            Deployment.Current.Dispatcher.BeginInvoke(() =>
+                            {
+                                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileUploadResult(bytesSent, (long)response.StatusCode, responseString)));
+                            });
+                        }
+                    }
+                }
+            }
+            catch (Exception)
+            {
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    FileTransferError transferError = new FileTransferError(ConnectionError, uploadOptions.Server, uploadOptions.FilePath, 403);
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, transferError));
+                });
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/GeoLocation.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/GeoLocation.cs b/lib/cordova-wp7/templates/standalone/Plugins/GeoLocation.cs
new file mode 100644
index 0000000..c53cb29
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/GeoLocation.cs
@@ -0,0 +1,34 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Threading;
+using System.Device.Location;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// This is a command stub, the browser provides the correct implementation.  We use this to trigger the static analyzer that we require this permission 
+    /// </summary>
+    public class GeoLocation
+    {
+        /* Unreachable code, by design -jm */
+        private void triggerGeoInclusion()
+        {
+            new GeoCoordinateWatcher();
+        }
+    }
+}


[45/50] git commit: Fix up all hardcoded paths to the www dir in favour of the util helpers

Posted by br...@apache.org.
Fix up all hardcoded paths to the www dir in favour of the util helpers


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

Branch: refs/heads/master2
Commit: 01811ca454e7aff7c49041ae0577e75f387f0eb7
Parents: 0584e17
Author: Braden Shepherdson <br...@gmail.com>
Authored: Thu May 23 16:50:40 2013 -0400
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Thu May 23 16:50:40 2013 -0400

----------------------------------------------------------------------
 src/serve.js |   28 ++++++++++------------------
 src/util.js  |    3 +++
 2 files changed, 13 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/01811ca4/src/serve.js
----------------------------------------------------------------------
diff --git a/src/serve.js b/src/serve.js
index 2829847..e7eb42b 100644
--- a/src/serve.js
+++ b/src/serve.js
@@ -21,14 +21,18 @@ var cordova_util = require('./util'),
     path = require('path'),
     shell = require('shelljs'),
     config_parser = require('./config_parser'),
-    android_parser = require('./metadata/android_parser'),
-    ios_parser = require('./metadata/ios_parser'),
-    blackberry_parser = require('./metadata/blackberry_parser'),
     fs = require('fs'),
     util = require('util'),
     http = require("http"),
     url = require("url");
 
+var parsers = {
+    'android': require('./metadata/android_parser'),
+    'ios': require('./metadata/ios_parser'),
+    'blackberry': require('./metadata/blackberry_parser'),
+    'wp7': require('./metadata/wp7_parser'),
+    'wp8': require('./metadata/wp8_parser')
+};
 
 function launch_server(www, platform_www, config_xml_path, port) {
     port = port || 8000;
@@ -123,21 +127,9 @@ module.exports.config = function (platform, port, callback) {
     };
 
     // Top-level www directory.
-    result.paths.push(projectRoot + path.sep + 'www');
-
-    var parser;
-
-    switch (platform) {
-        case 'android':
-            parser = new android_parser(path.join(projectRoot, 'platforms', platform));
-            break;
-        case 'blackberry-10':
-            parser = new blackberry_parser(path.join(projectRoot, 'platforms', platform));
-            break;
-        case 'ios':
-            parser = new ios_parser(path.join(projectRoot, 'platforms', platform));
-            break;
-    }
+    result.paths.push(cordova_util.projectWww(projectRoot));
+
+    var parser = parsers[platform](path.join(projectRoot, 'platforms', platform));
 
     // Update the related platform project from the config
     parser.update_project(cfg, function() {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/01811ca4/src/util.js
----------------------------------------------------------------------
diff --git a/src/util.js b/src/util.js
index cd34533..9b725a4 100644
--- a/src/util.js
+++ b/src/util.js
@@ -77,6 +77,9 @@ module.exports = {
 
         return plugins;
     },
+    appDir: function(projectDir) {
+        return projectDir;
+    },
     projectWww: function(projectDir) {
         return path.join(projectDir, 'www');
     },


[22/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/Device.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/Device.cs b/lib/cordova-wp8/templates/standalone/Plugins/Device.cs
new file mode 100644
index 0000000..07100ae
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/Device.cs
@@ -0,0 +1,135 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Info;
+using System.IO.IsolatedStorage;
+using System.Windows.Resources;
+using System.IO;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public class Device : BaseCommand
+    {
+        public void getDeviceInfo(string notused)
+        {
+
+            string res = String.Format("\"name\":\"{0}\",\"cordova\":\"{1}\",\"platform\":\"{2}\",\"uuid\":\"{3}\",\"version\":\"{4}\",\"model\":\"{5}\"",
+                                        this.name,
+                                        this.cordova,
+                                        this.platform,
+                                        this.uuid,
+                                        this.version,
+                                        this.model);
+
+
+
+            res = "{" + res + "}";
+            //Debug.WriteLine("Result::" + res);
+            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, res));
+        }
+
+        public string model
+        {
+            get
+            {
+                return DeviceStatus.DeviceName;
+                //return String.Format("{0},{1},{2}", DeviceStatus.DeviceManufacturer, DeviceStatus.DeviceHardwareVersion, DeviceStatus.DeviceFirmwareVersion); 
+            }
+        }
+
+        public string name
+        {
+            get
+            {
+                return DeviceStatus.DeviceName;
+                
+            }
+        }
+
+        public string cordova
+        {
+            get
+            {
+                // TODO: should be able to dynamically read the Cordova version from somewhere...
+                return "2.7.0";
+            }
+        }
+
+        public string platform
+        {
+            get
+            {
+                return Environment.OSVersion.Platform.ToString();
+            }
+        }
+
+        public string uuid
+        {
+            get
+            {
+                string returnVal = "";
+                object id;
+                UserExtendedProperties.TryGetValue("ANID", out id);
+
+                if (id != null)
+                {
+                    returnVal = id.ToString().Substring(2, 32);
+                }
+                else
+                {
+                    returnVal = "???unknown???";
+
+                    using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        try
+                        {
+                            IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Open, FileAccess.Read, appStorage);
+
+                            using (StreamReader reader = new StreamReader(fileStream))
+                            {
+                                returnVal = reader.ReadLine();
+                            }
+                        }
+                        catch (Exception /*ex*/)
+                        {
+
+                        }
+                    }
+                }
+
+                return returnVal;
+            }
+        }
+
+        public string version
+        {
+            get
+            {
+                return Environment.OSVersion.Version.ToString();
+            }
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/File.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/File.cs b/lib/cordova-wp8/templates/standalone/Plugins/File.cs
new file mode 100644
index 0000000..cde7a1c
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/File.cs
@@ -0,0 +1,1676 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Runtime.Serialization;
+using System.Security;
+using System.Text;
+using System.Windows;
+using System.Windows.Resources;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Provides access to isolated storage
+    /// </summary>
+    public class File : BaseCommand
+    {
+        // Error codes
+        public const int NOT_FOUND_ERR = 1;
+        public const int SECURITY_ERR = 2;
+        public const int ABORT_ERR = 3;
+        public const int NOT_READABLE_ERR = 4;
+        public const int ENCODING_ERR = 5;
+        public const int NO_MODIFICATION_ALLOWED_ERR = 6;
+        public const int INVALID_STATE_ERR = 7;
+        public const int SYNTAX_ERR = 8;
+        public const int INVALID_MODIFICATION_ERR = 9;
+        public const int QUOTA_EXCEEDED_ERR = 10;
+        public const int TYPE_MISMATCH_ERR = 11;
+        public const int PATH_EXISTS_ERR = 12;
+
+        // File system options
+        public const int TEMPORARY = 0;
+        public const int PERSISTENT = 1;
+        public const int RESOURCE = 2;
+        public const int APPLICATION = 3;
+
+        /// <summary>
+        /// Temporary directory name
+        /// </summary>
+        private readonly string TMP_DIRECTORY_NAME = "tmp";
+
+        /// <summary>
+        /// Represents error code for callback
+        /// </summary>
+        [DataContract]
+        public class ErrorCode
+        {
+            /// <summary>
+            /// Error code
+            /// </summary>
+            [DataMember(IsRequired = true, Name = "code")]
+            public int Code { get; set; }
+
+            /// <summary>
+            /// Creates ErrorCode object
+            /// </summary>
+            public ErrorCode(int code)
+            {
+                this.Code = code;
+            }
+        }
+
+        /// <summary>
+        /// Represents File action options.
+        /// </summary>
+        [DataContract]
+        public class FileOptions
+        {
+            /// <summary>
+            /// File path
+            /// </summary>
+            /// 
+            private string _fileName;
+            [DataMember(Name = "fileName")]
+            public string FilePath
+            {
+                get
+                {
+                    return this._fileName;
+                }
+
+                set
+                {
+                    int index = value.IndexOfAny(new char[] { '#', '?' });
+                    this._fileName = index > -1 ? value.Substring(0, index) : value;
+                }
+            }
+
+            /// <summary>
+            /// Full entryPath
+            /// </summary>
+            [DataMember(Name = "fullPath")]
+            public string FullPath { get; set; }
+
+            /// <summary>
+            /// Directory name
+            /// </summary>
+            [DataMember(Name = "dirName")]
+            public string DirectoryName { get; set; }
+
+            /// <summary>
+            /// Path to create file/directory
+            /// </summary>
+            [DataMember(Name = "path")]
+            public string Path { get; set; }
+
+            /// <summary>
+            /// The encoding to use to encode the file's content. Default is UTF8.
+            /// </summary>
+            [DataMember(Name = "encoding")]
+            public string Encoding { get; set; }
+
+            /// <summary>
+            /// Uri to get file
+            /// </summary>
+            /// 
+            private string _uri;
+            [DataMember(Name = "uri")]
+            public string Uri
+            {
+                get
+                {
+                    return this._uri;
+                }
+
+                set
+                {
+                    int index = value.IndexOfAny(new char[] { '#', '?' });
+                    this._uri = index > -1 ? value.Substring(0, index) : value;
+                }
+            }
+
+            /// <summary>
+            /// Size to truncate file
+            /// </summary>
+            [DataMember(Name = "size")]
+            public long Size { get; set; }
+
+            /// <summary>
+            /// Data to write in file
+            /// </summary>
+            [DataMember(Name = "data")]
+            public string Data { get; set; }
+
+            /// <summary>
+            /// Position the writing starts with
+            /// </summary>
+            [DataMember(Name = "position")]
+            public int Position { get; set; }
+
+            /// <summary>
+            /// Type of file system requested
+            /// </summary>
+            [DataMember(Name = "type")]
+            public int FileSystemType { get; set; }
+
+            /// <summary>
+            /// New file/directory name
+            /// </summary>
+            [DataMember(Name = "newName")]
+            public string NewName { get; set; }
+
+            /// <summary>
+            /// Destination directory to copy/move file/directory
+            /// </summary>
+            [DataMember(Name = "parent")]
+            public string Parent { get; set; }
+
+            /// <summary>
+            /// Options for getFile/getDirectory methods
+            /// </summary>
+            [DataMember(Name = "options")]
+            public CreatingOptions CreatingOpt { get; set; }
+
+            /// <summary>
+            /// Creates options object with default parameters
+            /// </summary>
+            public FileOptions()
+            {
+                this.SetDefaultValues(new StreamingContext());
+            }
+
+            /// <summary>
+            /// Initializes default values for class fields.
+            /// Implemented in separate method because default constructor is not invoked during deserialization.
+            /// </summary>
+            /// <param name="context"></param>
+            [OnDeserializing()]
+            public void SetDefaultValues(StreamingContext context)
+            {
+                this.Encoding = "UTF-8";
+                this.FilePath = "";
+                this.FileSystemType = -1; 
+            }
+        }
+
+        /// <summary>
+        /// Stores image info
+        /// </summary>
+        [DataContract]
+        public class FileMetadata
+        {
+            [DataMember(Name = "fileName")]
+            public string FileName { get; set; }
+
+            [DataMember(Name = "fullPath")]
+            public string FullPath { get; set; }
+
+            [DataMember(Name = "type")]
+            public string Type { get; set; }
+
+            [DataMember(Name = "lastModifiedDate")]
+            public string LastModifiedDate { get; set; }
+
+            [DataMember(Name = "size")]
+            public long Size { get; set; }
+
+            public FileMetadata(string filePath)
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    if (string.IsNullOrEmpty(filePath))
+                    {
+                        throw new FileNotFoundException("File doesn't exist");
+                    }
+                    else if (!isoFile.FileExists(filePath))
+                    {
+                        // attempt to get it from the resources
+                        if (filePath.IndexOf("www") == 0)
+                        {
+                            Uri fileUri = new Uri(filePath, UriKind.Relative);
+                            StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri);
+                            if (streamInfo != null)
+                            {
+                                this.Size = streamInfo.Stream.Length;
+                                this.FileName = filePath.Substring(filePath.LastIndexOf("/") + 1);
+                                this.FullPath = filePath;
+                            }
+                        }
+                        else
+                        {
+                            throw new FileNotFoundException("File doesn't exist");
+                        }
+                    }
+                    else
+                    {
+                        //TODO get file size the other way if possible                
+                        using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.Read, isoFile))
+                        {
+                            this.Size = stream.Length;
+                        }
+                        this.FullPath = filePath;
+                        this.FileName = System.IO.Path.GetFileName(filePath);
+                        this.LastModifiedDate = isoFile.GetLastWriteTime(filePath).DateTime.ToString();
+                    }
+                    this.Type = MimeTypeMapper.GetMimeType(this.FileName);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Represents file or directory modification metadata
+        /// </summary>
+        [DataContract]
+        public class ModificationMetadata
+        {
+            /// <summary>
+            /// Modification time
+            /// </summary>
+            [DataMember]
+            public string modificationTime { get; set; }
+        }
+
+        /// <summary>
+        /// Represents file or directory entry
+        /// </summary>
+        [DataContract]
+        public class FileEntry
+        {
+
+            /// <summary>
+            /// File type
+            /// </summary>
+            [DataMember(Name = "isFile")]
+            public bool IsFile { get; set; }
+
+            /// <summary>
+            /// Directory type
+            /// </summary>
+            [DataMember(Name = "isDirectory")]
+            public bool IsDirectory { get; set; }
+
+            /// <summary>
+            /// File/directory name
+            /// </summary>
+            [DataMember(Name = "name")]
+            public string Name { get; set; }
+
+            /// <summary>
+            /// Full path to file/directory
+            /// </summary>
+            [DataMember(Name = "fullPath")]
+            public string FullPath { get; set; }
+
+            public bool IsResource { get; set; }
+
+            public static FileEntry GetEntry(string filePath, bool bIsRes=false)
+            {
+                FileEntry entry = null;
+                try
+                {
+                    entry = new FileEntry(filePath, bIsRes);
+
+                }
+                catch (Exception ex)
+                {
+                    Debug.WriteLine("Exception in GetEntry for filePath :: " + filePath + " " + ex.Message);
+                }
+                return entry;
+            }
+
+            /// <summary>
+            /// Creates object and sets necessary properties
+            /// </summary>
+            /// <param name="filePath"></param>
+            public FileEntry(string filePath, bool bIsRes = false)
+            {
+                if (string.IsNullOrEmpty(filePath))
+                {
+                    throw new ArgumentException();
+                }
+
+                if(filePath.Contains(" ")) 
+                {
+                    Debug.WriteLine("FilePath with spaces :: " +  filePath);
+                }
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    IsResource = bIsRes;
+                    IsFile = isoFile.FileExists(filePath);
+                    IsDirectory = isoFile.DirectoryExists(filePath);
+                    if (IsFile)
+                    {
+                        this.Name = Path.GetFileName(filePath);
+                    }
+                    else if (IsDirectory)
+                    {
+                        this.Name = this.GetDirectoryName(filePath);
+                        if (string.IsNullOrEmpty(Name))
+                        {
+                            this.Name = "/";
+                        }
+                    }
+                    else
+                    {
+                        if (IsResource)
+                        {
+                            this.Name = Path.GetFileName(filePath);
+                        }
+                        else
+                        {
+                            throw new FileNotFoundException();
+                        }
+                    }
+
+                    try
+                    {
+                        this.FullPath = filePath.Replace('\\', '/'); // new Uri(filePath).LocalPath;
+                    }
+                    catch (Exception)
+                    {
+                        this.FullPath = filePath;
+                    }
+                }
+            }
+
+            /// <summary>
+            /// Extracts directory name from path string
+            /// Path should refer to a directory, for example \foo\ or /foo.
+            /// </summary>
+            /// <param name="path"></param>
+            /// <returns></returns>
+            private string GetDirectoryName(string path)
+            {
+                if (String.IsNullOrEmpty(path))
+                {
+                    return path;
+                }
+
+                string[] split = path.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
+                if (split.Length < 1)
+                {
+                    return null;
+                }
+                else
+                {
+                    return split[split.Length - 1];
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Represents info about requested file system
+        /// </summary>
+        [DataContract]
+        public class FileSystemInfo
+        {
+            /// <summary>
+            /// file system type
+            /// </summary>
+            [DataMember(Name = "name", IsRequired = true)]
+            public string Name { get; set; }
+
+            /// <summary>
+            /// Root directory entry
+            /// </summary>
+            [DataMember(Name = "root", EmitDefaultValue = false)]
+            public FileEntry Root { get; set; }
+
+            /// <summary>
+            /// Creates class instance
+            /// </summary>
+            /// <param name="name"></param>
+            /// <param name="rootEntry"> Root directory</param>
+            public FileSystemInfo(string name, FileEntry rootEntry = null)
+            {
+                Name = name;
+                Root = rootEntry;
+            }
+        }
+
+        [DataContract]
+        public class CreatingOptions
+        {
+            /// <summary>
+            /// Create file/directory if is doesn't exist
+            /// </summary>
+            [DataMember(Name = "create")]
+            public bool Create { get; set; }
+
+            /// <summary>
+            /// Generate an exception if create=true and file/directory already exists
+            /// </summary>
+            [DataMember(Name = "exclusive")]
+            public bool Exclusive { get; set; }
+
+
+        }
+
+        // returns null value if it fails.
+        private string[] getOptionStrings(string options)
+        {
+            string[] optStings = null;
+            try
+            {
+                optStings = JSON.JsonHelper.Deserialize<string[]>(options);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), CurrentCommandCallbackId);
+            }
+            return optStings;
+        }
+
+        /// <summary>
+        /// Gets amount of free space available for Isolated Storage
+        /// </summary>
+        /// <param name="options">No options is needed for this method</param>
+        public void getFreeDiskSpace(string options)
+        {
+            string callbackId = getOptionStrings(options)[0];
+
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isoFile.AvailableFreeSpace), callbackId);
+                }
+            }
+            catch (IsolatedStorageException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Check if file exists
+        /// </summary>
+        /// <param name="options">File path</param>
+        public void testFileExists(string options)
+        {
+            IsDirectoryOrFileExist(options, false);
+        }
+
+        /// <summary>
+        /// Check if directory exists
+        /// </summary>
+        /// <param name="options">directory name</param>
+        public void testDirectoryExists(string options)
+        {
+            IsDirectoryOrFileExist(options, true);
+        }
+
+        /// <summary>
+        /// Check if file or directory exist
+        /// </summary>
+        /// <param name="options">File path/Directory name</param>
+        /// <param name="isDirectory">Flag to recognize what we should check</param>
+        public void IsDirectoryOrFileExist(string options, bool isDirectory)
+        {
+            string[] args = getOptionStrings(options);
+            string callbackId = args[1];
+            FileOptions fileOptions = JSON.JsonHelper.Deserialize<FileOptions>(args[0]);
+            string filePath = args[0];
+
+            if (fileOptions == null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId);
+            }
+
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    bool isExist;
+                    if (isDirectory)
+                    {
+                        isExist = isoFile.DirectoryExists(fileOptions.DirectoryName);
+                    }
+                    else
+                    {
+                        isExist = isoFile.FileExists(fileOptions.FilePath);
+                    }
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isExist), callbackId);
+                }
+            }
+            catch (IsolatedStorageException) // default handler throws INVALID_MODIFICATION_ERR
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                }
+            }
+
+        }
+
+        public void readAsDataURL(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+            string filePath = optStrings[0];
+            int startPos = int.Parse(optStrings[1]);
+            int endPos = int.Parse(optStrings[2]);
+            string callbackId = optStrings[3];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    string base64URL = null;
+
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (!isoFile.FileExists(filePath))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                            return;
+                        }
+                        string mimeType = MimeTypeMapper.GetMimeType(filePath);
+
+                        using (IsolatedStorageFileStream stream = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read))
+                        {
+                            string base64String = GetFileContent(stream);
+                            base64URL = "data:" + mimeType + ";base64," + base64String;
+                        }
+                    }
+
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, base64URL), callbackId);
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                    }
+                }
+            }
+        }
+
+        public void readAsArrayBuffer(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+            string filePath = optStrings[0];
+            int startPos = int.Parse(optStrings[1]);
+            int endPos = int.Parse(optStrings[2]);
+            string callbackId = optStrings[3];
+            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR), callbackId);
+        }
+
+        public void readAsBinaryString(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+            string filePath = optStrings[0];
+            int startPos = int.Parse(optStrings[1]);
+            int endPos = int.Parse(optStrings[2]);
+            string callbackId = optStrings[3];
+            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR), callbackId);
+        }
+
+        public void readAsText(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+            string filePath = optStrings[0];
+            string encStr = optStrings[1];
+            int startPos = int.Parse(optStrings[2]);
+            int endPos = int.Parse(optStrings[3]);
+            string callbackId = optStrings[4];
+
+            try
+            {
+                string text = "";
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    if (!isoFile.FileExists(filePath))
+                    {
+                        readResourceAsText(options);
+                        return;
+                    }
+                    Encoding encoding = Encoding.GetEncoding(encStr);
+
+                    using (TextReader reader = new StreamReader(isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read), encoding))
+                    {
+                        text = reader.ReadToEnd();
+                        if (startPos < 0)
+                        {
+                            startPos = Math.Max(text.Length + startPos, 0);
+                        }
+                        else if (startPos > 0)
+                        {
+                            startPos = Math.Min(text.Length, startPos);
+                        }
+
+                        if (endPos > 0)
+                        {
+                            endPos = Math.Min(text.Length, endPos);
+                        }
+                        else if (endPos < 0)
+                        {
+                            endPos = Math.Max(endPos + text.Length, 0);
+                        }
+                        
+                        
+                        text = text.Substring(startPos, endPos - startPos);
+                        
+                    }
+                }
+
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, text), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex, callbackId))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Reads application resource as a text
+        /// </summary>
+        /// <param name="options">Path to a resource</param>
+        public void readResourceAsText(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+            string pathToResource = optStrings[0];
+            string encStr = optStrings[1];
+            int start = int.Parse(optStrings[2]);
+            int endMarker = int.Parse(optStrings[3]);
+            string callbackId = optStrings[4];
+
+            try
+            {
+                if (pathToResource.StartsWith("/"))
+                {
+                    pathToResource = pathToResource.Remove(0, 1);
+                }
+                
+                var resource = Application.GetResourceStream(new Uri(pathToResource, UriKind.Relative));
+                
+                if (resource == null)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    return;
+                }
+
+                string text;
+                StreamReader streamReader = new StreamReader(resource.Stream);
+                text = streamReader.ReadToEnd();
+
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, text), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex, callbackId))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+            }
+        }
+
+        public void truncate(string options)
+        {
+            string[] optStrings = getOptionStrings(options);
+
+            string filePath = optStrings[0];
+            int size = int.Parse(optStrings[1]);
+            string callbackId = optStrings[2];
+
+            try
+            {
+                long streamLength = 0;
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    if (!isoFile.FileExists(filePath))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                        return;
+                    }
+
+                    using (FileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.ReadWrite, isoFile))
+                    {
+                        if (0 <= size && size <= stream.Length)
+                        {
+                            stream.SetLength(size);
+                        }
+                        streamLength = stream.Length;
+                    }
+                }
+
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, streamLength), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex, callbackId))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+            }
+        }
+
+        //write:["filePath","data","position"],
+        public void write(string options)
+        {
+            // TODO: try/catch
+            string[] optStrings = getOptionStrings(options);
+
+            string filePath = optStrings[0];
+            string data = optStrings[1];
+            int position = int.Parse(optStrings[2]);
+            string callbackId = optStrings[3];
+
+            try
+            {
+                if (string.IsNullOrEmpty(data))
+                {
+                    Debug.WriteLine("Expected some data to be send in the write command to {0}", filePath);
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId);
+                    return;
+                }
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    // create the file if not exists
+                    if (!isoFile.FileExists(filePath))
+                    {
+                        var file = isoFile.CreateFile(filePath);
+                        file.Close();
+                    }
+
+                    using (FileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.ReadWrite, isoFile))
+                    {
+                        if (0 <= position && position <= stream.Length)
+                        {
+                            stream.SetLength(position);
+                        }
+                        using (BinaryWriter writer = new BinaryWriter(stream))
+                        {
+                            writer.Seek(0, SeekOrigin.End);
+                            writer.Write(data.ToCharArray());
+                        }
+                    }
+                }
+
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, data.Length), callbackId);
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex, callbackId))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Look up metadata about this entry.
+        /// </summary>
+        /// <param name="options">filePath to entry</param>   
+        public void getMetadata(string options)
+        {
+            string[] optStings = getOptionStrings(options);
+            string filePath = optStings[0];
+            string callbackId = optStings[1];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (isoFile.FileExists(filePath))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK,
+                                new ModificationMetadata() { modificationTime = isoFile.GetLastWriteTime(filePath).DateTime.ToString() }), callbackId);
+                        }
+                        else if (isoFile.DirectoryExists(filePath))
+                        {
+                            string modTime = isoFile.GetLastWriteTime(filePath).DateTime.ToString();
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new ModificationMetadata() { modificationTime = modTime }), callbackId);
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                        }
+
+                    }
+                }
+                catch (IsolatedStorageException)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                    }
+                }
+            }
+
+        }
+
+
+        /// <summary>
+        /// Returns a File that represents the current state of the file that this FileEntry represents.
+        /// </summary>
+        /// <param name="filePath">filePath to entry</param>
+        /// <returns></returns>
+        public void getFileMetadata(string options)
+        {
+            string[] optStings = getOptionStrings(options);
+            string filePath = optStings[0];
+            string callbackId = optStings[1];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    FileMetadata metaData = new FileMetadata(filePath);
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, metaData), callbackId);
+                }
+                catch (IsolatedStorageException)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Look up the parent DirectoryEntry containing this Entry. 
+        /// If this Entry is the root of IsolatedStorage, its parent is itself.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getParent(string options)
+        {
+            string[] optStings = getOptionStrings(options);
+            string filePath = optStings[0];
+            string callbackId = optStings[1];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    if (string.IsNullOrEmpty(filePath))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId);
+                        return;
+                    }
+
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        FileEntry entry;
+
+                        if (isoFile.FileExists(filePath) || isoFile.DirectoryExists(filePath))
+                        {
+                           
+                             
+                            string path = this.GetParentDirectory(filePath);
+                            entry = FileEntry.GetEntry(path);
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry),callbackId);
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
+                        }
+
+                    }
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
+                    }
+                }
+            }
+        }
+
+        public void remove(string options)
+        {
+            string[] args = getOptionStrings(options);
+            string filePath = args[0];
+            string callbackId = args[1];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    if (filePath == "/" || filePath == "" || filePath == @"\")
+                    {
+                        throw new Exception("Cannot delete root file system") ;
+                    }
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (isoFile.FileExists(filePath))
+                        {
+                            isoFile.DeleteFile(filePath);
+                        }
+                        else
+                        {
+                            if (isoFile.DirectoryExists(filePath))
+                            {
+                                isoFile.DeleteDirectory(filePath);
+                            }
+                            else
+                            {
+                                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
+                                return;
+                            }
+                        }
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK),callbackId);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
+                    }
+                }
+            }
+        }
+
+        public void removeRecursively(string options)
+        {
+            string[] args = getOptionStrings(options);
+            string filePath = args[0];
+            string callbackId = args[1];
+
+            if (filePath != null)
+            {
+                if (string.IsNullOrEmpty(filePath))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId);
+                }
+                else
+                {
+                    if (removeDirRecursively(filePath, callbackId))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK), callbackId);
+                    }
+                }
+            }
+        }
+
+        public void readEntries(string options)
+        {
+            string[] args = getOptionStrings(options);
+            string filePath = args[0];
+            string callbackId = args[1];
+
+            if (filePath != null)
+            {
+                try
+                {
+                    if (string.IsNullOrEmpty(filePath))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId);
+                        return;
+                    }
+
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (isoFile.DirectoryExists(filePath))
+                        {
+                            string path = File.AddSlashToDirectory(filePath);
+                            List<FileEntry> entries = new List<FileEntry>();
+                            string[] files = isoFile.GetFileNames(path + "*");
+                            string[] dirs = isoFile.GetDirectoryNames(path + "*");
+                            foreach (string file in files)
+                            {
+                                entries.Add(FileEntry.GetEntry(path + file));
+                            }
+                            foreach (string dir in dirs)
+                            {
+                                entries.Add(FileEntry.GetEntry(path + dir + "/"));
+                            }
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entries),callbackId);
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
+                        }
+                    }
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
+                    }
+                }
+            }
+        }
+
+        public void requestFileSystem(string options)
+        {
+            // TODO: try/catch
+            string[] optVals = getOptionStrings(options);
+            //FileOptions fileOptions = new FileOptions();
+            int fileSystemType = int.Parse(optVals[0]);
+            double size = double.Parse(optVals[1]);
+            string callbackId = optVals[2];
+
+
+            IsolatedStorageFile.GetUserStoreForApplication();
+
+            if (size > (10 * 1024 * 1024)) // 10 MB, compier will clean this up!
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, QUOTA_EXCEEDED_ERR), callbackId);
+                return;
+            }
+
+            try
+            {
+                if (size != 0)
+                {
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        long availableSize = isoFile.AvailableFreeSpace;
+                        if (size > availableSize)
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, QUOTA_EXCEEDED_ERR), callbackId);
+                            return;
+                        }
+                    }
+                }
+
+                if (fileSystemType == PERSISTENT)
+                {
+                    // TODO: this should be in it's own folder to prevent overwriting of the app assets, which are also in ISO
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("persistent", FileEntry.GetEntry("/"))), callbackId);
+                }
+                else if (fileSystemType == TEMPORARY)
+                {
+                    using (IsolatedStorageFile isoStorage = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (!isoStorage.FileExists(TMP_DIRECTORY_NAME))
+                        {
+                            isoStorage.CreateDirectory(TMP_DIRECTORY_NAME);
+                        }
+                    }
+
+                    string tmpFolder = "/" + TMP_DIRECTORY_NAME + "/";
+
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("temporary", FileEntry.GetEntry(tmpFolder))), callbackId);
+                }
+                else if (fileSystemType == RESOURCE)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("resource")), callbackId);
+                }
+                else if (fileSystemType == APPLICATION)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("application")), callbackId);
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
+                }
+
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
+                }
+            }
+        }
+
+        public void resolveLocalFileSystemURI(string options)
+        {
+
+            string[] optVals = getOptionStrings(options);
+            string uri = optVals[0].Split('?')[0];
+            string callbackId = optVals[1];
+
+            if (uri != null)
+            {
+                // a single '/' is valid, however, '/someDir' is not, but '/tmp//somedir' and '///someDir' are valid
+                if (uri.StartsWith("/") && uri.IndexOf("//") < 0 && uri != "/")
+                {
+                     DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
+                     return;
+                }
+                try
+                {
+                    // fix encoded spaces
+                    string path = Uri.UnescapeDataString(uri);
+
+                    FileEntry uriEntry = FileEntry.GetEntry(path);
+                    if (uriEntry != null)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, uriEntry), callbackId);
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    if (!this.HandleException(ex, callbackId))
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
+                    }
+                }
+            }
+        }
+
+        public void copyTo(string options)
+        {
+            TransferTo(options, false);
+        }
+
+        public void moveTo(string options)
+        {
+            TransferTo(options, true);
+        }
+
+        public void getFile(string options)
+        {
+            GetFileOrDirectory(options, false);
+        }
+
+        public void getDirectory(string options)
+        {
+            GetFileOrDirectory(options, true);
+        }
+
+        #region internal functionality
+
+        /// <summary>
+        /// Retrieves the parent directory name of the specified path,
+        /// </summary>
+        /// <param name="path">Path</param>
+        /// <returns>Parent directory name</returns>
+        private string GetParentDirectory(string path)
+        {
+            if (String.IsNullOrEmpty(path) || path == "/")
+            {
+                return "/";
+            }
+
+            if (path.EndsWith(@"/") || path.EndsWith(@"\"))
+            {
+                return this.GetParentDirectory(Path.GetDirectoryName(path));
+            }
+
+            string result = Path.GetDirectoryName(path);
+            if (result == null)
+            {
+                result = "/";
+            }
+
+            return result;
+        }
+
+        private bool removeDirRecursively(string fullPath,string callbackId)
+        {
+            try
+            {
+                if (fullPath == "/")
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
+                    return false;
+                }
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    if (isoFile.DirectoryExists(fullPath))
+                    {
+                        string tempPath = File.AddSlashToDirectory(fullPath);
+                        string[] files = isoFile.GetFileNames(tempPath + "*");
+                        if (files.Length > 0)
+                        {
+                            foreach (string file in files)
+                            {
+                                isoFile.DeleteFile(tempPath + file);
+                            }
+                        }
+                        string[] dirs = isoFile.GetDirectoryNames(tempPath + "*");
+                        if (dirs.Length > 0)
+                        {
+                            foreach (string dir in dirs)
+                            {
+                                if (!removeDirRecursively(tempPath + dir, callbackId))
+                                {
+                                    return false;
+                                }
+                            }
+                        }
+                        isoFile.DeleteDirectory(fullPath);
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private bool CanonicalCompare(string pathA, string pathB)
+        {
+            string a = pathA.Replace("//", "/");
+            string b = pathB.Replace("//", "/");
+
+            return a.Equals(b, StringComparison.OrdinalIgnoreCase);
+        }
+
+        /*
+         *  copyTo:["fullPath","parent", "newName"],
+         *  moveTo:["fullPath","parent", "newName"],
+         */
+        private void TransferTo(string options, bool move)
+        {
+            // TODO: try/catch
+            string[] optStrings = getOptionStrings(options);
+            string fullPath = optStrings[0];
+            string parent = optStrings[1];
+            string newFileName = optStrings[2];
+            string callbackId = optStrings[3];
+
+            char[] invalids = Path.GetInvalidPathChars();
+            
+            if (newFileName.IndexOfAny(invalids) > -1 || newFileName.IndexOf(":") > -1 )
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
+                return;
+            }
+
+            try
+            {
+                if ((parent == null) || (string.IsNullOrEmpty(parent)) || (string.IsNullOrEmpty(fullPath)))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    return;
+                }
+
+                string parentPath = File.AddSlashToDirectory(parent);
+                string currentPath = fullPath;
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    bool isFileExist = isoFile.FileExists(currentPath);
+                    bool isDirectoryExist = isoFile.DirectoryExists(currentPath);
+                    bool isParentExist = isoFile.DirectoryExists(parentPath);
+
+                    if ( ( !isFileExist && !isDirectoryExist ) || !isParentExist )
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                        return;
+                    }
+                    string newName;
+                    string newPath;
+                    if (isFileExist)
+                    {
+                        newName = (string.IsNullOrEmpty(newFileName))
+                                    ? Path.GetFileName(currentPath)
+                                    : newFileName;
+
+                        newPath = Path.Combine(parentPath, newName);
+                        
+                        // sanity check ..
+                        // cannot copy file onto itself
+                        if (CanonicalCompare(newPath,currentPath)) //(parent + newFileName))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId);
+                            return;
+                        }
+                        else if (isoFile.DirectoryExists(newPath)) 
+                        {
+                            // there is already a folder with the same name, operation is not allowed
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId);
+                            return;
+                        }
+                        else if (isoFile.FileExists(newPath))
+                        {   // remove destination file if exists, in other case there will be exception
+                            isoFile.DeleteFile(newPath);
+                        }
+
+                        if (move)
+                        {
+                            isoFile.MoveFile(currentPath, newPath);
+                        }
+                        else
+                        {
+                            isoFile.CopyFile(currentPath, newPath, true);
+                        }
+                    }
+                    else
+                    {
+                        newName = (string.IsNullOrEmpty(newFileName))
+                                    ? currentPath
+                                    : newFileName;
+
+                        newPath = Path.Combine(parentPath, newName);
+
+                        if (move)
+                        {
+                            // remove destination directory if exists, in other case there will be exception
+                            // target directory should be empty
+                            if (!newPath.Equals(currentPath) && isoFile.DirectoryExists(newPath))
+                            {
+                                isoFile.DeleteDirectory(newPath);
+                            }
+
+                            isoFile.MoveDirectory(currentPath, newPath);
+                        }
+                        else
+                        {
+                            CopyDirectory(currentPath, newPath, isoFile);
+                        }
+                    }
+                    FileEntry entry = FileEntry.GetEntry(newPath);
+                    if (entry != null)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId);
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    }
+                }
+
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex, callbackId))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
+                }
+            }
+        }
+
+        private bool HandleException(Exception ex, string cbId="")
+        {
+            bool handled = false;
+            string callbackId = String.IsNullOrEmpty(cbId) ? this.CurrentCommandCallbackId : cbId;
+            if (ex is SecurityException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR), callbackId);
+                handled = true;
+            }
+            else if (ex is FileNotFoundException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                handled = true;
+            }
+            else if (ex is ArgumentException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
+                handled = true;
+            }
+            else if (ex is IsolatedStorageException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId);
+                handled = true;
+            }
+            else if (ex is DirectoryNotFoundException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                handled = true;
+            }
+            return handled;
+        }
+
+        private void CopyDirectory(string sourceDir, string destDir, IsolatedStorageFile isoFile)
+        {
+            string path = File.AddSlashToDirectory(sourceDir);
+
+            bool bExists = isoFile.DirectoryExists(destDir);
+
+            if (!bExists)
+            {
+                isoFile.CreateDirectory(destDir);
+            }
+
+            destDir = File.AddSlashToDirectory(destDir);
+               
+            string[] files = isoFile.GetFileNames(path + "*");
+                
+            if (files.Length > 0)
+            {
+                foreach (string file in files)
+                {
+                    isoFile.CopyFile(path + file, destDir + file,true);
+                }
+            }
+            string[] dirs = isoFile.GetDirectoryNames(path + "*");
+            if (dirs.Length > 0)
+            {
+                foreach (string dir in dirs)
+                {
+                    CopyDirectory(path + dir, destDir + dir, isoFile);
+                }
+            }
+        }
+
+        private void GetFileOrDirectory(string options, bool getDirectory)
+        {
+            FileOptions fOptions = new FileOptions();
+            string[] args = getOptionStrings(options);
+
+            fOptions.FullPath = args[0];
+            fOptions.Path = args[1];
+
+            string callbackId = args[3];
+
+            try
+            {
+                fOptions.CreatingOpt = JSON.JsonHelper.Deserialize<CreatingOptions>(args[2]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId);
+                return;
+            }
+
+            try
+            {
+                if ((string.IsNullOrEmpty(fOptions.Path)) || (string.IsNullOrEmpty(fOptions.FullPath)))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    return;
+                }
+
+                string path;
+
+                if (fOptions.Path.Split(':').Length > 2)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
+                    return;
+                }
+
+                try
+                {
+                    path = Path.Combine(fOptions.FullPath + "/", fOptions.Path);
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
+                    return;
+                }        
+
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    bool isFile = isoFile.FileExists(path);
+                    bool isDirectory = isoFile.DirectoryExists(path);
+                    bool create = (fOptions.CreatingOpt == null) ? false : fOptions.CreatingOpt.Create;
+                    bool exclusive = (fOptions.CreatingOpt == null) ? false : fOptions.CreatingOpt.Exclusive;
+                    if (create)
+                    {
+                        if (exclusive && (isoFile.FileExists(path) || isoFile.DirectoryExists(path)))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, PATH_EXISTS_ERR), callbackId);
+                            return;
+                        }
+
+                        // need to make sure the parent exists
+                        // it is an error to create a directory whose immediate parent does not yet exist
+			            // see issue: https://issues.apache.org/jira/browse/CB-339
+                        string[] pathParts = path.Split('/');
+                        string builtPath = pathParts[0];
+                        for (int n = 1; n < pathParts.Length - 1; n++)
+                        {
+                            builtPath += "/" + pathParts[n];
+                            if (!isoFile.DirectoryExists(builtPath))
+                            {
+                                Debug.WriteLine(String.Format("Error :: Parent folder \"{0}\" does not exist, when attempting to create \"{1}\"",builtPath,path));
+                                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                                return;
+                            }
+                        }
+
+                        if ((getDirectory) && (!isDirectory))
+                        {
+                            isoFile.CreateDirectory(path);
+                        }
+                        else
+                        {
+                            if ((!getDirectory) && (!isFile))
+                            {
+
+                                IsolatedStorageFileStream fileStream = isoFile.CreateFile(path);
+                                fileStream.Close();
+                            }
+                        }
+                    }
+                    else // (not create)
+                    {
+                        if ((!isFile) && (!isDirectory))
+                        {
+                            if (path.IndexOf("//www") == 0)
+                            {
+                                Uri fileUri = new Uri(path.Remove(0,2), UriKind.Relative);
+                                StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri);
+                                if (streamInfo != null)
+                                {
+                                    FileEntry _entry = FileEntry.GetEntry(fileUri.OriginalString,true);
+
+                                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, _entry), callbackId);
+
+                                    //using (BinaryReader br = new BinaryReader(streamInfo.Stream))
+                                    //{
+                                    //    byte[] data = br.ReadBytes((int)streamInfo.Stream.Length);
+                                   
+                                    //}
+
+                                }
+                                else
+                                {
+                                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                                }
+
+
+                            }
+                            else
+                            {
+                                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                            }
+                            return;
+                        }
+                        if (((getDirectory) && (!isDirectory)) || ((!getDirectory) && (!isFile)))
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, TYPE_MISMATCH_ERR), callbackId);
+                            return;
+                        }
+                    }
+                    FileEntry entry = FileEntry.GetEntry(path);
+                    if (entry != null)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId);
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                if (!this.HandleException(ex))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
+                }
+            }
+        }
+
+        private static string AddSlashToDirectory(string dirPath)
+        {
+            if (dirPath.EndsWith("/"))
+            {
+                return dirPath;
+            }
+            else
+            {
+                return dirPath + "/";
+            }
+        }
+
+        /// <summary>
+        /// Returns file content in a form of base64 string
+        /// </summary>
+        /// <param name="stream">File stream</param>
+        /// <returns>Base64 representation of the file</returns>
+        private string GetFileContent(Stream stream)
+        {
+            int streamLength = (int)stream.Length;
+            byte[] fileData = new byte[streamLength + 1];
+            stream.Read(fileData, 0, streamLength);
+            stream.Close();
+            return Convert.ToBase64String(fileData);
+        }
+
+        #endregion
+
+    }
+}


[16/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/www/css/index.css
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/www/css/index.css b/lib/cordova-wp8/templates/standalone/www/css/index.css
new file mode 100644
index 0000000..f1f9d76
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+}
+
+body {
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/www/img/logo.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/www/img/logo.png b/lib/cordova-wp8/templates/standalone/www/img/logo.png
new file mode 100644
index 0000000..9519e7d
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/www/img/logo.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/www/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/www/index.html b/lib/cordova-wp8/templates/standalone/www/index.html
new file mode 100644
index 0000000..31f61f4
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/www/index.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!--
+       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.
+-->
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova-2.7.0.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/www/js/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/www/js/index.js b/lib/cordova-wp8/templates/standalone/www/js/index.js
new file mode 100644
index 0000000..3b75d3f
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // `load`, `deviceready`, `offline`, and `online`.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of `this` is the event. In order to call the `receivedEvent`
+    // function, we must explicity call `app.receivedEvent(...);`
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/vs/MyTemplateStandAlone.vstemplate
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/vs/MyTemplateStandAlone.vstemplate b/lib/cordova-wp8/templates/vs/MyTemplateStandAlone.vstemplate
new file mode 100644
index 0000000..d7e4a70
--- /dev/null
+++ b/lib/cordova-wp8/templates/vs/MyTemplateStandAlone.vstemplate
@@ -0,0 +1,113 @@
+<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
+  <TemplateData>
+    <Name>CordovaWP8_2_7_0</Name>
+    <Description>Cordova 2.7.0 for Windows Phone 8 using the Cordova source code directly.</Description>
+    <ProjectType>CSharp</ProjectType>
+    <ProjectSubType>
+    </ProjectSubType>
+    <SortOrder>1000</SortOrder>
+    <CreateNewFolder>true</CreateNewFolder>
+    <DefaultName>CordovaWP8_2_7_0</DefaultName>
+    <ProvideDefaultName>true</ProvideDefaultName>
+    <LocationField>Enabled</LocationField>
+    <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
+    <Icon>__TemplateIcon.png</Icon>
+    <PreviewImage>__PreviewImage.jpg</PreviewImage>
+  </TemplateData>
+  <TemplateContent>
+    <Project TargetFileName="CordovaAppProj.csproj" File="CordovaAppProj.csproj" ReplaceParameters="true">
+      <ProjectItem ReplaceParameters="true" TargetFileName="App.xaml">App.xaml</ProjectItem>
+      <ProjectItem ReplaceParameters="true" TargetFileName="App.xaml.cs">App.xaml.cs</ProjectItem>
+      <ProjectItem ReplaceParameters="false" TargetFileName="ApplicationIcon.png">ApplicationIcon.png</ProjectItem>
+      <ProjectItem ReplaceParameters="false" TargetFileName="Background.png">Background.png</ProjectItem>
+      <ProjectItem ReplaceParameters="true" TargetFileName="config.xml">config.xml</ProjectItem>
+      <Folder Name="cordovalib" TargetFolderName="cordovalib">
+        <ProjectItem ReplaceParameters="true" TargetFileName="BrowserMouseHelper.cs">BrowserMouseHelper.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="CommandFactory.cs">CommandFactory.cs</ProjectItem>
+        <Folder Name="Commands" TargetFolderName="Commands">
+          <ProjectItem ReplaceParameters="true" TargetFileName="BaseCommand.cs">BaseCommand.cs</ProjectItem>
+        </Folder>
+        <ProjectItem ReplaceParameters="true" TargetFileName="ConfigHandler.cs">ConfigHandler.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="CordovaCommandCall.cs">CordovaCommandCall.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="CordovaView.xaml">CordovaView.xaml</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="CordovaView.xaml.cs">CordovaView.xaml.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="DOMStorageHelper.cs">DOMStorageHelper.cs</ProjectItem>
+        <Folder Name="JSON" TargetFolderName="JSON">
+          <ProjectItem ReplaceParameters="true" TargetFileName="JsonHelper.cs">JsonHelper.cs</ProjectItem>
+        </Folder>
+        <ProjectItem ReplaceParameters="true" TargetFileName="NativeExecution.cs">NativeExecution.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="OrientationHelper.cs">OrientationHelper.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="PluginResult.cs">PluginResult.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="ScriptCallback.cs">ScriptCallback.cs</ProjectItem>
+      </Folder>
+      <Folder Name="Images" TargetFolderName="Images">
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.back.rest.png">appbar.back.rest.png</ProjectItem>
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.close.rest.png">appbar.close.rest.png</ProjectItem>
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.feature.video.rest.png">appbar.feature.video.rest.png</ProjectItem>
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.next.rest.png">appbar.next.rest.png</ProjectItem>
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.save.rest.png">appbar.save.rest.png</ProjectItem>
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.stop.rest.png">appbar.stop.rest.png</ProjectItem>
+      </Folder>
+      <ProjectItem ReplaceParameters="true" TargetFileName="MainPage.xaml">MainPage.xaml</ProjectItem>
+      <ProjectItem ReplaceParameters="true" TargetFileName="MainPage.xaml.cs">MainPage.xaml.cs</ProjectItem>
+      <Folder Name="Plugins" TargetFolderName="Plugins">
+        <ProjectItem ReplaceParameters="true" TargetFileName="Accelerometer.cs">Accelerometer.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="AudioFormatsHelper.cs">AudioFormatsHelper.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="AudioPlayer.cs">AudioPlayer.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Battery.cs">Battery.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Camera.cs">Camera.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Capture.cs">Capture.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Compass.cs">Compass.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Contacts.cs">Contacts.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="DebugConsole.cs">DebugConsole.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Device.cs">Device.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="File.cs">File.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="FileTransfer.cs">FileTransfer.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="GeoLocation.cs">GeoLocation.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Globalization.cs">Globalization.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="ImageExifHelper.cs">ImageExifHelper.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="InAppBrowser.cs">InAppBrowser.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Media.cs">Media.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="MimeTypeMapper.cs">MimeTypeMapper.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="NetworkStatus.cs">NetworkStatus.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Notification.cs">Notification.cs</ProjectItem>
+        <Folder Name="UI" TargetFolderName="UI">
+          <ProjectItem ReplaceParameters="true" TargetFileName="AudioCaptureTask.cs">AudioCaptureTask.cs</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="AudioRecorder.xaml">AudioRecorder.xaml</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="AudioRecorder.xaml.cs">AudioRecorder.xaml.cs</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="ImageCapture.xaml">ImageCapture.xaml</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="ImageCapture.xaml.cs">ImageCapture.xaml.cs</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="NotificationBox.xaml">NotificationBox.xaml</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="NotificationBox.xaml.cs">NotificationBox.xaml.cs</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="VideoCaptureTask.cs">VideoCaptureTask.cs</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="VideoRecorder.xaml">VideoRecorder.xaml</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="VideoRecorder.xaml.cs">VideoRecorder.xaml.cs</ProjectItem>
+        </Folder>
+      </Folder>
+      <Folder Name="Properties" TargetFolderName="Properties">
+        <ProjectItem ReplaceParameters="true" TargetFileName="AppManifest.xml">AppManifest.xml</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="AssemblyInfo.cs">AssemblyInfo.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="WMAppManifest.xml">WMAppManifest.xml</ProjectItem>
+      </Folder>
+      <Folder Name="resources" TargetFolderName="resources">
+        <ProjectItem ReplaceParameters="false" TargetFileName="notification-beep.wav">notification-beep.wav</ProjectItem>
+      </Folder>
+      <Folder Name="Service References" TargetFolderName="Service References" />
+      <ProjectItem ReplaceParameters="false" TargetFileName="SplashScreenImage.jpg">SplashScreenImage.jpg</ProjectItem>
+      <ProjectItem ReplaceParameters="false" TargetFileName="VERSION">VERSION</ProjectItem>
+      <Folder Name="www" TargetFolderName="www">
+        <ProjectItem ReplaceParameters="true" TargetFileName="cordova-2.7.0.js">cordova-2.7.0.js</ProjectItem>
+        <Folder Name="css" TargetFolderName="css">
+          <ProjectItem ReplaceParameters="true" TargetFileName="index.css">index.css</ProjectItem>
+        </Folder>
+        <Folder Name="img" TargetFolderName="img">
+          <ProjectItem ReplaceParameters="false" TargetFileName="logo.png">logo.png</ProjectItem>
+        </Folder>
+        <ProjectItem ReplaceParameters="true" TargetFileName="index.html">index.html</ProjectItem>
+        <Folder Name="js" TargetFolderName="js">
+          <ProjectItem ReplaceParameters="true" TargetFileName="index.js">index.js</ProjectItem>
+        </Folder>
+      </Folder>
+    </Project>
+  </TemplateContent>
+</VSTemplate>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/vs/description.txt
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/vs/description.txt b/lib/cordova-wp8/templates/vs/description.txt
new file mode 100644
index 0000000..b8c91ec
--- /dev/null
+++ b/lib/cordova-wp8/templates/vs/description.txt
@@ -0,0 +1,8 @@
+CordovaWP8_2_6_0_Full
+Apache Cordova 2.7.0 Windows Phone 8 App using a pre-built dll.
+
+CordovaWP8_2_6_0_StandAlone
+Apache Cordova 2.7.0 Windows Phone 8 App including all framework source code.
+
+
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/vs/pg_templateIcon.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/vs/pg_templateIcon.png b/lib/cordova-wp8/templates/vs/pg_templateIcon.png
new file mode 100644
index 0000000..75c17c7
Binary files /dev/null and b/lib/cordova-wp8/templates/vs/pg_templateIcon.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/vs/pg_templatePreview.jpg
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/vs/pg_templatePreview.jpg b/lib/cordova-wp8/templates/vs/pg_templatePreview.jpg
new file mode 100644
index 0000000..1d72941
Binary files /dev/null and b/lib/cordova-wp8/templates/vs/pg_templatePreview.jpg differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/App.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/App.xaml b/lib/cordova-wp8/tests/MobileSpecUnitTests/App.xaml
new file mode 100644
index 0000000..ac01322
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/App.xaml
@@ -0,0 +1,38 @@
+<!--
+ 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. 
+-->
+<Application 
+    x:Class="MobileSpecUnitTests.App"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"       
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone">
+
+    <!--Application Resources-->
+    <Application.Resources>
+        <MediaElement x:Key="PhoneGapMediaPlayer" Visibility="Collapsed" />        
+    </Application.Resources>
+
+    <Application.ApplicationLifetimeObjects>
+        <!--Required object that handles lifetime events for the application-->
+        <shell:PhoneApplicationService 
+            Launching="Application_Launching" Closing="Application_Closing" 
+            Activated="Application_Activated" Deactivated="Application_Deactivated"/>
+    </Application.ApplicationLifetimeObjects>
+
+</Application>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/App.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/App.xaml.cs b/lib/cordova-wp8/tests/MobileSpecUnitTests/App.xaml.cs
new file mode 100644
index 0000000..53d9a90
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/App.xaml.cs
@@ -0,0 +1,158 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Shell;
+
+namespace MobileSpecUnitTests
+{
+    public partial class App : Application
+    {
+        /// <summary>
+        /// Provides easy access to the root frame of the Phone Application.
+        /// </summary>
+        /// <returns>The root frame of the Phone Application.</returns>
+        public PhoneApplicationFrame RootFrame { get; private set; }
+
+        /// <summary>
+        /// Constructor for the Application object.
+        /// </summary>
+        public App()
+        {
+            // Global handler for uncaught exceptions. 
+            UnhandledException += Application_UnhandledException;
+
+            // Show graphics profiling information while debugging.
+            if (System.Diagnostics.Debugger.IsAttached)
+            {
+                // Display the current frame rate counters.
+                Application.Current.Host.Settings.EnableFrameRateCounter = true;
+
+                // Show the areas of the app that are being redrawn in each frame.
+                //Application.Current.Host.Settings.EnableRedrawRegions = true;
+
+                // Enable non-production analysis visualization mode, 
+                // which shows areas of a page that are being GPU accelerated with a colored overlay.
+                //Application.Current.Host.Settings.EnableCacheVisualization = true;
+            }
+
+            // Standard Silverlight initialization
+            InitializeComponent();
+
+            // Phone-specific initialization
+            InitializePhoneApplication();
+        }
+
+        // Code to execute when the application is launching (eg, from Start)
+        // This code will not execute when the application is reactivated
+        private void Application_Launching(object sender, LaunchingEventArgs e)
+        {
+            System.Diagnostics.Debug.WriteLine("Application_Launching");
+        }
+
+        // Code to execute when the application is activated (brought to foreground)
+        // This code will not execute when the application is first launched
+        private void Application_Activated(object sender, ActivatedEventArgs e)
+        {
+            System.Diagnostics.Debug.WriteLine("Application_Activated");
+        }
+
+        // Code to execute when the application is deactivated (sent to background)
+        // This code will not execute when the application is closing
+        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
+        {
+            System.Diagnostics.Debug.WriteLine("Application_Deactivated");
+        }
+
+        // Code to execute when the application is closing (eg, user hit Back)
+        // This code will not execute when the application is deactivated
+        private void Application_Closing(object sender, ClosingEventArgs e)
+        {
+            System.Diagnostics.Debug.WriteLine("Application_Closing");
+        }
+
+        // Code to execute if a navigation fails
+        private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
+        {
+            if (System.Diagnostics.Debugger.IsAttached)
+            {
+                // A navigation has failed; break into the debugger
+                System.Diagnostics.Debugger.Break();
+            }
+        }
+
+        // Code to execute on Unhandled Exceptions
+        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
+        {
+            if (System.Diagnostics.Debugger.IsAttached)
+            {
+                // An unhandled exception has occurred; break into the debugger
+                System.Diagnostics.Debugger.Break();
+            }
+        }
+
+        #region Phone application initialization
+
+        // Avoid double-initialization
+        private bool phoneApplicationInitialized = false;
+
+        // Do not add any additional code to this method
+        private void InitializePhoneApplication()
+        {
+            if (phoneApplicationInitialized)
+                return;
+
+            // Create the frame but don't set it as RootVisual yet; this allows the splash
+            // screen to remain active until the application is ready to render.
+            RootFrame = new PhoneApplicationFrame();
+            RootFrame.Navigated += CompleteInitializePhoneApplication;
+
+            // Handle navigation failures
+            RootFrame.NavigationFailed += RootFrame_NavigationFailed;
+
+            // Ensure we don't initialize again
+            phoneApplicationInitialized = true;
+        }
+
+        // Do not add any additional code to this method
+        private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
+        {
+            // Set the root visual to allow the application to render
+            if (RootVisual != RootFrame)
+                RootVisual = RootFrame;
+
+            // Remove this handler since it is no longer needed
+            RootFrame.Navigated -= CompleteInitializePhoneApplication;
+        }
+
+        #endregion
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/ApplicationIcon.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/ApplicationIcon.png b/lib/cordova-wp8/tests/MobileSpecUnitTests/ApplicationIcon.png
new file mode 100644
index 0000000..6b69046
Binary files /dev/null and b/lib/cordova-wp8/tests/MobileSpecUnitTests/ApplicationIcon.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/Background.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/Background.png b/lib/cordova-wp8/tests/MobileSpecUnitTests/Background.png
new file mode 100644
index 0000000..2667df4
Binary files /dev/null and b/lib/cordova-wp8/tests/MobileSpecUnitTests/Background.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/MainPage.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/MainPage.xaml b/lib/cordova-wp8/tests/MobileSpecUnitTests/MainPage.xaml
new file mode 100644
index 0000000..4b39b4e
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/MainPage.xaml
@@ -0,0 +1,52 @@
+<!--
+ 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. 
+-->
+<phone:PhoneApplicationPage 
+    x:Class="MobileSpecUnitTests.MainPage"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    xmlns:my="clr-namespace:WPCordovaClassLib;assembly=WPCordovaClassLib"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    SupportedOrientations="Portrait" Orientation="Portrait"
+    mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
+    shell:SystemTray.IsVisible="True">
+
+    <!--LayoutRoot is the root grid where all page content is placed-->
+    <Grid x:Name="LayoutRoot" Background="Transparent">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+        
+        <my:CordovaView Name="PGView" 
+                        VerticalAlignment="Stretch"
+                        HorizontalAlignment="Stretch" 
+                        Margin="0,0,0,0">
+        </my:CordovaView>       
+        
+
+    </Grid>
+ 
+
+
+</phone:PhoneApplicationPage>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/MainPage.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/MainPage.xaml.cs b/lib/cordova-wp8/tests/MobileSpecUnitTests/MainPage.xaml.cs
new file mode 100644
index 0000000..a81888b
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/MainPage.xaml.cs
@@ -0,0 +1,42 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+
+namespace MobileSpecUnitTests
+{
+    public partial class MainPage : PhoneApplicationPage
+    {
+        public MainPage()
+        {
+            InitializeComponent();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/MobileSpecUnitTests.csproj
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/MobileSpecUnitTests.csproj b/lib/cordova-wp8/tests/MobileSpecUnitTests/MobileSpecUnitTests.csproj
new file mode 100644
index 0000000..6c8ce71
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/MobileSpecUnitTests.csproj
@@ -0,0 +1,247 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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. 
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>10.0.20506</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}</ProjectGuid>
+    <ProjectTypeGuids>{C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>MobileSpecUnitTests</RootNamespace>
+    <AssemblyName>MobileSpecUnitTests</AssemblyName>
+    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
+    <SilverlightVersion>
+    </SilverlightVersion>
+    <TargetFrameworkProfile>
+    </TargetFrameworkProfile>
+    <TargetFrameworkIdentifier>WindowsPhone</TargetFrameworkIdentifier>
+    <SilverlightApplication>true</SilverlightApplication>
+    <SupportedCultures>
+    </SupportedCultures>
+    <XapOutputs>true</XapOutputs>
+    <GenerateSilverlightManifest>true</GenerateSilverlightManifest>
+    <XapFilename>MobileSpecUnitTests_$(Configuration)_$(Platform).xap</XapFilename>
+    <SilverlightManifestTemplate>Properties\AppManifest.xml</SilverlightManifestTemplate>
+    <SilverlightAppEntry>MobileSpecUnitTests.App</SilverlightAppEntry>
+    <ValidateXaml>true</ValidateXaml>
+    <ThrowErrorsInValidation>true</ThrowErrorsInValidation>
+    <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>Bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>Bin\Release</OutputPath>
+    <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>Bin\x86\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>full</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Optimize>false</Optimize>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <OutputPath>Bin\x86\Release</OutputPath>
+    <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <Optimize>true</Optimize>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>Bin\ARM\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>full</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+    <Optimize>false</Optimize>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
+    <OutputPath>Bin\ARM\Release</OutputPath>
+    <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <Optimize>true</Optimize>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="App.xaml.cs">
+      <DependentUpon>App.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="MainPage.xaml.cs">
+      <DependentUpon>MainPage.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="App.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </ApplicationDefinition>
+    <Page Include="MainPage.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Properties\AppManifest.xml" />
+    <None Include="Properties\WMAppManifest.xml">
+      <SubType>Designer</SubType>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="ApplicationIcon.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Background.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="www\accelerometer\index.html" />
+    <Content Include="www\audio\index.html" />
+    <Content Include="www\autotest\html\HtmlReporter.js" />
+    <Content Include="www\autotest\html\HtmlReporterHelpers.js" />
+    <Content Include="www\autotest\html\ReporterView.js" />
+    <Content Include="www\autotest\html\SpecView.js" />
+    <Content Include="www\autotest\html\SuiteView.js" />
+    <Content Include="www\autotest\html\TrivialReporter.js" />
+    <Content Include="www\autotest\index.html" />
+    <Content Include="www\autotest\jasmine.css" />
+    <Content Include="www\autotest\jasmine.js" />
+    <Content Include="www\autotest\pages\accelerometer.html" />
+    <Content Include="www\autotest\pages\all.html" />
+    <Content Include="www\autotest\pages\battery.html" />
+    <Content Include="www\autotest\pages\bridge.html" />
+    <Content Include="www\autotest\pages\camera.html" />
+    <Content Include="www\autotest\pages\capture.html" />
+    <Content Include="www\autotest\pages\compass.html" />
+    <Content Include="www\autotest\pages\contacts.html" />
+    <Content Include="www\autotest\pages\datauri.html" />
+    <Content Include="www\autotest\pages\device.html" />
+    <Content Include="www\autotest\pages\file.html" />
+    <Content Include="www\autotest\pages\filetransfer.html" />
+    <Content Include="www\autotest\pages\geolocation.html" />
+    <Content Include="www\autotest\pages\globalization.html" />
+    <Content Include="www\autotest\pages\media.html" />
+    <Content Include="www\autotest\pages\network.html" />
+    <Content Include="www\autotest\pages\notification.html" />
+    <Content Include="www\autotest\pages\platform.html" />
+    <Content Include="www\autotest\pages\storage.html" />
+    <Content Include="www\autotest\test-runner.js" />
+    <Content Include="www\autotest\tests\accelerometer.tests.js" />
+    <Content Include="www\autotest\tests\battery.tests.js" />
+    <Content Include="www\autotest\tests\bridge.tests.js" />
+    <Content Include="www\autotest\tests\camera.tests.js" />
+    <Content Include="www\autotest\tests\capture.tests.js" />
+    <Content Include="www\autotest\tests\compass.tests.js" />
+    <Content Include="www\autotest\tests\contacts.tests.js" />
+    <Content Include="www\autotest\tests\datauri.tests.js" />
+    <Content Include="www\autotest\tests\device.tests.js" />
+    <Content Include="www\autotest\tests\file.tests.js" />
+    <Content Include="www\autotest\tests\filetransfer.tests.js" />
+    <Content Include="www\autotest\tests\geolocation.tests.js" />
+    <Content Include="www\autotest\tests\globalization.tests.js" />
+    <Content Include="www\autotest\tests\media.tests.js" />
+    <Content Include="www\autotest\tests\network.tests.js" />
+    <Content Include="www\autotest\tests\notification.tests.js" />
+    <Content Include="www\autotest\tests\platform.tests.js" />
+    <Content Include="www\autotest\tests\storage.tests.js" />
+    <Content Include="www\battery\index.html" />
+    <Content Include="www\camera\index.html" />
+    <Content Include="www\compass\index.html" />
+    <Content Include="www\contacts\index.html" />
+    <Content Include="www\cordova-2.5.0.js" />
+    <Content Include="www\cordova.js" />
+    <Content Include="www\events\index.html" />
+    <Content Include="www\index.html">
+      <SubType>Designer</SubType>
+    </Content>
+    <Content Include="www\location\index.html" />
+    <Content Include="www\main.js" />
+    <Content Include="www\master.css" />
+    <Content Include="SplashScreenImage.jpg" />
+    <Content Include="www\misc\index.html" />
+    <Content Include="www\misc\page2.html" />
+    <Content Include="www\network\index.html" />
+    <Content Include="www\notification\index.html" />
+    <Content Include="www\sql\index.html" />
+    <Content Include="www\storage\index.html" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\framework\WPCordovaClassLib.csproj">
+      <Project>{FC6A1A70-892D-46AD-9E4A-9793F72AF780}</Project>
+      <Name>WPCordovaClassLib</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="www\execbenchmark\" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).$(TargetFrameworkVersion).Overrides.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions />
+  <PropertyGroup>
+  </PropertyGroup>
+</Project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/MobileSpecUnitTests.sln
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/MobileSpecUnitTests.sln b/lib/cordova-wp8/tests/MobileSpecUnitTests/MobileSpecUnitTests.sln
new file mode 100644
index 0000000..20257da
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/MobileSpecUnitTests.sln
@@ -0,0 +1,44 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MobileSpecUnitTests", "MobileSpecUnitTests.csproj", "{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPCordovaClassLib", "..\..\framework\WPCordovaClassLib.csproj", "{FC6A1A70-892D-46AD-9E4A-9793F72AF780}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|ARM = Debug|ARM
+		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|ARM = Release|ARM
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}.Release|Any CPU.Deploy.0 = Release|Any CPU
+		{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}.Release|ARM.ActiveCfg = Release|Any CPU
+		{86DC5E2F-4AEE-42E3-9471-A082B9BEBEAC}.Release|x86.ActiveCfg = Release|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|ARM.ActiveCfg = Debug|ARM
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|ARM.Build.0 = Debug|ARM
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|x86.ActiveCfg = Debug|x86
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|x86.Build.0 = Debug|x86
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|Any CPU.Build.0 = Release|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|ARM.ActiveCfg = Release|ARM
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|ARM.Build.0 = Release|ARM
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|x86.ActiveCfg = Release|x86
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|x86.Build.0 = Release|x86
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/AppManifest.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/AppManifest.xml b/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/AppManifest.xml
new file mode 100644
index 0000000..6712a11
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/AppManifest.xml
@@ -0,0 +1,6 @@
+<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+>
+    <Deployment.Parts>
+    </Deployment.Parts>
+</Deployment>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/AssemblyInfo.cs b/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..5acaa9c
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MobileSpecUnitTests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("MobileSpecUnitTests")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("ce0ba3ac-f23b-47d7-b37b-09bdd19ebefe")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/WMAppManifest.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/WMAppManifest.xml b/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/WMAppManifest.xml
new file mode 100644
index 0000000..009cc2e
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/Properties/WMAppManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2012/deployment" AppPlatformVersion="8.0">
+  <DefaultLanguage xmlns="" code="" />
+  <App xmlns="" ProductID="{f2c6c498-143c-44e6-b107-2f71bf3e08b9}" Title="MobileSpecUnitTests" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" Author="MobileSpecUnitTests author" Description="Sample description" Publisher="MobileSpecUnitTests" PublisherID="{8a4bc540-418c-471e-b8fd-184444b00802}">
+    <IconPath IsRelative="true" IsResource="false">ApplicationIcon.png</IconPath>
+    <Capabilities>
+      <Capability Name="ID_CAP_GAMERSERVICES" />
+      <Capability Name="ID_CAP_IDENTITY_DEVICE" />
+      <Capability Name="ID_CAP_IDENTITY_USER" />
+      <Capability Name="ID_CAP_LOCATION" />
+      <Capability Name="ID_CAP_MICROPHONE" />
+      <Capability Name="ID_CAP_NETWORKING" />
+      <Capability Name="ID_CAP_PHONEDIALER" />
+      <Capability Name="ID_CAP_PUSH_NOTIFICATION" />
+      <Capability Name="ID_CAP_SENSORS" />
+      <Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
+      <Capability Name="ID_CAP_CONTACTS" />
+      <Capability Name="ID_CAP_SENSORS" />
+      <Capability Name="ID_CAP_ISV_CAMERA" />
+      <Capability Name="ID_CAP_MEDIALIB_AUDIO" />
+	  <Capability Name="ID_CAP_MEDIALIB_PLAYBACK" />
+      <Capability Name="ID_CAP_MEDIALIB_PHOTO" />
+    </Capabilities>
+    <Tasks>
+      <DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
+    </Tasks>
+    <Tokens>
+      <PrimaryToken TokenID="MobileSpecUnitTestsToken" TaskName="_default">
+        <TemplateFlip>
+          <SmallImageURI IsResource="false" IsRelative="true">Background.png</SmallImageURI>
+          <Count>0</Count>
+          <BackgroundImageURI IsResource="false" IsRelative="true">Background.png</BackgroundImageURI>
+          <Title>MobileSpecUnitTests</Title>
+          <BackContent></BackContent>
+          <BackBackgroundImageURI></BackBackgroundImageURI>
+          <BackTitle></BackTitle>
+          <LargeBackgroundImageURI></LargeBackgroundImageURI>
+          <LargeBackContent></LargeBackContent>
+          <LargeBackBackgroundImageURI></LargeBackBackgroundImageURI>
+          <DeviceLockImageURI></DeviceLockImageURI>
+          <HasLarge>false</HasLarge>
+        </TemplateFlip>
+      </PrimaryToken>
+    </Tokens>
+    <ScreenResolutions>
+      <ScreenResolution Name="ID_RESOLUTION_WVGA" />
+      <ScreenResolution Name="ID_RESOLUTION_WXGA" />
+      <ScreenResolution Name="ID_RESOLUTION_HD720P" />
+    </ScreenResolutions>
+  </App>
+</Deployment>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/SplashScreenImage.jpg
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/SplashScreenImage.jpg b/lib/cordova-wp8/tests/MobileSpecUnitTests/SplashScreenImage.jpg
new file mode 100644
index 0000000..479d3e4
Binary files /dev/null and b/lib/cordova-wp8/tests/MobileSpecUnitTests/SplashScreenImage.jpg differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/accelerometer/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/accelerometer/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/accelerometer/index.html
new file mode 100644
index 0000000..efa043b
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/accelerometer/index.html
@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    function roundNumber(num) {
+        var dec = 3;
+        var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
+        return result;
+    }
+
+    //-------------------------------------------------------------------------
+    // Acceleration
+    //-------------------------------------------------------------------------
+    var watchAccelId = null;
+    
+    /**
+     * Start watching acceleration
+     */
+    var watchAccel = function() {
+        console.log("watchAccel()");
+
+        // Success callback
+        var success = function(a){
+            document.getElementById('x').innerHTML = roundNumber(a.x);
+            document.getElementById('y').innerHTML = roundNumber(a.y);
+            document.getElementById('z').innerHTML = roundNumber(a.z);
+            console.log("watchAccel success callback");
+        };
+
+        // Fail callback
+        var fail = function(e){
+            console.log("watchAccel fail callback with error code "+e);
+            stopAccel();
+            setAccelStatus(Accelerometer.ERROR_MSG[e]);
+        };
+
+        // Update acceleration every 1 sec
+        var opt = {};
+        opt.frequency = 1000;
+        watchAccelId = navigator.accelerometer.watchAcceleration(success, fail, opt);
+
+        setAccelStatus("Running");
+    };
+
+    /**
+     * Stop watching the acceleration
+     */
+    var stopAccel = function() {
+    	console.log("stopAccel()");
+        setAccelStatus("Stopped");
+        if (watchAccelId) {
+            navigator.accelerometer.clearWatch(watchAccelId);
+            watchAccelId = null;
+        }
+    };
+
+    /**
+     * Get current acceleration
+     */
+    var getAccel = function() {
+        console.log("getAccel()");
+
+        // Stop accel if running
+        stopAccel();
+
+        // Success callback
+        var success = function(a){
+            document.getElementById('x').innerHTML = roundNumber(a.x);
+            document.getElementById('y').innerHTML = roundNumber(a.y);
+            document.getElementById('z').innerHTML = roundNumber(a.z);
+        };
+
+        // Fail callback
+        var fail = function(e){
+            console.log("getAccel fail callback with error code "+e);
+            setAccelStatus(Accelerometer.ERROR_MSG[e]);
+        };
+
+        // Make call
+        var opt = {};
+        navigator.accelerometer.getCurrentAcceleration(success, fail, opt);
+    };
+
+    /**
+     * Set accelerometer status
+     */
+    var setAccelStatus = function(status) {
+        document.getElementById('accel_status').innerHTML = status;
+    };
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        console.log("accelerometer.init()");
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Acceleration</h1>
+    <div id="info">
+        <div id="accel_status">Stopped</div>
+        <div ><table width="100%">
+            <tr><td width="20%">X:</td><td id="x"> </td></tr>
+            <tr><td width="20%">Y:</td><td id="y"> </td></tr>
+            <tr><td width="20%">Z:</td><td id="z"> </td></tr>
+        </table></div>
+    </div>
+
+    <h2>Action</h2>
+    <div class="btn large" onclick="getAccel();">Get Acceleration</div>
+    <div class="btn large" onclick="watchAccel();">Start Watch</div>
+    <div class="btn large" onclick="stopAccel();">Clear Watch</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/audio/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/audio/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/audio/index.html
new file mode 100644
index 0000000..42cfe83
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/audio/index.html
@@ -0,0 +1,394 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Audio Tests</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8"/>
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    //-------------------------------------------------------------------------
+    // Audio player
+    //-------------------------------------------------------------------------
+    var media1 = null;
+    var media1Timer = null;
+    var audioSrc = null;
+    var recordSrc = "myRecording.mp3";
+
+    /**
+     * Play audio
+     */
+    function playAudio(url) {
+        console.log("playAudio()");
+        console.log(" -- media="+media1);
+
+        //var src = "http://neuga.s3.amazonaws.com/onclassical/strings-or gan.mp3";
+       var src = "http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3";
+        //var src = "/android_asset/www/Jet_Sledding.mp4"; // no work
+        //var src = "http://vprbbc.streamguys.net/vprbbc24.mp3"; // mp3 streaming
+        
+        if (url) {
+            src = url;
+        }
+
+        // Stop playing if src is different from currently playing source
+        if (src != audioSrc) {
+            if (media1 != null) {
+                stopAudio();
+                media1 = null;
+            }
+        }
+
+        if (media1 == null) {
+
+
+            // TEST STREAMING AUDIO PLAYBACK
+            //var src = "http://nunzioweb.com/misc/Bon_Jovi-Crush_Mystery_Train.mp3";   // works
+            //var src = "http://nunzioweb.com/misc/Bon_Jovi-Crush_Mystery_Train.m3u"; // doesn't work
+            //var src = "http://www.wav-sounds.com/cartoon/bugsbunny1.wav"; // works
+            //var src = "http://www.angelfire.com/fl5/html-tutorial/a/couldyou.mid"; // doesn't work
+            //var src = "MusicSearch/mp3/train.mp3";    // works
+            //var src = "bryce.mp3";  // works
+            //var src = "/android_asset/www/bryce.mp3"; // works
+
+            media1 = new Media(src,
+                function() {
+                    console.log("playAudio():Audio Success");
+                },
+                function(err) {
+                    console.log("playAudio():Audio Error: "+err.code);
+                    setAudioStatus("Error: " + err.code);
+                },
+                function(status) {
+                    console.log("playAudio():Audio Status: "+status);
+                    setAudioStatus(Media.MEDIA_MSG[status]);
+
+                    // If stopped, then stop getting current position
+                    if (Media.MEDIA_STOPPED == status) {
+                        clearInterval(media1Timer);
+                        media1Timer = null;
+                        setAudioPosition("0 sec");
+                    }
+                });
+        }
+        audioSrc = src;
+        document.getElementById('audio_duration').innerHTML = "";
+        // Play audio
+        media1.play();
+        if (media1Timer == null && media1.getCurrentPosition) {
+            media1Timer = setInterval(
+                function() {
+                    media1.getCurrentPosition(
+                        function(position) {
+                            console.log("Pos="+position);
+                            if (position >= 0.0) {
+                                setAudioPosition(position+" sec");
+                            }
+                        },
+                        function(e) {
+                            console.log("Error getting pos="+e);
+                            setAudioPosition("Error: "+e);
+                        }
+                    );
+                },
+                1000
+            );
+        }
+
+        // Get duration
+        var counter = 0;
+        var timerDur = setInterval(
+            function() {
+                counter = counter + 100;
+                if (counter > 2000) {
+                    clearInterval(timerDur);
+                }
+                var dur = media1.getDuration();
+                if (dur > 0) {
+                    clearInterval(timerDur);
+                    document.getElementById('audio_duration').innerHTML = dur + " sec";
+                }
+            }, 100);
+    }
+
+    /**
+     * Pause audio playback
+     */
+    function pauseAudio() {
+        console.log("pauseAudio()");
+        if (media1) {
+            media1.pause();
+        }
+    }
+
+    /**
+     * Stop audio
+     */
+    function stopAudio() {
+        console.log("stopAudio()");
+        if (media1) {
+            media1.stop();
+            media1.release();
+        }
+        clearInterval(media1Timer);
+        media1Timer = null;
+    }
+
+    /**
+     * Set audio status
+     */
+    var setAudioStatus = function(status) {
+        document.getElementById('audio_status').innerHTML = status;
+    };
+
+    /**
+     * Set audio position
+     */
+    var setAudioPosition = function(position) {
+        document.getElementById('audio_position').innerHTML = position;
+    };
+
+    //-------------------------------------------------------------------------
+    // Audio recorder
+    //-------------------------------------------------------------------------
+    var mediaRec = null;
+    var recTime = 0;
+
+    /**
+     * Record audio
+     */
+    function recordAudio() {
+        console.log("recordAudio()");
+        console.log(" -- media="+mediaRec);
+        if (mediaRec == null) {
+
+            var src = recordSrc;
+            mediaRec = new Media(src,
+                    function() {
+                        console.log("recordAudio():Audio Success");
+                    },
+                    function(err) {
+                        console.log("recordAudio():Audio Error: "+err.code);
+                        setAudioStatus("Error: " + err.code);
+                    },
+                    function(status) {
+                        console.log("recordAudio():Audio Status: "+status);
+                        setAudioStatus(Media.MEDIA_MSG[status]);
+                    }
+                );
+        }
+
+        navigator.notification.beep(1);
+
+        // Record audio
+        mediaRec.startRecord();
+
+        // Stop recording after 10 sec
+        recTime = 0;
+        var recInterval = setInterval(function() {
+            recTime = recTime + 1;
+            setAudioPosition(recTime+" sec");
+            if (recTime >= 10) {
+                clearInterval(recInterval);
+                if (mediaRec.stopAudioRecord){
+                    mediaRec.stopAudioRecord();
+                } else {
+                    mediaRec.stopRecord();
+                }
+                console.log("recordAudio(): stop");
+                navigator.notification.beep(1);
+            }
+        }, 1000);
+    }
+
+    /**
+     * Play back recorded audio
+     */
+    function playRecording() {
+        playAudio(recordSrc);
+    }
+    
+    /**
+     * Function to create a file for iOS recording
+     */
+    function getRecordSrc() {
+        var fsFail = function(error) {
+            console.log("error creating file for iOS recording");
+        };
+        var gotFile = function(file) {
+            recordSrc = file.fullPath;
+            //console.log("recording Src: " + recordSrc);
+        };
+        var gotFS = function(fileSystem) {
+            fileSystem.root.getFile("iOSRecording.wav", {create: true}, gotFile, fsFail);
+        };
+        window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, gotFS, fsFail);
+    }
+    
+    /**
+     * Function to create a file for BB recording
+     */
+    function getRecordSrcBB() {
+        var fsFail = function(error) {
+            console.log("error creating file for BB recording");
+        };
+        var gotFile = function(file) {
+            recordSrc = file.fullPath;
+        };
+        var gotFS = function(fileSystem) {
+            fileSystem.root.getFile("BBRecording.amr", {create: true}, gotFile, fsFail);
+        };
+        window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, gotFS, fsFail);
+    }
+
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                if (device.platform.indexOf("iPhone") !=-1 || device.platform.indexOf("iPad") !=-1)
+                {
+                     getRecordSrc();
+                } else if (typeof blackberry !== 'undefined') {
+                    getRecordSrcBB();
+                }
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+    
+    /**
+     * for forced updates of position after a successful seek
+     */
+    function updatePosition() {
+        media1.getCurrentPosition(
+            function(position) {
+                console.log("Pos="+position);
+                if (position >= 0.0) {
+                    setAudioPosition(position+" sec");
+                }
+            },
+            function(e) {
+                console.log("Error getting pos="+e);
+                setAudioPosition("Error: "+e);
+            });
+    }
+
+    /**
+     *
+     */
+    function seekAudio(mode) {
+        var time = document.getElementById("seekinput").value;
+        if (time == "") {
+            time = 5000;
+        } else {
+            time = time * 1000; //we expect the input to be in seconds
+        }
+        if (media1 == null) {
+            console.log("seekTo requested while media1 is null");
+            if (audioSrc == null) {
+                audioSrc = "http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3";
+            }
+            media1 = new Media(audioSrc,
+                function() {
+                    console.log("seekToAudio():Audio Success");
+                },
+                function(err) {
+                    console.log("seekAudio():Audio Error: "+err.code);
+                    setAudioStatus("Error: " + err.code);
+                },
+                function(status) {
+                    console.log("seekAudio():Audio Status: "+status);
+                    setAudioStatus(Media.MEDIA_MSG[status]);
+
+                    // If stopped, then stop getting current position
+                    if (Media.MEDIA_STOPPED == status) {
+                        clearInterval(media1Timer);
+                        media1Timer = null;
+                        setAudioPosition("0 sec");
+                    }
+                });
+        }
+        
+        media1.getCurrentPosition(
+            function (position) {
+                var deltat = time;
+                if (mode == "by") {
+                    deltat = time + position * 1000;   
+                }
+                media1.seekTo(deltat,
+                    function () {
+                        console.log("seekAudioTo():Audio Success");
+                        //force an update on the position display
+                        updatePosition();
+                    },
+                    function (err) {
+                        console.log("seekAudioTo():Audio Error: " + err.code);
+                    });
+            },
+            function(e) {
+                console.log("Error getting pos="+e);
+                setAudioPosition("Error: "+e);
+            });
+    }
+    
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Audio</h1>  
+    <div id="info">
+        <table width="100%">
+        <tr><td><b>Status:</b></td><td id="audio_status"> </td></tr>
+        <tr><td><b>Duration:</b></td><td id="audio_duration"></td></tr>
+        <tr><td><b>Position:</b></td><td id="audio_position"></td></tr>
+        </table>
+    </div>
+    <h2>Action</h2>
+    <table>
+        <tr>
+            <th colspan=3>Play Sample Audio</th>
+        </tr>
+        <tr>
+            <td><div class="btn large" style="width:100%;" onclick="playAudio();">Play</div></td>
+            <td><div class="btn large" style="width:100%;" onclick="pauseAudio();">Pause</div></td>
+            <td><div class="btn large" style="width:100%;" onclick="stopAudio();">Stop</div></td>
+        </tr>
+        <tr>
+            <td><div class="btn large" style="width:100%;" onclick="seekAudio('by');">Seek By</div></td>
+            <td><div class="btn large" style="width:100%;" onclick="seekAudio('to');">Seek To</div></td>
+            <td>
+                <div style="width:100%;">
+                    <input class="input numeric" type="number" id="seekinput" value="in seconds">
+                </div>
+            </td>
+            <td><h2>s</h2></td>
+        </tr>
+        <tr>
+            <th colspan=3><br><br>Record Audio</th>
+        </tr>
+        <tr>
+            <td colspan=3><div class="btn large" onclick="recordAudio();">Record Audio for 10 sec</a></td>
+        </tr>
+        <tr>
+            <td><div class="btn large" style="width:100%;" onclick="playRecording();">Play</div></td>
+            <td><div class="btn large" style="width:100%;" onclick="pauseAudio();">Pause</div></td>
+            <td><div class="btn large" style="width:100%;" onclick="stopAudio();">Stop</div></td>
+        </tr>
+    </table>
+    
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+    
+  </body>
+</html>      

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/HtmlReporter.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/HtmlReporter.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/HtmlReporter.js
new file mode 100644
index 0000000..7d9d924
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/HtmlReporter.js
@@ -0,0 +1,101 @@
+jasmine.HtmlReporter = function(_doc) {
+  var self = this;
+  var doc = _doc || window.document;
+
+  var reporterView;
+
+  var dom = {};
+
+  // Jasmine Reporter Public Interface
+  self.logRunningSpecs = false;
+
+  self.reportRunnerStarting = function(runner) {
+    var specs = runner.specs() || [];
+
+    if (specs.length == 0) {
+      return;
+    }
+
+    createReporterDom(runner.env.versionString());
+    doc.body.appendChild(dom.reporter);
+
+    reporterView = new jasmine.HtmlReporter.ReporterView(dom);
+    reporterView.addSpecs(specs, self.specFilter);
+  };
+
+  self.reportRunnerResults = function(runner) {
+    reporterView && reporterView.complete();
+  };
+
+  self.reportSuiteResults = function(suite) {
+    reporterView.suiteComplete(suite);
+  };
+
+  self.reportSpecStarting = function(spec) {
+    if (self.logRunningSpecs) {
+      self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+    }
+  };
+
+  self.reportSpecResults = function(spec) {
+    reporterView.specComplete(spec);
+  };
+
+  self.log = function() {
+    var console = jasmine.getGlobal().console;
+    if (console && console.log) {
+      if (console.log.apply) {
+        console.log.apply(console, arguments);
+      } else {
+        console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+      }
+    }
+  };
+
+  self.specFilter = function(spec) {
+    if (!focusedSpecName()) {
+      return true;
+    }
+
+    return spec.getFullName().indexOf(focusedSpecName()) === 0;
+  };
+
+  return self;
+
+  function focusedSpecName() {
+    var specName;
+
+    (function memoizeFocusedSpec() {
+      if (specName) {
+        return;
+      }
+
+      var paramMap = [];
+      var params = doc.location.search.substring(1).split('&');
+
+      for (var i = 0; i < params.length; i++) {
+        var p = params[i].split('=');
+        paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
+      }
+
+      specName = paramMap.spec;
+    })();
+
+    return specName;
+  }
+
+  function createReporterDom(version) {
+    dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
+      dom.banner = self.createDom('div', { className: 'banner' },
+        self.createDom('span', { className: 'title' }, "Jasmine "),
+        self.createDom('span', { className: 'version' }, version)),
+
+      dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
+      dom.alert = self.createDom('div', {className: 'alert'}),
+      dom.results = self.createDom('div', {className: 'results'},
+        dom.summary = self.createDom('div', { className: 'summary' }),
+        dom.details = self.createDom('div', { id: 'details' }))
+    );
+  }
+};
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/HtmlReporterHelpers.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/HtmlReporterHelpers.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/HtmlReporterHelpers.js
new file mode 100644
index 0000000..745e1e0
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/HtmlReporterHelpers.js
@@ -0,0 +1,60 @@
+jasmine.HtmlReporterHelpers = {};
+
+jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
+  var el = document.createElement(type);
+
+  for (var i = 2; i < arguments.length; i++) {
+    var child = arguments[i];
+
+    if (typeof child === 'string') {
+      el.appendChild(document.createTextNode(child));
+    } else {
+      if (child) {
+        el.appendChild(child);
+      }
+    }
+  }
+
+  for (var attr in attrs) {
+    if (attr == "className") {
+      el[attr] = attrs[attr];
+    } else {
+      el.setAttribute(attr, attrs[attr]);
+    }
+  }
+
+  return el;
+};
+
+jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
+  var results = child.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.skipped) {
+    status = 'skipped';
+  }
+
+  return status;
+};
+
+jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
+  var parentDiv = this.dom.summary;
+  var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
+  var parent = child[parentSuite];
+
+  if (parent) {
+    if (typeof this.views.suites[parent.id] == 'undefined') {
+      this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
+    }
+    parentDiv = this.views.suites[parent.id].element;
+  }
+
+  parentDiv.appendChild(childElement);
+};
+
+
+jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
+  for(var fn in jasmine.HtmlReporterHelpers) {
+    ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
+  }
+};
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/ReporterView.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/ReporterView.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/ReporterView.js
new file mode 100644
index 0000000..6a6d005
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/ReporterView.js
@@ -0,0 +1,164 @@
+jasmine.HtmlReporter.ReporterView = function(dom) {
+  this.startedAt = new Date();
+  this.runningSpecCount = 0;
+  this.completeSpecCount = 0;
+  this.passedCount = 0;
+  this.failedCount = 0;
+  this.skippedCount = 0;
+
+  this.createResultsMenu = function() {
+    this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
+      this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
+      ' | ',
+      this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
+
+    this.summaryMenuItem.onclick = function() {
+      dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
+    };
+
+    this.detailsMenuItem.onclick = function() {
+      showDetails();
+    };
+  };
+
+  this.addSpecs = function(specs, specFilter) {
+    this.totalSpecCount = specs.length;
+
+    this.views = {
+      specs: {},
+      suites: {}
+    };
+
+    for (var i = 0; i < specs.length; i++) {
+      var spec = specs[i];
+      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
+      if (specFilter(spec)) {
+        this.runningSpecCount++;
+      }
+    }
+  };
+
+  this.specComplete = function(spec) {
+    this.completeSpecCount++;
+
+    if (isUndefined(this.views.specs[spec.id])) {
+      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
+    }
+
+    var specView = this.views.specs[spec.id];
+
+    switch (specView.status()) {
+      case 'passed':
+        this.passedCount++;
+        break;
+
+      case 'failed':
+        this.failedCount++;
+        break;
+
+      case 'skipped':
+        this.skippedCount++;
+        break;
+    }
+
+    specView.refresh();
+    this.refresh();
+  };
+
+  this.suiteComplete = function(suite) {
+    var suiteView = this.views.suites[suite.id];
+    if (isUndefined(suiteView)) {
+      return;
+    }
+    suiteView.refresh();
+  };
+
+  this.refresh = function() {
+
+    if (isUndefined(this.resultsMenu)) {
+      this.createResultsMenu();
+    }
+
+    // currently running UI
+    if (isUndefined(this.runningAlert)) {
+      this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
+      dom.alert.appendChild(this.runningAlert);
+    }
+    this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
+
+    // skipped specs UI
+    if (isUndefined(this.skippedAlert)) {
+      this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
+    }
+
+    this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
+
+    if (this.skippedCount === 1 && isDefined(dom.alert)) {
+      dom.alert.appendChild(this.skippedAlert);
+    }
+
+    // passing specs UI
+    if (isUndefined(this.passedAlert)) {
+      this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
+    }
+    this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
+
+    // failing specs UI
+    if (isUndefined(this.failedAlert)) {
+      this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
+    }
+    this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
+
+    if (this.failedCount === 1 && isDefined(dom.alert)) {
+      dom.alert.appendChild(this.failedAlert);
+      dom.alert.appendChild(this.resultsMenu);
+    }
+
+    // summary info
+    this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
+    this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
+  };
+
+  this.complete = function() {
+    dom.alert.removeChild(this.runningAlert);
+
+    this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
+
+    if (this.failedCount === 0) {
+      dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
+    } else {
+      showDetails();
+    }
+
+    dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
+  };
+
+  return this;
+
+  function showDetails() {
+    if (dom.reporter.className.search(/showDetails/) === -1) {
+      dom.reporter.className += " showDetails";
+    }
+  }
+
+  function isUndefined(obj) {
+    return typeof obj === 'undefined';
+  }
+
+  function isDefined(obj) {
+    return !isUndefined(obj);
+  }
+
+  function specPluralizedFor(count) {
+    var str = count + " spec";
+    if (count > 1) {
+      str += "s"
+    }
+    return str;
+  }
+
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
+
+


[09/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/cordova-2.5.0.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/cordova-2.5.0.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/cordova-2.5.0.js
new file mode 100644
index 0000000..5989d67
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/cordova-2.5.0.js
@@ -0,0 +1,6446 @@
+// Platform: windowsphone
+
+// commit 54660e97952c558518cad8c4eecc67cfa42b2993
+
+// File generated at :: Tue Feb 26 2013 16:24:06 GMT-0800 (Pacific Standard Time)
+
+/*
+ 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.
+*/
+
+;(function() {
+
+// file: lib\scripts\require.js
+
+var require,
+    define;
+
+(function () {
+    var modules = {};
+    // Stack of moduleIds currently being built.
+    var requireStack = [];
+    // Map of module ID -> index into requireStack of modules currently being built.
+    var inProgressModules = {};
+
+    function build(module) {
+        var factory = module.factory;
+        module.exports = {};
+        delete module.factory;
+        factory(require, module.exports, module);
+        return module.exports;
+    }
+
+    require = function (id) {
+        if (!modules[id]) {
+            throw "module " + id + " not found";
+        } else if (id in inProgressModules) {
+            var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;
+            throw "Cycle in require graph: " + cycle;
+        }
+        if (modules[id].factory) {
+            try {
+                inProgressModules[id] = requireStack.length;
+                requireStack.push(id);
+                return build(modules[id]);
+            } finally {
+                delete inProgressModules[id];
+                requireStack.pop();
+            }
+        }
+        return modules[id].exports;
+    };
+
+    define = function (id, factory) {
+        if (modules[id]) {
+            throw "module " + id + " already defined";
+        }
+
+        modules[id] = {
+            id: id,
+            factory: factory
+        };
+    };
+
+    define.remove = function (id) {
+        delete modules[id];
+    };
+
+    define.moduleMap = modules;
+})();
+
+//Export for use in node
+if (typeof module === "object" && typeof require === "function") {
+    module.exports.require = require;
+    module.exports.define = define;
+}
+
+// file: lib/cordova.js
+define("cordova", function(require, exports, module) {
+
+
+var channel = require('cordova/channel');
+
+/**
+ * Listen for DOMContentLoaded and notify our channel subscribers.
+ */
+document.addEventListener('DOMContentLoaded', function() {
+    channel.onDOMContentLoaded.fire();
+}, false);
+if (document.readyState == 'complete' || document.readyState == 'interactive') {
+    channel.onDOMContentLoaded.fire();
+}
+
+/**
+ * Intercept calls to addEventListener + removeEventListener and handle deviceready,
+ * resume, and pause events.
+ */
+var m_document_addEventListener = document.addEventListener;
+var m_document_removeEventListener = document.removeEventListener;
+var m_window_addEventListener = window.addEventListener;
+var m_window_removeEventListener = window.removeEventListener;
+
+/**
+ * Houses custom event handlers to intercept on document + window event listeners.
+ */
+var documentEventHandlers = {},
+    windowEventHandlers = {};
+
+document.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    if (typeof documentEventHandlers[e] != 'undefined') {
+        documentEventHandlers[e].subscribe(handler);
+    } else {
+        m_document_addEventListener.call(document, evt, handler, capture);
+    }
+};
+
+window.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    if (typeof windowEventHandlers[e] != 'undefined') {
+        windowEventHandlers[e].subscribe(handler);
+    } else {
+        m_window_addEventListener.call(window, evt, handler, capture);
+    }
+};
+
+document.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    // If unsubscribing from an event that is handled by a plugin
+    if (typeof documentEventHandlers[e] != "undefined") {
+        documentEventHandlers[e].unsubscribe(handler);
+    } else {
+        m_document_removeEventListener.call(document, evt, handler, capture);
+    }
+};
+
+window.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    // If unsubscribing from an event that is handled by a plugin
+    if (typeof windowEventHandlers[e] != "undefined") {
+        windowEventHandlers[e].unsubscribe(handler);
+    } else {
+        m_window_removeEventListener.call(window, evt, handler, capture);
+    }
+};
+
+function createEvent(type, data) {
+    var event = document.createEvent('Events');
+    event.initEvent(type, false, false);
+    if (data) {
+        for (var i in data) {
+            if (data.hasOwnProperty(i)) {
+                event[i] = data[i];
+            }
+        }
+    }
+    return event;
+}
+
+if(typeof window.console === "undefined") {
+    window.console = {
+        log:function(){}
+    };
+}
+
+var cordova = {
+    define:define,
+    require:require,
+    /**
+     * Methods to add/remove your own addEventListener hijacking on document + window.
+     */
+    addWindowEventHandler:function(event) {
+        return (windowEventHandlers[event] = channel.create(event));
+    },
+    addStickyDocumentEventHandler:function(event) {
+        return (documentEventHandlers[event] = channel.createSticky(event));
+    },
+    addDocumentEventHandler:function(event) {
+        return (documentEventHandlers[event] = channel.create(event));
+    },
+    removeWindowEventHandler:function(event) {
+        delete windowEventHandlers[event];
+    },
+    removeDocumentEventHandler:function(event) {
+        delete documentEventHandlers[event];
+    },
+    /**
+     * Retrieve original event handlers that were replaced by Cordova
+     *
+     * @return object
+     */
+    getOriginalHandlers: function() {
+        return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
+        'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
+    },
+    /**
+     * Method to fire event from native code
+     * bNoDetach is required for events which cause an exception which needs to be caught in native code
+     */
+    fireDocumentEvent: function(type, data, bNoDetach) {
+        var evt = createEvent(type, data);
+        if (typeof documentEventHandlers[type] != 'undefined') {
+            if( bNoDetach ) {
+              documentEventHandlers[type].fire(evt);
+            }
+            else {
+              setTimeout(function() {
+                  documentEventHandlers[type].fire(evt);
+              }, 0);
+            }
+        } else {
+            document.dispatchEvent(evt);
+        }
+    },
+    fireWindowEvent: function(type, data) {
+        var evt = createEvent(type,data);
+        if (typeof windowEventHandlers[type] != 'undefined') {
+            setTimeout(function() {
+                windowEventHandlers[type].fire(evt);
+            }, 0);
+        } else {
+            window.dispatchEvent(evt);
+        }
+    },
+
+    /**
+     * Plugin callback mechanism.
+     */
+    // Randomize the starting callbackId to avoid collisions after refreshing or navigating.
+    // This way, it's very unlikely that any new callback would get the same callbackId as an old callback.
+    callbackId: Math.floor(Math.random() * 2000000000),
+    callbacks:  {},
+    callbackStatus: {
+        NO_RESULT: 0,
+        OK: 1,
+        CLASS_NOT_FOUND_EXCEPTION: 2,
+        ILLEGAL_ACCESS_EXCEPTION: 3,
+        INSTANTIATION_EXCEPTION: 4,
+        MALFORMED_URL_EXCEPTION: 5,
+        IO_EXCEPTION: 6,
+        INVALID_ACTION: 7,
+        JSON_EXCEPTION: 8,
+        ERROR: 9
+    },
+
+    /**
+     * Called by native code when returning successful result from an action.
+     */
+    callbackSuccess: function(callbackId, args) {
+        try {
+            cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
+        } catch (e) {
+            console.log("Error in error callback: " + callbackId + " = "+e);
+        }
+    },
+
+    /**
+     * Called by native code when returning error result from an action.
+     */
+    callbackError: function(callbackId, args) {
+        // TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative.
+        // Derive success from status.
+        try {
+            cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
+        } catch (e) {
+            console.log("Error in error callback: " + callbackId + " = "+e);
+        }
+    },
+
+    /**
+     * Called by native code when returning the result from an action.
+     */
+    callbackFromNative: function(callbackId, success, status, args, keepCallback) {
+        var callback = cordova.callbacks[callbackId];
+        if (callback) {
+            if (success && status == cordova.callbackStatus.OK) {
+                callback.success && callback.success.apply(null, args);
+            } else if (!success) {
+                callback.fail && callback.fail.apply(null, args);
+            }
+
+            // Clear callback if not expecting any more results
+            if (!keepCallback) {
+                delete cordova.callbacks[callbackId];
+            }
+        }
+    },
+    addConstructor: function(func) {
+        channel.onCordovaReady.subscribe(function() {
+            try {
+                func();
+            } catch(e) {
+                console.log("Failed to run constructor: " + e);
+            }
+        });
+    }
+};
+
+// Register pause, resume and deviceready channels as events on document.
+channel.onPause = cordova.addDocumentEventHandler('pause');
+channel.onResume = cordova.addDocumentEventHandler('resume');
+channel.onDeviceReady = cordova.addStickyDocumentEventHandler('deviceready');
+
+module.exports = cordova;
+
+});
+
+// file: lib\common\argscheck.js
+define("cordova/argscheck", function(require, exports, module) {
+
+var exec = require('cordova/exec');
+var utils = require('cordova/utils');
+
+var moduleExports = module.exports;
+
+var typeMap = {
+    'A': 'Array',
+    'D': 'Date',
+    'N': 'Number',
+    'S': 'String',
+    'F': 'Function',
+    'O': 'Object'
+};
+
+function extractParamName(callee, argIndex) {
+  return (/.*?\((.*?)\)/).exec(callee)[1].split(', ')[argIndex];
+}
+
+function checkArgs(spec, functionName, args, opt_callee) {
+    if (!moduleExports.enableChecks) {
+        return;
+    }
+    var errMsg = null;
+    var typeName;
+    for (var i = 0; i < spec.length; ++i) {
+        var c = spec.charAt(i),
+            cUpper = c.toUpperCase(),
+            arg = args[i];
+        // Asterix means allow anything.
+        if (c == '*') {
+            continue;
+        }
+        typeName = utils.typeName(arg);
+        if ((arg === null || arg === undefined) && c == cUpper) {
+            continue;
+        }
+        if (typeName != typeMap[cUpper]) {
+            errMsg = 'Expected ' + typeMap[cUpper];
+            break;
+        }
+    }
+    if (errMsg) {
+        errMsg += ', but got ' + typeName + '.';
+        errMsg = 'Wrong type for parameter "' + extractParamName(opt_callee || args.callee, i) + '" of ' + functionName + ': ' + errMsg;
+        // Don't log when running jake test.
+        if (typeof jasmine == 'undefined') {
+            console.error(errMsg);
+        }
+        throw TypeError(errMsg);
+    }
+}
+
+function getValue(value, defaultValue) {
+    return value === undefined ? defaultValue : value;
+}
+
+moduleExports.checkArgs = checkArgs;
+moduleExports.getValue = getValue;
+moduleExports.enableChecks = true;
+
+
+});
+
+// file: lib\common\builder.js
+define("cordova/builder", function(require, exports, module) {
+
+var utils = require('cordova/utils');
+
+function each(objects, func, context) {
+    for (var prop in objects) {
+        if (objects.hasOwnProperty(prop)) {
+            func.apply(context, [objects[prop], prop]);
+        }
+    }
+}
+
+function clobber(obj, key, value) {
+    exports.replaceHookForTesting(obj, key);
+    obj[key] = value;
+    // Getters can only be overridden by getters.
+    if (obj[key] !== value) {
+        utils.defineGetter(obj, key, function() {
+            return value;
+        });
+    }
+}
+
+function assignOrWrapInDeprecateGetter(obj, key, value, message) {
+    if (message) {
+        utils.defineGetter(obj, key, function() {
+            console.log(message);
+            delete obj[key];
+            clobber(obj, key, value);
+            return value;
+        });
+    } else {
+        clobber(obj, key, value);
+    }
+}
+
+function include(parent, objects, clobber, merge) {
+    each(objects, function (obj, key) {
+        try {
+          var result = obj.path ? require(obj.path) : {};
+
+          if (clobber) {
+              // Clobber if it doesn't exist.
+              if (typeof parent[key] === 'undefined') {
+                  assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+              } else if (typeof obj.path !== 'undefined') {
+                  // If merging, merge properties onto parent, otherwise, clobber.
+                  if (merge) {
+                      recursiveMerge(parent[key], result);
+                  } else {
+                      assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+                  }
+              }
+              result = parent[key];
+          } else {
+            // Overwrite if not currently defined.
+            if (typeof parent[key] == 'undefined') {
+              assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+            } else {
+              // Set result to what already exists, so we can build children into it if they exist.
+              result = parent[key];
+            }
+          }
+
+          if (obj.children) {
+            include(result, obj.children, clobber, merge);
+          }
+        } catch(e) {
+          utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"');
+        }
+    });
+}
+
+/**
+ * Merge properties from one object onto another recursively.  Properties from
+ * the src object will overwrite existing target property.
+ *
+ * @param target Object to merge properties into.
+ * @param src Object to merge properties from.
+ */
+function recursiveMerge(target, src) {
+    for (var prop in src) {
+        if (src.hasOwnProperty(prop)) {
+            if (target.prototype && target.prototype.constructor === target) {
+                // If the target object is a constructor override off prototype.
+                clobber(target.prototype, prop, src[prop]);
+            } else {
+                if (typeof src[prop] === 'object' && typeof target[prop] === 'object') {
+                    recursiveMerge(target[prop], src[prop]);
+                } else {
+                    clobber(target, prop, src[prop]);
+                }
+            }
+        }
+    }
+}
+
+exports.buildIntoButDoNotClobber = function(objects, target) {
+    include(target, objects, false, false);
+};
+exports.buildIntoAndClobber = function(objects, target) {
+    include(target, objects, true, false);
+};
+exports.buildIntoAndMerge = function(objects, target) {
+    include(target, objects, true, true);
+};
+exports.recursiveMerge = recursiveMerge;
+exports.assignOrWrapInDeprecateGetter = assignOrWrapInDeprecateGetter;
+exports.replaceHookForTesting = function() {};
+
+});
+
+// file: lib\common\channel.js
+define("cordova/channel", function(require, exports, module) {
+
+var utils = require('cordova/utils'),
+    nextGuid = 1;
+
+/**
+ * Custom pub-sub "channel" that can have functions subscribed to it
+ * This object is used to define and control firing of events for
+ * cordova initialization, as well as for custom events thereafter.
+ *
+ * The order of events during page load and Cordova startup is as follows:
+ *
+ * onDOMContentLoaded*         Internal event that is received when the web page is loaded and parsed.
+ * onNativeReady*              Internal event that indicates the Cordova native side is ready.
+ * onCordovaReady*             Internal event fired when all Cordova JavaScript objects have been created.
+ * onCordovaInfoReady*         Internal event fired when device properties are available.
+ * onCordovaConnectionReady*   Internal event fired when the connection property has been set.
+ * onDeviceReady*              User event fired to indicate that Cordova is ready
+ * onResume                    User event fired to indicate a start/resume lifecycle event
+ * onPause                     User event fired to indicate a pause lifecycle event
+ * onDestroy*                  Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
+ *
+ * The events marked with an * are sticky. Once they have fired, they will stay in the fired state.
+ * All listeners that subscribe after the event is fired will be executed right away.
+ *
+ * The only Cordova events that user code should register for are:
+ *      deviceready           Cordova native code is initialized and Cordova APIs can be called from JavaScript
+ *      pause                 App has moved to background
+ *      resume                App has returned to foreground
+ *
+ * Listeners can be registered as:
+ *      document.addEventListener("deviceready", myDeviceReadyListener, false);
+ *      document.addEventListener("resume", myResumeListener, false);
+ *      document.addEventListener("pause", myPauseListener, false);
+ *
+ * The DOM lifecycle events should be used for saving and restoring state
+ *      window.onload
+ *      window.onunload
+ *
+ */
+
+/**
+ * Channel
+ * @constructor
+ * @param type  String the channel name
+ */
+var Channel = function(type, sticky) {
+    this.type = type;
+    // Map of guid -> function.
+    this.handlers = {};
+    // 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
+    this.state = sticky ? 1 : 0;
+    // Used in sticky mode to remember args passed to fire().
+    this.fireArgs = null;
+    // Used by onHasSubscribersChange to know if there are any listeners.
+    this.numHandlers = 0;
+    // Function that is called when the first listener is subscribed, or when
+    // the last listener is unsubscribed.
+    this.onHasSubscribersChange = null;
+},
+    channel = {
+        /**
+         * Calls the provided function only after all of the channels specified
+         * have been fired. All channels must be sticky channels.
+         */
+        join: function(h, c) {
+            var len = c.length,
+                i = len,
+                f = function() {
+                    if (!(--i)) h();
+                };
+            for (var j=0; j<len; j++) {
+                if (c[j].state === 0) {
+                    throw Error('Can only use join with sticky channels.');
+                }
+                c[j].subscribe(f);
+            }
+            if (!len) h();
+        },
+        create: function(type) {
+            return channel[type] = new Channel(type, false);
+        },
+        createSticky: function(type) {
+            return channel[type] = new Channel(type, true);
+        },
+
+        /**
+         * cordova Channels that must fire before "deviceready" is fired.
+         */
+        deviceReadyChannelsArray: [],
+        deviceReadyChannelsMap: {},
+
+        /**
+         * Indicate that a feature needs to be initialized before it is ready to be used.
+         * This holds up Cordova's "deviceready" event until the feature has been initialized
+         * and Cordova.initComplete(feature) is called.
+         *
+         * @param feature {String}     The unique feature name
+         */
+        waitForInitialization: function(feature) {
+            if (feature) {
+                var c = channel[feature] || this.createSticky(feature);
+                this.deviceReadyChannelsMap[feature] = c;
+                this.deviceReadyChannelsArray.push(c);
+            }
+        },
+
+        /**
+         * Indicate that initialization code has completed and the feature is ready to be used.
+         *
+         * @param feature {String}     The unique feature name
+         */
+        initializationComplete: function(feature) {
+            var c = this.deviceReadyChannelsMap[feature];
+            if (c) {
+                c.fire();
+            }
+        }
+    };
+
+function forceFunction(f) {
+    if (typeof f != 'function') throw "Function required as first argument!";
+}
+
+/**
+ * Subscribes the given function to the channel. Any time that
+ * Channel.fire is called so too will the function.
+ * Optionally specify an execution context for the function
+ * and a guid that can be used to stop subscribing to the channel.
+ * Returns the guid.
+ */
+Channel.prototype.subscribe = function(f, c) {
+    // need a function to call
+    forceFunction(f);
+    if (this.state == 2) {
+        f.apply(c || this, this.fireArgs);
+        return;
+    }
+
+    var func = f,
+        guid = f.observer_guid;
+    if (typeof c == "object") { func = utils.close(c, f); }
+
+    if (!guid) {
+        // first time any channel has seen this subscriber
+        guid = '' + nextGuid++;
+    }
+    func.observer_guid = guid;
+    f.observer_guid = guid;
+
+    // Don't add the same handler more than once.
+    if (!this.handlers[guid]) {
+        this.handlers[guid] = func;
+        this.numHandlers++;
+        if (this.numHandlers == 1) {
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+/**
+ * Unsubscribes the function with the given guid from the channel.
+ */
+Channel.prototype.unsubscribe = function(f) {
+    // need a function to unsubscribe
+    forceFunction(f);
+
+    var guid = f.observer_guid,
+        handler = this.handlers[guid];
+    if (handler) {
+        delete this.handlers[guid];
+        this.numHandlers--;
+        if (this.numHandlers === 0) {
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+/**
+ * Calls all functions subscribed to this channel.
+ */
+Channel.prototype.fire = function(e) {
+    var fail = false,
+        fireArgs = Array.prototype.slice.call(arguments);
+    // Apply stickiness.
+    if (this.state == 1) {
+        this.state = 2;
+        this.fireArgs = fireArgs;
+    }
+    if (this.numHandlers) {
+        // Copy the values first so that it is safe to modify it from within
+        // callbacks.
+        var toCall = [];
+        for (var item in this.handlers) {
+            toCall.push(this.handlers[item]);
+        }
+        for (var i = 0; i < toCall.length; ++i) {
+            toCall[i].apply(this, fireArgs);
+        }
+        if (this.state == 2 && this.numHandlers) {
+            this.numHandlers = 0;
+            this.handlers = {};
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+
+// defining them here so they are ready super fast!
+// DOM event that is received when the web page is loaded and parsed.
+channel.createSticky('onDOMContentLoaded');
+
+// Event to indicate the Cordova native side is ready.
+channel.createSticky('onNativeReady');
+
+// Event to indicate that all Cordova JavaScript objects have been created
+// and it's time to run plugin constructors.
+channel.createSticky('onCordovaReady');
+
+// Event to indicate that device properties are available
+channel.createSticky('onCordovaInfoReady');
+
+// Event to indicate that the connection property has been set.
+channel.createSticky('onCordovaConnectionReady');
+
+// Event to indicate that all automatically loaded JS plugins are loaded and ready.
+// This is used in conjunction with the automatic plugin JS loading CLI prototype.
+channel.createSticky('onPluginsReady');
+
+// Event to indicate that Cordova is ready
+channel.createSticky('onDeviceReady');
+
+// Event to indicate a resume lifecycle event
+channel.create('onResume');
+
+// Event to indicate a pause lifecycle event
+channel.create('onPause');
+
+// Event to indicate a destroy lifecycle event
+channel.createSticky('onDestroy');
+
+// Channels that must fire before "deviceready" is fired.
+channel.waitForInitialization('onCordovaReady');
+channel.waitForInitialization('onCordovaConnectionReady');
+
+module.exports = channel;
+
+});
+
+// file: lib\common\commandProxy.js
+define("cordova/commandProxy", function(require, exports, module) {
+
+
+// internal map of proxy function
+var CommandProxyMap = {};
+
+module.exports = {
+
+    // example: cordova.commandProxy.add("Accelerometer",{getCurrentAcceleration: function(successCallback, errorCallback, options) {...},...);
+    add:function(id,proxyObj) {
+        console.log("adding proxy for " + id);
+        CommandProxyMap[id] = proxyObj;
+        return proxyObj;
+    },
+
+    // cordova.commandProxy.remove("Accelerometer");
+    remove:function(id) {
+        var proxy = CommandProxyMap[id];
+        delete CommandProxyMap[id];
+        CommandProxyMap[id] = null;
+        return proxy;
+    },
+
+    get:function(service,action) {
+        return ( CommandProxyMap[service] ? CommandProxyMap[service][action] : null );
+    }
+};
+});
+
+// file: lib\windowsphone\exec.js
+define("cordova/exec", function(require, exports, module) {
+
+var cordova = require('cordova');
+
+/**
+ * Execute a cordova command.  It is up to the native side whether this action
+ * is synchronous or asynchronous.  The native side can return:
+ *      Synchronous: PluginResult object as a JSON string
+ *      Asynchronous: Empty string ""
+ * If async, the native side will cordova.callbackSuccess or cordova.callbackError,
+ * depending upon the result of the action.
+ *
+ * @param {Function} success    The success callback
+ * @param {Function} fail       The fail callback
+ * @param {String} service      The name of the service to use
+ * @param {String} action       Action to be run in cordova
+ * @param {String[]} [args]     Zero or more arguments to pass to the method
+
+ */
+
+module.exports = function(success, fail, service, action, args) {
+
+    var callbackId = service + cordova.callbackId++;
+    if (typeof success == "function" || typeof fail == "function") {
+        cordova.callbacks[callbackId] = {success:success, fail:fail};
+    }
+    // generate a new command string, ex. DebugConsole/log/DebugConsole23/["wtf dude?"]
+    for(var n = 0; n < args.length; n++)
+    {
+        if(typeof args[n] !== "string")
+        {
+            args[n] = JSON.stringify(args[n]);
+        }
+    }
+    var command = service + "/" + action + "/" + callbackId + "/" + JSON.stringify(args);
+    // pass it on to Notify
+    try {
+        if(window.external) {
+            window.external.Notify(command);
+        }
+        else {
+            console.log("window.external not available :: command=" + command);
+        }
+    }
+    catch(e) {
+        console.log("Exception calling native with command :: " + command + " :: exception=" + e);
+    }
+};
+
+
+});
+
+// file: lib\common\modulemapper.js
+define("cordova/modulemapper", function(require, exports, module) {
+
+var builder = require('cordova/builder'),
+    moduleMap = define.moduleMap,
+    symbolList,
+    deprecationMap;
+
+exports.reset = function() {
+    symbolList = [];
+    deprecationMap = {};
+};
+
+function addEntry(strategy, moduleName, symbolPath, opt_deprecationMessage) {
+    if (!(moduleName in moduleMap)) {
+        throw new Error('Module ' + moduleName + ' does not exist.');
+    }
+    symbolList.push(strategy, moduleName, symbolPath);
+    if (opt_deprecationMessage) {
+        deprecationMap[symbolPath] = opt_deprecationMessage;
+    }
+}
+
+// Note: Android 2.3 does have Function.bind().
+exports.clobbers = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('c', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+exports.merges = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('m', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+exports.defaults = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('d', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+function prepareNamespace(symbolPath, context) {
+    if (!symbolPath) {
+        return context;
+    }
+    var parts = symbolPath.split('.');
+    var cur = context;
+    for (var i = 0, part; part = parts[i]; ++i) {
+        cur = cur[part] = cur[part] || {};
+    }
+    return cur;
+}
+
+exports.mapModules = function(context) {
+    var origSymbols = {};
+    context.CDV_origSymbols = origSymbols;
+    for (var i = 0, len = symbolList.length; i < len; i += 3) {
+        var strategy = symbolList[i];
+        var moduleName = symbolList[i + 1];
+        var symbolPath = symbolList[i + 2];
+        var lastDot = symbolPath.lastIndexOf('.');
+        var namespace = symbolPath.substr(0, lastDot);
+        var lastName = symbolPath.substr(lastDot + 1);
+
+        var module = require(moduleName);
+        var deprecationMsg = symbolPath in deprecationMap ? 'Access made to deprecated symbol: ' + symbolPath + '. ' + deprecationMsg : null;
+        var parentObj = prepareNamespace(namespace, context);
+        var target = parentObj[lastName];
+
+        if (strategy == 'm' && target) {
+            builder.recursiveMerge(target, module);
+        } else if ((strategy == 'd' && !target) || (strategy != 'd')) {
+            if (!(symbolPath in origSymbols)) {
+                origSymbols[symbolPath] = target;
+            }
+            builder.assignOrWrapInDeprecateGetter(parentObj, lastName, module, deprecationMsg);
+        }
+    }
+};
+
+exports.getOriginalSymbol = function(context, symbolPath) {
+    var origSymbols = context.CDV_origSymbols;
+    if (origSymbols && (symbolPath in origSymbols)) {
+        return origSymbols[symbolPath];
+    }
+    var parts = symbolPath.split('.');
+    var obj = context;
+    for (var i = 0; i < parts.length; ++i) {
+        obj = obj && obj[parts[i]];
+    }
+    return obj;
+};
+
+exports.loadMatchingModules = function(matchingRegExp) {
+    for (var k in moduleMap) {
+        if (matchingRegExp.exec(k)) {
+            require(k);
+        }
+    }
+};
+
+exports.reset();
+
+
+});
+
+// file: lib\windowsphone\platform.js
+define("cordova/platform", function(require, exports, module) {
+
+var cordova = require('cordova'),
+      exec = require('cordova/exec');
+
+// specifically require the following patches :
+
+// localStorage+SessionStorage APIs
+require("cordova/plugin/windowsphone/DOMStorage");
+
+// Fix XHR calls to local file-system
+require("cordova/plugin/windowsphone/XHRPatch");
+
+
+module.exports = {
+    id: "windowsphone",
+    initialize:function() {
+        var modulemapper = require('cordova/modulemapper');
+
+        modulemapper.loadMatchingModules(/cordova.*\/symbols$/);
+        modulemapper.mapModules(window);
+
+        window.alert = window.alert || require("cordova/plugin/notification").alert;
+        window.confirm = window.confirm || require("cordova/plugin/notification").confirm;
+
+        // Inject a listener for the backbutton, and tell native to override the flag (true/false) when we have 1 or more, or 0, listeners
+        var backButtonChannel = cordova.addDocumentEventHandler('backbutton');
+        backButtonChannel.onHasSubscribersChange = function() {
+            exec(null, null, "CoreEvents", "overridebackbutton", [this.numHandlers == 1]);
+        };
+    },
+    clobbers: {
+        CordovaCommandResult: {
+            path:"cordova/plugin/windowsphone/CordovaCommandResult"
+        },
+        navigator: {
+            children: {
+                console: {
+                    path: "cordova/plugin/windowsphone/console"
+
+                }
+            }
+        },
+        console:{
+          path: "cordova/plugin/windowsphone/console"
+        },
+        FileTransfer: {
+            path: 'cordova/plugin/windowsphone/FileTransfer'
+        },
+        open : {
+            path: 'cordova/plugin/InAppBrowser'
+        }
+    }
+};
+
+});
+
+// file: lib\common\plugin\Acceleration.js
+define("cordova/plugin/Acceleration", function(require, exports, module) {
+
+var Acceleration = function(x, y, z, timestamp) {
+    this.x = x;
+    this.y = y;
+    this.z = z;
+    this.timestamp = timestamp || (new Date()).getTime();
+};
+
+module.exports = Acceleration;
+
+});
+
+// file: lib\common\plugin\Camera.js
+define("cordova/plugin/Camera", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    Camera = require('cordova/plugin/CameraConstants'),
+    CameraPopoverHandle = require('cordova/plugin/CameraPopoverHandle');
+
+var cameraExport = {};
+
+// Tack on the Camera Constants to the base camera plugin.
+for (var key in Camera) {
+    cameraExport[key] = Camera[key];
+}
+
+/**
+ * Gets a picture from source defined by "options.sourceType", and returns the
+ * image as defined by the "options.destinationType" option.
+
+ * The defaults are sourceType=CAMERA and destinationType=FILE_URI.
+ *
+ * @param {Function} successCallback
+ * @param {Function} errorCallback
+ * @param {Object} options
+ */
+cameraExport.getPicture = function(successCallback, errorCallback, options) {
+    argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
+    options = options || {};
+    var getValue = argscheck.getValue;
+
+    var quality = getValue(options.quality, 50);
+    var destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
+    var sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
+    var targetWidth = getValue(options.targetWidth, -1);
+    var targetHeight = getValue(options.targetHeight, -1);
+    var encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG);
+    var mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE);
+    var allowEdit = !!options.allowEdit;
+    var correctOrientation = !!options.correctOrientation;
+    var saveToPhotoAlbum = !!options.saveToPhotoAlbum;
+    var popoverOptions = getValue(options.popoverOptions, null);
+
+    var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
+                mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions];
+
+    exec(successCallback, errorCallback, "Camera", "takePicture", args);
+    return new CameraPopoverHandle();
+};
+
+cameraExport.cleanup = function(successCallback, errorCallback) {
+    exec(successCallback, errorCallback, "Camera", "cleanup", []);
+};
+
+module.exports = cameraExport;
+
+});
+
+// file: lib\common\plugin\CameraConstants.js
+define("cordova/plugin/CameraConstants", function(require, exports, module) {
+
+module.exports = {
+  DestinationType:{
+    DATA_URL: 0,         // Return base64 encoded string
+    FILE_URI: 1,         // Return file uri (content://media/external/images/media/2 for Android)
+    NATIVE_URI: 2        // Return native uri (eg. asset-library://... for iOS)
+  },
+  EncodingType:{
+    JPEG: 0,             // Return JPEG encoded image
+    PNG: 1               // Return PNG encoded image
+  },
+  MediaType:{
+    PICTURE: 0,          // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
+    VIDEO: 1,            // allow selection of video only, ONLY RETURNS URL
+    ALLMEDIA : 2         // allow selection from all media types
+  },
+  PictureSourceType:{
+    PHOTOLIBRARY : 0,    // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
+    CAMERA : 1,          // Take picture from camera
+    SAVEDPHOTOALBUM : 2  // Choose image from picture library (same as PHOTOLIBRARY for Android)
+  },
+  PopoverArrowDirection:{
+      ARROW_UP : 1,        // matches iOS UIPopoverArrowDirection constants to specify arrow location on popover
+      ARROW_DOWN : 2,
+      ARROW_LEFT : 4,
+      ARROW_RIGHT : 8,
+      ARROW_ANY : 15
+  }
+};
+
+});
+
+// file: lib\common\plugin\CameraPopoverHandle.js
+define("cordova/plugin/CameraPopoverHandle", function(require, exports, module) {
+
+var exec = require('cordova/exec');
+
+/**
+ * A handle to an image picker popover.
+ */
+var CameraPopoverHandle = function() {
+    this.setPosition = function(popoverOptions) {
+        console.log('CameraPopoverHandle.setPosition is only supported on iOS.');
+    };
+};
+
+module.exports = CameraPopoverHandle;
+
+});
+
+// file: lib\common\plugin\CameraPopoverOptions.js
+define("cordova/plugin/CameraPopoverOptions", function(require, exports, module) {
+
+var Camera = require('cordova/plugin/CameraConstants');
+
+/**
+ * Encapsulates options for iOS Popover image picker
+ */
+var CameraPopoverOptions = function(x,y,width,height,arrowDir){
+    // information of rectangle that popover should be anchored to
+    this.x = x || 0;
+    this.y = y || 32;
+    this.width = width || 320;
+    this.height = height || 480;
+    // The direction of the popover arrow
+    this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY;
+};
+
+module.exports = CameraPopoverOptions;
+
+});
+
+// file: lib\common\plugin\CaptureAudioOptions.js
+define("cordova/plugin/CaptureAudioOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all audio capture operation configuration options.
+ */
+var CaptureAudioOptions = function(){
+    // Upper limit of sound clips user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single sound clip in seconds.
+    this.duration = 0;
+    // The selected audio mode. Must match with one of the elements in supportedAudioModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureAudioOptions;
+
+});
+
+// file: lib\common\plugin\CaptureError.js
+define("cordova/plugin/CaptureError", function(require, exports, module) {
+
+/**
+ * The CaptureError interface encapsulates all errors in the Capture API.
+ */
+var CaptureError = function(c) {
+   this.code = c || null;
+};
+
+// Camera or microphone failed to capture image or sound.
+CaptureError.CAPTURE_INTERNAL_ERR = 0;
+// Camera application or audio capture application is currently serving other capture request.
+CaptureError.CAPTURE_APPLICATION_BUSY = 1;
+// Invalid use of the API (e.g. limit parameter has value less than one).
+CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
+// User exited camera application or audio capture application before capturing anything.
+CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
+// The requested capture operation is not supported.
+CaptureError.CAPTURE_NOT_SUPPORTED = 20;
+
+module.exports = CaptureError;
+
+});
+
+// file: lib\common\plugin\CaptureImageOptions.js
+define("cordova/plugin/CaptureImageOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all image capture operation configuration options.
+ */
+var CaptureImageOptions = function(){
+    // Upper limit of images user can take. Value must be equal or greater than 1.
+    this.limit = 1;
+    // The selected image mode. Must match with one of the elements in supportedImageModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureImageOptions;
+
+});
+
+// file: lib\common\plugin\CaptureVideoOptions.js
+define("cordova/plugin/CaptureVideoOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all video capture operation configuration options.
+ */
+var CaptureVideoOptions = function(){
+    // Upper limit of videos user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single video clip in seconds.
+    this.duration = 0;
+    // The selected video mode. Must match with one of the elements in supportedVideoModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureVideoOptions;
+
+});
+
+// file: lib\common\plugin\CompassError.js
+define("cordova/plugin/CompassError", function(require, exports, module) {
+
+/**
+ *  CompassError.
+ *  An error code assigned by an implementation when an error has occurred
+ * @constructor
+ */
+var CompassError = function(err) {
+    this.code = (err !== undefined ? err : null);
+};
+
+CompassError.COMPASS_INTERNAL_ERR = 0;
+CompassError.COMPASS_NOT_SUPPORTED = 20;
+
+module.exports = CompassError;
+
+});
+
+// file: lib\common\plugin\CompassHeading.js
+define("cordova/plugin/CompassHeading", function(require, exports, module) {
+
+var CompassHeading = function(magneticHeading, trueHeading, headingAccuracy, timestamp) {
+  this.magneticHeading = magneticHeading;
+  this.trueHeading = trueHeading;
+  this.headingAccuracy = headingAccuracy;
+  this.timestamp = timestamp || new Date().getTime();
+};
+
+module.exports = CompassHeading;
+
+});
+
+// file: lib\common\plugin\ConfigurationData.js
+define("cordova/plugin/ConfigurationData", function(require, exports, module) {
+
+/**
+ * Encapsulates a set of parameters that the capture device supports.
+ */
+function ConfigurationData() {
+    // The ASCII-encoded string in lower case representing the media type.
+    this.type = null;
+    // The height attribute represents height of the image or video in pixels.
+    // In the case of a sound clip this attribute has value 0.
+    this.height = 0;
+    // The width attribute represents width of the image or video in pixels.
+    // In the case of a sound clip this attribute has value 0
+    this.width = 0;
+}
+
+module.exports = ConfigurationData;
+
+});
+
+// file: lib\common\plugin\Connection.js
+define("cordova/plugin/Connection", function(require, exports, module) {
+
+/**
+ * Network status
+ */
+module.exports = {
+        UNKNOWN: "unknown",
+        ETHERNET: "ethernet",
+        WIFI: "wifi",
+        CELL_2G: "2g",
+        CELL_3G: "3g",
+        CELL_4G: "4g",
+        CELL:"cellular",
+        NONE: "none"
+};
+
+});
+
+// file: lib\common\plugin\Contact.js
+define("cordova/plugin/Contact", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    ContactError = require('cordova/plugin/ContactError'),
+    utils = require('cordova/utils');
+
+/**
+* Converts primitives into Complex Object
+* Currently only used for Date fields
+*/
+function convertIn(contact) {
+    var value = contact.birthday;
+    try {
+      contact.birthday = new Date(parseFloat(value));
+    } catch (exception){
+      console.log("Cordova Contact convertIn error: exception creating date.");
+    }
+    return contact;
+}
+
+/**
+* Converts Complex objects into primitives
+* Only conversion at present is for Dates.
+**/
+
+function convertOut(contact) {
+    var value = contact.birthday;
+    if (value !== null) {
+        // try to make it a Date object if it is not already
+        if (!utils.isDate(value)){
+            try {
+                value = new Date(value);
+            } catch(exception){
+                value = null;
+            }
+        }
+        if (utils.isDate(value)){
+            value = value.valueOf(); // convert to milliseconds
+        }
+        contact.birthday = value;
+    }
+    return contact;
+}
+
+/**
+* Contains information about a single contact.
+* @constructor
+* @param {DOMString} id unique identifier
+* @param {DOMString} displayName
+* @param {ContactName} name
+* @param {DOMString} nickname
+* @param {Array.<ContactField>} phoneNumbers array of phone numbers
+* @param {Array.<ContactField>} emails array of email addresses
+* @param {Array.<ContactAddress>} addresses array of addresses
+* @param {Array.<ContactField>} ims instant messaging user ids
+* @param {Array.<ContactOrganization>} organizations
+* @param {DOMString} birthday contact's birthday
+* @param {DOMString} note user notes about contact
+* @param {Array.<ContactField>} photos
+* @param {Array.<ContactField>} categories
+* @param {Array.<ContactField>} urls contact's web sites
+*/
+var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
+    ims, organizations, birthday, note, photos, categories, urls) {
+    this.id = id || null;
+    this.rawId = null;
+    this.displayName = displayName || null;
+    this.name = name || null; // ContactName
+    this.nickname = nickname || null;
+    this.phoneNumbers = phoneNumbers || null; // ContactField[]
+    this.emails = emails || null; // ContactField[]
+    this.addresses = addresses || null; // ContactAddress[]
+    this.ims = ims || null; // ContactField[]
+    this.organizations = organizations || null; // ContactOrganization[]
+    this.birthday = birthday || null;
+    this.note = note || null;
+    this.photos = photos || null; // ContactField[]
+    this.categories = categories || null; // ContactField[]
+    this.urls = urls || null; // ContactField[]
+};
+
+/**
+* Removes contact from device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.remove = function(successCB, errorCB) {
+    argscheck.checkArgs('FF', 'Contact.remove', arguments);
+    var fail = errorCB && function(code) {
+        errorCB(new ContactError(code));
+    };
+    if (this.id === null) {
+        fail(ContactError.UNKNOWN_ERROR);
+    }
+    else {
+        exec(successCB, fail, "Contacts", "remove", [this.id]);
+    }
+};
+
+/**
+* Creates a deep copy of this Contact.
+* With the contact ID set to null.
+* @return copy of this Contact
+*/
+Contact.prototype.clone = function() {
+    var clonedContact = utils.clone(this);
+    clonedContact.id = null;
+    clonedContact.rawId = null;
+
+    function nullIds(arr) {
+        if (arr) {
+            for (var i = 0; i < arr.length; ++i) {
+                arr[i].id = null;
+            }
+        }
+    }
+
+    // Loop through and clear out any id's in phones, emails, etc.
+    nullIds(clonedContact.phoneNumbers);
+    nullIds(clonedContact.emails);
+    nullIds(clonedContact.addresses);
+    nullIds(clonedContact.ims);
+    nullIds(clonedContact.organizations);
+    nullIds(clonedContact.categories);
+    nullIds(clonedContact.photos);
+    nullIds(clonedContact.urls);
+    return clonedContact;
+};
+
+/**
+* Persists contact to device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.save = function(successCB, errorCB) {
+    argscheck.checkArgs('FFO', 'Contact.save', arguments);
+    var fail = errorCB && function(code) {
+        errorCB(new ContactError(code));
+    };
+    var success = function(result) {
+        if (result) {
+            if (successCB) {
+                var fullContact = require('cordova/plugin/contacts').create(result);
+                successCB(convertIn(fullContact));
+            }
+        }
+        else {
+            // no Entry object returned
+            fail(ContactError.UNKNOWN_ERROR);
+        }
+    };
+    var dupContact = convertOut(utils.clone(this));
+    exec(success, fail, "Contacts", "save", [dupContact]);
+};
+
+
+module.exports = Contact;
+
+});
+
+// file: lib\common\plugin\ContactAddress.js
+define("cordova/plugin/ContactAddress", function(require, exports, module) {
+
+/**
+* Contact address.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code
+* @param formatted // NOTE: not a W3C standard
+* @param streetAddress
+* @param locality
+* @param region
+* @param postalCode
+* @param country
+*/
+
+var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
+    this.id = null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+    this.type = type || null;
+    this.formatted = formatted || null;
+    this.streetAddress = streetAddress || null;
+    this.locality = locality || null;
+    this.region = region || null;
+    this.postalCode = postalCode || null;
+    this.country = country || null;
+};
+
+module.exports = ContactAddress;
+
+});
+
+// file: lib\common\plugin\ContactError.js
+define("cordova/plugin/ContactError", function(require, exports, module) {
+
+/**
+ *  ContactError.
+ *  An error code assigned by an implementation when an error has occurred
+ * @constructor
+ */
+var ContactError = function(err) {
+    this.code = (typeof err != 'undefined' ? err : null);
+};
+
+/**
+ * Error codes
+ */
+ContactError.UNKNOWN_ERROR = 0;
+ContactError.INVALID_ARGUMENT_ERROR = 1;
+ContactError.TIMEOUT_ERROR = 2;
+ContactError.PENDING_OPERATION_ERROR = 3;
+ContactError.IO_ERROR = 4;
+ContactError.NOT_SUPPORTED_ERROR = 5;
+ContactError.PERMISSION_DENIED_ERROR = 20;
+
+module.exports = ContactError;
+
+});
+
+// file: lib\common\plugin\ContactField.js
+define("cordova/plugin/ContactField", function(require, exports, module) {
+
+/**
+* Generic contact field.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param type
+* @param value
+* @param pref
+*/
+var ContactField = function(type, value, pref) {
+    this.id = null;
+    this.type = (type && type.toString()) || null;
+    this.value = (value && value.toString()) || null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+};
+
+module.exports = ContactField;
+
+});
+
+// file: lib\common\plugin\ContactFindOptions.js
+define("cordova/plugin/ContactFindOptions", function(require, exports, module) {
+
+/**
+ * ContactFindOptions.
+ * @constructor
+ * @param filter used to match contacts against
+ * @param multiple boolean used to determine if more than one contact should be returned
+ */
+
+var ContactFindOptions = function(filter, multiple) {
+    this.filter = filter || '';
+    this.multiple = (typeof multiple != 'undefined' ? multiple : false);
+};
+
+module.exports = ContactFindOptions;
+
+});
+
+// file: lib\common\plugin\ContactName.js
+define("cordova/plugin/ContactName", function(require, exports, module) {
+
+/**
+* Contact name.
+* @constructor
+* @param formatted // NOTE: not part of W3C standard
+* @param familyName
+* @param givenName
+* @param middle
+* @param prefix
+* @param suffix
+*/
+var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
+    this.formatted = formatted || null;
+    this.familyName = familyName || null;
+    this.givenName = givenName || null;
+    this.middleName = middle || null;
+    this.honorificPrefix = prefix || null;
+    this.honorificSuffix = suffix || null;
+};
+
+module.exports = ContactName;
+
+});
+
+// file: lib\common\plugin\ContactOrganization.js
+define("cordova/plugin/ContactOrganization", function(require, exports, module) {
+
+/**
+* Contact organization.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param name
+* @param dept
+* @param title
+* @param startDate
+* @param endDate
+* @param location
+* @param desc
+*/
+
+var ContactOrganization = function(pref, type, name, dept, title) {
+    this.id = null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+    this.type = type || null;
+    this.name = name || null;
+    this.department = dept || null;
+    this.title = title || null;
+};
+
+module.exports = ContactOrganization;
+
+});
+
+// file: lib\common\plugin\Coordinates.js
+define("cordova/plugin/Coordinates", function(require, exports, module) {
+
+/**
+ * This class contains position information.
+ * @param {Object} lat
+ * @param {Object} lng
+ * @param {Object} alt
+ * @param {Object} acc
+ * @param {Object} head
+ * @param {Object} vel
+ * @param {Object} altacc
+ * @constructor
+ */
+var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
+    /**
+     * The latitude of the position.
+     */
+    this.latitude = lat;
+    /**
+     * The longitude of the position,
+     */
+    this.longitude = lng;
+    /**
+     * The accuracy of the position.
+     */
+    this.accuracy = acc;
+    /**
+     * The altitude of the position.
+     */
+    this.altitude = (alt !== undefined ? alt : null);
+    /**
+     * The direction the device is moving at the position.
+     */
+    this.heading = (head !== undefined ? head : null);
+    /**
+     * The velocity with which the device is moving at the position.
+     */
+    this.speed = (vel !== undefined ? vel : null);
+
+    if (this.speed === 0 || this.speed === null) {
+        this.heading = NaN;
+    }
+
+    /**
+     * The altitude accuracy of the position.
+     */
+    this.altitudeAccuracy = (altacc !== undefined) ? altacc : null;
+};
+
+module.exports = Coordinates;
+
+});
+
+// file: lib\common\plugin\DirectoryEntry.js
+define("cordova/plugin/DirectoryEntry", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    Entry = require('cordova/plugin/Entry'),
+    FileError = require('cordova/plugin/FileError'),
+    DirectoryReader = require('cordova/plugin/DirectoryReader');
+
+/**
+ * An interface representing a directory on the file system.
+ *
+ * {boolean} isFile always false (readonly)
+ * {boolean} isDirectory always true (readonly)
+ * {DOMString} name of the directory, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the directory (readonly)
+ * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly)
+ */
+var DirectoryEntry = function(name, fullPath) {
+     DirectoryEntry.__super__.constructor.call(this, false, true, name, fullPath);
+};
+
+utils.extend(DirectoryEntry, Entry);
+
+/**
+ * Creates a new DirectoryReader to read entries from this directory
+ */
+DirectoryEntry.prototype.createReader = function() {
+    return new DirectoryReader(this.fullPath);
+};
+
+/**
+ * Creates or looks up a directory
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
+ * @param {Flags} options to create or exclusively create the directory
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
+    argscheck.checkArgs('sOFF', 'DirectoryEntry.getDirectory', arguments);
+    var win = successCallback && function(result) {
+        var entry = new DirectoryEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]);
+};
+
+/**
+ * Deletes a directory and all of it's contents
+ *
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'DirectoryEntry.removeRecursively', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]);
+};
+
+/**
+ * Creates or looks up a file
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
+ * @param {Flags} options to create or exclusively create the file
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
+    argscheck.checkArgs('sOFF', 'DirectoryEntry.getFile', arguments);
+    var win = successCallback && function(result) {
+        var FileEntry = require('cordova/plugin/FileEntry');
+        var entry = new FileEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getFile", [this.fullPath, path, options]);
+};
+
+module.exports = DirectoryEntry;
+
+});
+
+// file: lib\common\plugin\DirectoryReader.js
+define("cordova/plugin/DirectoryReader", function(require, exports, module) {
+
+var exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError') ;
+
+/**
+ * An interface that lists the files and directories in a directory.
+ */
+function DirectoryReader(path) {
+    this.path = path || null;
+}
+
+/**
+ * Returns a list of entries from a directory.
+ *
+ * @param {Function} successCallback is called with a list of entries
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
+    var win = typeof successCallback !== 'function' ? null : function(result) {
+        var retVal = [];
+        for (var i=0; i<result.length; i++) {
+            var entry = null;
+            if (result[i].isDirectory) {
+                entry = new (require('cordova/plugin/DirectoryEntry'))();
+            }
+            else if (result[i].isFile) {
+                entry = new (require('cordova/plugin/FileEntry'))();
+            }
+            entry.isDirectory = result[i].isDirectory;
+            entry.isFile = result[i].isFile;
+            entry.name = result[i].name;
+            entry.fullPath = result[i].fullPath;
+            retVal.push(entry);
+        }
+        successCallback(retVal);
+    };
+    var fail = typeof errorCallback !== 'function' ? null : function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "readEntries", [this.path]);
+};
+
+module.exports = DirectoryReader;
+
+});
+
+// file: lib\common\plugin\Entry.js
+define("cordova/plugin/Entry", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError'),
+    Metadata = require('cordova/plugin/Metadata');
+
+/**
+ * Represents a file or directory on the local file system.
+ *
+ * @param isFile
+ *            {boolean} true if Entry is a file (readonly)
+ * @param isDirectory
+ *            {boolean} true if Entry is a directory (readonly)
+ * @param name
+ *            {DOMString} name of the file or directory, excluding the path
+ *            leading to it (readonly)
+ * @param fullPath
+ *            {DOMString} the absolute full path to the file or directory
+ *            (readonly)
+ */
+function Entry(isFile, isDirectory, name, fullPath, fileSystem) {
+    this.isFile = !!isFile;
+    this.isDirectory = !!isDirectory;
+    this.name = name || '';
+    this.fullPath = fullPath || '';
+    this.filesystem = fileSystem || null;
+}
+
+/**
+ * Look up the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ */
+Entry.prototype.getMetadata = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.getMetadata', arguments);
+    var success = successCallback && function(lastModified) {
+        var metadata = new Metadata(lastModified);
+        successCallback(metadata);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+
+    exec(success, fail, "File", "getMetadata", [this.fullPath]);
+};
+
+/**
+ * Set the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ * @param metadataObject
+ *            {Object} keys and values to set
+ */
+Entry.prototype.setMetadata = function(successCallback, errorCallback, metadataObject) {
+    argscheck.checkArgs('FFO', 'Entry.setMetadata', arguments);
+    exec(successCallback, errorCallback, "File", "setMetadata", [this.fullPath, metadataObject]);
+};
+
+/**
+ * Move a file or directory to a new location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to move this entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new DirectoryEntry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
+    argscheck.checkArgs('oSFF', 'Entry.moveTo', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    // source path
+    var srcPath = this.fullPath,
+        // entry name
+        name = newName || this.name,
+        success = function(entry) {
+            if (entry) {
+                if (successCallback) {
+                    // create appropriate Entry object
+                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
+                    successCallback(result);
+                }
+            }
+            else {
+                // no Entry object returned
+                fail && fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+
+    // copy
+    exec(success, fail, "File", "moveTo", [srcPath, parent.fullPath, name]);
+};
+
+/**
+ * Copy a directory to a different location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to copy the entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new Entry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
+    argscheck.checkArgs('oSFF', 'Entry.copyTo', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+
+        // source path
+    var srcPath = this.fullPath,
+        // entry name
+        name = newName || this.name,
+        // success callback
+        success = function(entry) {
+            if (entry) {
+                if (successCallback) {
+                    // create appropriate Entry object
+                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
+                    successCallback(result);
+                }
+            }
+            else {
+                // no Entry object returned
+                fail && fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+
+    // copy
+    exec(success, fail, "File", "copyTo", [srcPath, parent.fullPath, name]);
+};
+
+/**
+ * Return a URL that can be used to identify this entry.
+ */
+Entry.prototype.toURL = function() {
+    // fullPath attribute contains the full URL
+    return this.fullPath;
+};
+
+/**
+ * Returns a URI that can be used to identify this entry.
+ *
+ * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
+ * @return uri
+ */
+Entry.prototype.toURI = function(mimeType) {
+    console.log("DEPRECATED: Update your code to use 'toURL'");
+    // fullPath attribute contains the full URI
+    return this.toURL();
+};
+
+/**
+ * Remove a file or directory. It is an error to attempt to delete a
+ * directory that is not empty. It is an error to attempt to delete a
+ * root directory of a file system.
+ *
+ * @param successCallback {Function} called with no parameters
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.remove = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.remove', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, "File", "remove", [this.fullPath]);
+};
+
+/**
+ * Look up the parent DirectoryEntry of this entry.
+ *
+ * @param successCallback {Function} called with the parent DirectoryEntry object
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.getParent = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.getParent', arguments);
+    var win = successCallback && function(result) {
+        var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
+        var entry = new DirectoryEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getParent", [this.fullPath]);
+};
+
+module.exports = Entry;
+
+});
+
+// file: lib\common\plugin\File.js
+define("cordova/plugin/File", function(require, exports, module) {
+
+/**
+ * Constructor.
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+
+var File = function(name, fullPath, type, lastModifiedDate, size){
+    this.name = name || '';
+    this.fullPath = fullPath || null;
+    this.type = type || null;
+    this.lastModifiedDate = lastModifiedDate || null;
+    this.size = size || 0;
+
+    // These store the absolute start and end for slicing the file.
+    this.start = 0;
+    this.end = this.size;
+};
+
+/**
+ * Returns a "slice" of the file. Since Cordova Files don't contain the actual
+ * content, this really returns a File with adjusted start and end.
+ * Slices of slices are supported.
+ * start {Number} The index at which to start the slice (inclusive).
+ * end {Number} The index at which to end the slice (exclusive).
+ */
+File.prototype.slice = function(start, end) {
+    var size = this.end - this.start;
+    var newStart = 0;
+    var newEnd = size;
+    if (arguments.length) {
+        if (start < 0) {
+            newStart = Math.max(size + start, 0);
+        } else {
+            newStart = Math.min(size, start);
+        }
+    }
+
+    if (arguments.length >= 2) {
+        if (end < 0) {
+            newEnd = Math.max(size + end, 0);
+        } else {
+            newEnd = Math.min(end, size);
+        }
+    }
+
+    var newFile = new File(this.name, this.fullPath, this.type, this.lastModifiedData, this.size);
+    newFile.start = this.start + newStart;
+    newFile.end = this.start + newEnd;
+    return newFile;
+};
+
+
+module.exports = File;
+
+});
+
+// file: lib\common\plugin\FileEntry.js
+define("cordova/plugin/FileEntry", function(require, exports, module) {
+
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    Entry = require('cordova/plugin/Entry'),
+    FileWriter = require('cordova/plugin/FileWriter'),
+    File = require('cordova/plugin/File'),
+    FileError = require('cordova/plugin/FileError');
+
+/**
+ * An interface representing a file on the file system.
+ *
+ * {boolean} isFile always true (readonly)
+ * {boolean} isDirectory always false (readonly)
+ * {DOMString} name of the file, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the file (readonly)
+ * {FileSystem} filesystem on which the file resides (readonly)
+ */
+var FileEntry = function(name, fullPath) {
+     FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath]);
+};
+
+utils.extend(FileEntry, Entry);
+
+/**
+ * Creates a new FileWriter associated with the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new FileWriter
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.createWriter = function(successCallback, errorCallback) {
+    this.file(function(filePointer) {
+        var writer = new FileWriter(filePointer);
+
+        if (writer.fileName === null || writer.fileName === "") {
+            errorCallback && errorCallback(new FileError(FileError.INVALID_STATE_ERR));
+        } else {
+            successCallback && successCallback(writer);
+        }
+    }, errorCallback);
+};
+
+/**
+ * Returns a File that represents the current state of the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new File object
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.file = function(successCallback, errorCallback) {
+    var win = successCallback && function(f) {
+        var file = new File(f.name, f.fullPath, f.type, f.lastModifiedDate, f.size);
+        successCallback(file);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getFileMetadata", [this.fullPath]);
+};
+
+
+module.exports = FileEntry;
+
+});
+
+// file: lib\common\plugin\FileError.js
+define("cordova/plugin/FileError", function(require, exports, module) {
+
+/**
+ * FileError
+ */
+function FileError(error) {
+  this.code = error || null;
+}
+
+// File error codes
+// Found in DOMException
+FileError.NOT_FOUND_ERR = 1;
+FileError.SECURITY_ERR = 2;
+FileError.ABORT_ERR = 3;
+
+// Added by File API specification
+FileError.NOT_READABLE_ERR = 4;
+FileError.ENCODING_ERR = 5;
+FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
+FileError.INVALID_STATE_ERR = 7;
+FileError.SYNTAX_ERR = 8;
+FileError.INVALID_MODIFICATION_ERR = 9;
+FileError.QUOTA_EXCEEDED_ERR = 10;
+FileError.TYPE_MISMATCH_ERR = 11;
+FileError.PATH_EXISTS_ERR = 12;
+
+module.exports = FileError;
+
+});
+
+// file: lib\common\plugin\FileReader.js
+define("cordova/plugin/FileReader", function(require, exports, module) {
+
+var exec = require('cordova/exec'),
+    modulemapper = require('cordova/modulemapper'),
+    utils = require('cordova/utils'),
+    File = require('cordova/plugin/File'),
+    FileError = require('cordova/plugin/FileError'),
+    ProgressEvent = require('cordova/plugin/ProgressEvent'),
+    origFileReader = modulemapper.getOriginalSymbol(this, 'FileReader');
+
+/**
+ * This class reads the mobile device file system.
+ *
+ * For Android:
+ *      The root directory is the root of the file system.
+ *      To read from the SD card, the file name is "sdcard/my_file.txt"
+ * @constructor
+ */
+var FileReader = function() {
+    this._readyState = 0;
+    this._error = null;
+    this._result = null;
+    this._fileName = '';
+    this._realReader = origFileReader ? new origFileReader() : {};
+};
+
+// States
+FileReader.EMPTY = 0;
+FileReader.LOADING = 1;
+FileReader.DONE = 2;
+
+utils.defineGetter(FileReader.prototype, 'readyState', function() {
+    return this._fileName ? this._readyState : this._realReader.readyState;
+});
+
+utils.defineGetter(FileReader.prototype, 'error', function() {
+    return this._fileName ? this._error: this._realReader.error;
+});
+
+utils.defineGetter(FileReader.prototype, 'result', function() {
+    return this._fileName ? this._result: this._realReader.result;
+});
+
+function defineEvent(eventName) {
+    utils.defineGetterSetter(FileReader.prototype, eventName, function() {
+        return this._realReader[eventName] || null;
+    }, function(value) {
+        this._realReader[eventName] = value;
+    });
+}
+defineEvent('onloadstart');    // When the read starts.
+defineEvent('onprogress');     // While reading (and decoding) file or fileBlob data, and reporting partial file data (progress.loaded/progress.total)
+defineEvent('onload');         // When the read has successfully completed.
+defineEvent('onerror');        // When the read has failed (see errors).
+defineEvent('onloadend');      // When the request has completed (either in success or failure).
+defineEvent('onabort');        // When the read has been aborted. For instance, by invoking the abort() method.
+
+function initRead(reader, file) {
+    // Already loading something
+    if (reader.readyState == FileReader.LOADING) {
+      throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    reader._result = null;
+    reader._error = null;
+    reader._readyState = FileReader.LOADING;
+
+    if (typeof file == 'string') {
+        // Deprecated in Cordova 2.4.
+        console.warning('Using a string argument with FileReader.readAs functions is deprecated.');
+        reader._fileName = file;
+    } else if (typeof file.fullPath == 'string') {
+        reader._fileName = file.fullPath;
+    } else {
+        reader._fileName = '';
+        return true;
+    }
+
+    reader.onloadstart && reader.onloadstart(new ProgressEvent("loadstart", {target:reader}));
+}
+
+/**
+ * Abort reading file.
+ */
+FileReader.prototype.abort = function() {
+    if (origFileReader && !this._fileName) {
+        return this._realReader.abort();
+    }
+    this._result = null;
+
+    if (this._readyState == FileReader.DONE || this._readyState == FileReader.EMPTY) {
+      return;
+    }
+
+    this._readyState = FileReader.DONE;
+
+    // If abort callback
+    if (typeof this.onabort === 'function') {
+        this.onabort(new ProgressEvent('abort', {target:this}));
+    }
+    // If load end callback
+    if (typeof this.onloadend === 'function') {
+        this.onloadend(new ProgressEvent('loadend', {target:this}));
+    }
+};
+
+/**
+ * Read text file.
+ *
+ * @param file          {File} File object containing file properties
+ * @param encoding      [Optional] (see http://www.iana.org/assignments/character-sets)
+ */
+FileReader.prototype.readAsText = function(file, encoding) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsText(file, encoding);
+    }
+
+    // Default encoding is UTF-8
+    var enc = encoding ? encoding : "UTF-8";
+    var me = this;
+    var execArgs = [this._fileName, enc];
+
+    // Maybe add slice parameters.
+    if (file.end < file.size) {
+        execArgs.push(file.start, file.end);
+    } else if (file.start > 0) {
+        execArgs.push(file.start);
+    }
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // Save result
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // null result
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsText", execArgs);
+};
+
+
+/**
+ * Read file and return data as a base64 encoded data url.
+ * A data url is of the form:
+ *      data:[<mediatype>][;base64],<data>
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsDataURL = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsDataURL(file);
+    }
+
+    var me = this;
+    var execArgs = [this._fileName];
+
+    // Maybe add slice parameters.
+    if (file.end < file.size) {
+        execArgs.push(file.start, file.end);
+    } else if (file.start > 0) {
+        execArgs.push(file.start);
+    }
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // Save result
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsDataURL", execArgs);
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsBinaryString = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsBinaryString(file);
+    }
+    // TODO - Can't return binary data to browser.
+    console.log('method "readAsBinaryString" is not supported at this time.');
+    this.abort();
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsArrayBuffer = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsArrayBuffer(file);
+    }
+    // TODO - Can't return binary data to browser.
+    console.log('This method is not supported at this time.');
+    this.abort();
+};
+
+module.exports = FileReader;
+
+});
+
+// file: lib\common\plugin\FileSystem.js
+define("cordova/plugin/FileSystem", function(require, exports, module) {
+
+var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
+
+/**
+ * An interface representing a file system
+ *
+ * @constructor
+ * {DOMString} name the unique name of the file system (readonly)
+ * {DirectoryEntry} root directory of the file system (readonly)
+ */
+var FileSystem = function(name, root) {
+    this.name = name || null;
+    if (root) {
+        this.root = new DirectoryEntry(root.name, root.fullPath);
+    }
+};
+
+module.exports = FileSystem;
+
+});
+
+// file: lib\common\plugin\FileTransfer.js
+define("cordova/plugin/FileTransfer", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    FileTransferError = require('cordova/plugin/FileTransferError'),
+    ProgressEvent = require('cordova/plugin/ProgressEvent');
+
+function newProgressEvent(result) {
+    var pe = new ProgressEvent();
+    pe.lengthComputable = result.lengthComputable;
+    pe.loaded = result.loaded;
+    pe.total = result.total;
+    return pe;
+}
+
+var idCounter = 0;
+
+/**
+ * FileTransfer uploads a file to a remote server.
+ * @constructor
+ */
+var FileTransfer = function() {
+    this._id = ++idCounter;
+    this.onprogress = null; // optional callback
+};
+
+/**
+* Given an absolute file path, uploads a file on the device to a remote server
+* using a multipart HTTP request.
+* @param filePath {String}           Full path of the file on the device
+* @param server {String}             URL of the server to receive the file
+* @param successCallback (Function}  Callback to be invoked when upload has completed
+* @param errorCallback {Function}    Callback to be invoked upon error
+* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
+* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
+*/
+FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
+    argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments);
+    // check for options
+    var fileKey = null;
+    var fileName = null;
+    var mimeType = null;
+    var params = null;
+    var chunkedMode = true;
+    var headers = null;
+    if (options) {
+        fileKey = options.fileKey;
+        fileName = options.fileName;
+        mimeType = options.mimeType;
+        headers = options.headers;
+        if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
+            chunkedMode = options.chunkedMode;
+        }
+        if (options.params) {
+            params = options.params;
+        }
+        else {
+            params = {};
+        }
+    }
+
+    var fail = errorCallback && function(e) {
+        var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
+        errorCallback(error);
+    };
+
+    var self = this;
+    var win = function(result) {
+        if (typeof result.lengthComputable != "undefined") {
+            if (self.onprogress) {
+                self.onprogress(newProgressEvent(result));
+            }
+        } else {
+            successCallback && successCallback(result);
+        }
+    };
+    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id]);
+};
+
+/**
+ * Downloads a file form a given URL and saves it to the specified directory.
+ * @param source {String}          URL of the server to receive the file
+ * @param target {String}         Full path of the file on the device
+ * @param successCallback (Function}  Callback to be invoked when upload has completed
+ * @param errorCallback {Function}    Callback to be invoked upon error
+ * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
+ */
+FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts) {
+    argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments);
+    var self = this;
+    var win = function(result) {
+        if (typeof result.lengthComputable != "undefined") {
+            if (self.onprogress) {
+                return self.onprogress(newProgressEvent(result));
+            }
+        } else if (successCallback) {
+            var entry = null;
+            if (result.isDirectory) {
+                entry = new (require('cordova/plugin/DirectoryEntry'))();
+            }
+            else if (result.isFile) {
+                entry = new (require('cordova/plugin/FileEntry'))();
+            }
+            entry.isDirectory = result.isDirectory;
+            entry.isFile = result.isFile;
+            entry.name = result.name;
+            entry.fullPath = result.fullPath;
+            successCallback(entry);
+        }
+    };
+
+    var fail = errorCallback && function(e) {
+        var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
+        errorCallback(error);
+    };
+
+    exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id]);
+};
+
+/**
+ * Aborts the ongoing file transfer on this object
+ * @param successCallback {Function}  Callback to be invoked upon success
+ * @param errorCallback {Function}    Callback to be invoked upon error
+ */
+FileTransfer.prototype.abort = function(successCallback, errorCallback) {
+    exec(successCallback, errorCallback, 'FileTransfer', 'abort', [this._id]);
+};
+
+module.exports = FileTransfer;
+
+});
+
+// file: lib\common\plugin\FileTransferError.js
+define("cordova/plugin/FileTransferError", function(require, exports, module) {
+
+/**
+ * FileTransferError
+ * @constructor
+ */
+var FileTransferError = function(code, source, target, status, body) {
+    this.code = code || null;
+    this.source = source || null;
+    this.target = target || null;
+    this.http_status = status || null;
+    this.body = body || null;
+};
+
+FileTransferError.FILE_NOT_FOUND_ERR = 1;
+FileTransferError.INVALID_URL_ERR = 2;
+FileTransferError.CONNECTION_ERR = 3;
+FileTransferError.ABORT_ERR = 4;
+
+module.exports = FileTransferError;
+
+});
+
+// file: lib\common\plugin\FileUploadOptions.js
+define("cordova/plugin/FileUploadOptions", function(require, exports, module) {
+
+/**
+ * Options to customize the HTTP request used to upload files.
+ * @constructor
+ * @param fileKey {String}   Name of file request parameter.
+ * @param fileName {String}  Filename to be used by the server. Defaults to image.jpg.
+ * @param mimeType {String}  Mimetype of the uploaded file. Defaults to image/jpeg.
+ * @param params {Object}    Object with key: value params to send to the server.
+ * @param headers {Object}   Keys are header names, values are header values. Multiple
+ *                           headers of the same name are not supported.
+ */
+var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers) {
+    this.fileKey = fileKey || null;
+    this.fileName = fileName || null;
+    this.mimeType = mimeType || null;
+    this.params = params || null;
+    this.headers = headers || null;
+};
+
+module.exports = FileUploadOptions;
+
+});
+
+// file: lib\common\plugin\FileUploadResult.js
+define("cordova/plugin/FileUploadResult", function(require, exports, module) {
+
+/**
+ * FileUploadResult
+ * @constructor
+ */
+var FileUploadResult = function() {
+    this.bytesSent = 0;
+    this.responseCode = null;
+    this.response = null;
+};
+
+module.exports = FileUploadResult;
+
+});
+
+// file: lib\common\plugin\FileWriter.js
+define("cordova/plugin/FileWriter", function(require, exports, module) {
+
+var exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError'),
+    ProgressEvent = require('cordova/plugin/ProgressEvent');
+
+/**
+ * This class writes to the mobile device file system.
+ *
+ * For Android:
+ *      The root directory is the root of the file system.
+ *      To write to the SD card, the file name is "sdcard/my_file.txt"
+ *
+ * @constructor
+ * @param file {File} File object containing file properties
+ * @param append if true write to the end of the file, otherwise overwrite the file
+ */
+var FileWriter = function(file) {
+    this.fileName = "";
+    this.length = 0;
+    if (file) {
+        this.fileName = file.fullPath || file;
+        this.length = file.size || 0;
+    }
+    // default is to write at the beginning of the file
+    this.position = 0;
+
+    this.readyState = 0; // EMPTY
+
+    this.result = null;
+
+    // Error
+    this.error = null;
+
+    // Event handlers
+    this.onwritestart = null;   // When writing starts
+    this.onprogress = null;     // While writing the file, and reporting partial file data
+    this.onwrite = null;        // When the write has successfully completed.
+    this.onwriteend = null;     // When the request has completed (either in success or failure).
+    this.onabort = null;        // When the write has been aborted. For instance, by invoking the abort() method.
+    this.

<TRUNCATED>

[11/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/filetransfer.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/filetransfer.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/filetransfer.tests.js
new file mode 100644
index 0000000..8c1cd35
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/filetransfer.tests.js
@@ -0,0 +1,513 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+describe('FileTransfer', function() {
+    // https://github.com/don/cordova-filetransfer
+    var server = "http://cordova-filetransfer.jitsu.com";
+
+    // deletes and re-creates the specified content
+    var writeFile = function(fileName, fileContent, success, error) {
+        deleteFile(fileName, function() {
+            root.getFile(fileName, {create: true}, function(fileEntry) {
+                fileEntry.createWriter(function (writer) {
+
+                    writer.onwrite = function(evt) {
+                        success(fileEntry);
+                    };
+
+                    writer.onabort = function(evt) {
+                        error(evt);
+                    };
+
+                    writer.error = function(evt) {
+                        error(evt);
+                    };
+
+                    writer.write(fileContent + "\n");
+                }, error);
+            }, error);
+        });
+    };
+
+    var readFileEntry = function(entry, success, error) {
+        entry.file(function(file) {
+            var reader = new FileReader();
+            reader.onerror = error;
+            reader.onload = function(e) {
+                success(reader.result);
+            };
+            reader.readAsText(file);
+        }, error);
+    };
+
+    var getMalformedUrl = function() {
+        if (device.platform.match(/Android/i)) {
+            // bad protocol causes a MalformedUrlException on Android
+            return "httpssss://example.com";
+        } else {
+            // iOS doesn't care about protocol, space in hostname causes error
+            return "httpssss://exa mple.com";
+        }
+    };
+
+    // deletes file, if it exists, then invokes callback
+    var deleteFile = function(fileName, callback) {
+        callback = callback || function() {};
+        var spy = jasmine.createSpy().andCallFake(callback);
+        root.getFile(fileName, null,
+            // remove file system entry
+            function(entry) {
+                entry.remove(spy, spy);
+            },
+            // doesn't exist
+            spy);
+        waitsFor(function() { return spy.wasCalled; }, Tests.TEST_TIMEOUT);
+    };
+
+    it("should exist and be constructable", function() {
+        var ft = new FileTransfer();
+        expect(ft).toBeDefined();
+    });
+    it("should contain proper functions", function() {
+        var ft = new FileTransfer();
+        expect(typeof ft.upload).toBe('function');
+        expect(typeof ft.download).toBe('function');
+    });
+    describe('FileTransferError', function() {
+        it("FileTransferError constants should be defined", function() {
+            expect(FileTransferError.FILE_NOT_FOUND_ERR).toBe(1);
+            expect(FileTransferError.INVALID_URL_ERR).toBe(2);
+            expect(FileTransferError.CONNECTION_ERR).toBe(3);
+        });
+    });
+
+    describe('download method', function() {
+
+        // NOTE: if download tests are failing, check the white list
+        // Android
+        //   <access origin="httpssss://example.com"/>
+        //   <access origin="apache.org" subdomains="true" />
+        //   <access origin="cordova-filetransfer.jitsu.com"/>
+        // iOS
+        //   # Cordova.plist
+        //   ExternalHosts
+        //     - Item 1 String cordova-filetransfer.jitsu.com
+        //     - Item 2 String *.apache.org
+
+        it("should be able to download a file using http", function() {
+            var fail = createDoNotCallSpy('downloadFail');
+            var remoteFile = server + "/robots.txt"
+            var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1);
+            var lastProgressEvent = null;
+
+            var downloadWin = jasmine.createSpy().andCallFake(function(entry) {
+                expect(entry.name).toBe(localFileName);
+                expect(lastProgressEvent.loaded).toBeGreaterThan(1);
+            });
+
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.onprogress = function(e) {
+                    lastProgressEvent = e;
+                };
+                ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, fail);
+            });
+
+            waitsForAny(downloadWin, fail);
+        });
+        it("should be able to download a file using https", function() {
+            var remoteFile = "https://www.apache.org/licenses/";
+            var localFileName = 'httpstest.html';
+            var downloadFail = createDoNotCallSpy('downloadFail', 'Ensure ' + remoteFile + ' is in the white-list');
+            var fileFail = createDoNotCallSpy('fileFail');
+            var downloadWin = function(entry) {
+                readFileEntry(entry, fileWin, fileFail);
+            };
+            var fileWin = jasmine.createSpy().andCallFake(function(content) {
+                expect(content).toMatch(/The Apache Software Foundation/); 
+            });
+
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail);
+            });
+
+            waitsForAny(fileWin, downloadFail, fileFail);
+        });
+        it("should be stopped by abort() right away", function() {
+            var downloadWin = createDoNotCallSpy('downloadWin');
+            var remoteFile = 'http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3';
+            var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1);
+            var startTime = +new Date();
+
+            var downloadFail = jasmine.createSpy().andCallFake(function(e) {
+                expect(e.code).toBe(FileTransferError.ABORT_ERR);
+                expect(new Date() - startTime).toBeLessThan(300);
+            });
+
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.abort(); // should be a no-op.
+                ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail);
+                ft.abort();
+                ft.abort(); // should be a no-op.
+            });
+
+            waitsForAny(downloadWin, downloadFail);
+        });
+        it("should get http status on failure", function() {
+            var downloadWin = createDoNotCallSpy('downloadWin');
+
+            var remoteFile = server + "/404";
+            var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1);
+            var downloadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.http_status).toBe(404);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+            });
+
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail);
+            });
+
+            waitsForAny(downloadWin, downloadFail);
+        });
+        it("should handle malformed urls", function() {
+            var downloadWin = createDoNotCallSpy('downloadWin');
+
+            var remoteFile = getMalformedUrl();
+            var localFileName = "download_malformed_url.txt";
+            var downloadFail = jasmine.createSpy().andCallFake(function(error) {
+                // Note: Android needs the bad protocol to be added to the access list
+                // <access origin=".*"/> won't match because ^https?:// is prepended to the regex
+                // The bad protocol must begin with http to avoid automatic prefix
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+                expect(error.code).toBe(FileTransferError.INVALID_URL_ERR);
+            });
+
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail);
+            });
+
+            waitsForAny(downloadWin, downloadFail);
+        });
+        it("should handle unknown host", function() {
+            var downloadWin = createDoNotCallSpy('downloadWin');
+
+            var remoteFile = "http://foobar.apache.org/index.html";
+            var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1);
+            var downloadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.CONNECTION_ERR);
+            });
+
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail);
+            });
+
+            waitsForAny(downloadWin, downloadFail);
+        });
+        it("should handle bad file path", function() {
+            var downloadWin = createDoNotCallSpy('downloadWin');
+
+            var remoteFile = server;
+            var badFilePath = "c:\\54321";
+            var downloadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR);
+            });
+
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.download(remoteFile, badFilePath, downloadWin, downloadFail);
+            });
+
+            waitsForAny(downloadWin, downloadFail);
+        });
+    });
+    describe('upload method', function() {
+
+        it("should be able to upload a file", function() {
+            var remoteFile = server + "/upload";
+            var localFileName = "upload.txt";
+            var fileContents = 'This file should upload';
+
+            var fileFail = createDoNotCallSpy('fileFail');
+            var uploadFail = createDoNotCallSpy('uploadFail', "Ensure " + remoteFile + " is in the white list");
+            var lastProgressEvent = null;
+
+            var uploadWin = jasmine.createSpy().andCallFake(function(uploadResult) {
+                expect(uploadResult.bytesSent).toBeGreaterThan(0);
+                expect(uploadResult.responseCode).toBe(200);
+                expect(uploadResult.response).toMatch(/fields:\s*{\s*value1.*/);
+            });
+
+            var fileWin = function(fileEntry) {
+                ft = new FileTransfer();
+
+                var options = new FileUploadOptions();
+                options.fileKey = "file";
+                options.fileName = localFileName;
+                options.mimeType = "text/plain";
+
+                var params = new Object();
+                params.value1 = "test";
+                params.value2 = "param";
+                options.params = params;
+
+                ft.onprogress = function(e) {
+                    expect(e.lengthComputable).toBe(true);
+                    expect(e.total).toBeGreaterThan(0);
+                    expect(e.loaded).toBeGreaterThan(0);
+                    lastProgressEvent = e;
+                };
+
+                // removing options cause Android to timeout
+                ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options);
+            };
+
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                writeFile(localFileName, fileContents, fileWin, fileFail);
+            });
+
+            waitsForAny(uploadWin, uploadFail, fileFail);
+            runs(function() {
+                expect(lastProgressEvent).not.toBeNull('expected progress events');
+            });
+        });
+        it("should be stopped by abort() right away.", function() {
+            var remoteFile = server + "/upload";
+            var localFileName = "upload.txt";
+
+            var fileFail = createDoNotCallSpy('fileFail');
+            var uploadWin = createDoNotCallSpy('uploadWin', 'Should have been aborted');
+            var startTime;
+
+            var uploadFail = jasmine.createSpy().andCallFake(function(e) {
+                expect(e.code).toBe(FileTransferError.ABORT_ERR);
+                expect(new Date() - startTime).toBeLessThan(300);
+            });
+
+            var fileWin = function(fileEntry) {
+                ft = new FileTransfer();
+
+                var options = new FileUploadOptions();
+                options.fileKey = "file";
+                options.fileName = localFileName;
+                options.mimeType = "text/plain";
+
+                startTime = +new Date();
+                // removing options cause Android to timeout
+                ft.abort(); // should be a no-op.
+                ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options);
+                ft.abort();
+                ft.abort(); // should be a no-op.
+            };
+
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                writeFile(localFileName, new Array(10000).join('aborttest!'), fileWin, fileFail);
+            });
+
+            waitsForAny(uploadWin, uploadFail, fileFail);
+        });
+        it("should get http status on failure", function() {
+            var fileFail = createDoNotCallSpy('fileFail');
+            var uploadWin = createDoNotCallSpy('uploadWin');
+
+            var remoteFile = server + "/403";
+            var localFileName = "upload_expect_fail.txt";
+            var uploadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.http_status).toBe(403);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+            });
+
+            var fileWin = function(fileEntry) {
+                var ft = new FileTransfer();
+
+                var options = new FileUploadOptions();
+                options.fileKey="file";
+                options.fileName=fileEntry.name;
+                options.mimeType="text/plain";
+
+                ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options);
+            };
+
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                writeFile(localFileName, "this file should fail to upload", fileWin, fileFail);
+            });
+
+            waitsForAny(uploadWin, uploadFail, fileFail);
+        });
+        it("should handle malformed urls", function() {
+            var fileFail = createDoNotCallSpy('fileFail');
+            var uploadWin = createDoNotCallSpy('uploadWin');
+
+            var remoteFile = getMalformedUrl();
+            var localFileName = "malformed_url.txt";
+            var uploadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.INVALID_URL_ERR);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+            });
+            var fileWin = function(fileEntry) {
+                var ft = new FileTransfer();
+                ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, {});
+            };
+
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                writeFile(localFileName, "Some content", fileWin, fileFail);
+            });
+
+            waitsForAny(uploadWin, uploadFail, fileFail);
+        });
+        it("should handle unknown host", function() {
+            var fileFail = createDoNotCallSpy('fileFail');
+            var uploadWin = createDoNotCallSpy('uploadWin');
+
+            var remoteFile = "http://foobar.apache.org/robots.txt";
+            var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1);
+            var uploadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.CONNECTION_ERR);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+            });
+            var fileWin = function(fileEntry) {
+                var ft = new FileTransfer();
+                ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, {});
+            };
+
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                writeFile(localFileName, "# allow all", fileWin, fileFail);
+            });
+
+            waitsForAny(uploadWin, uploadFail, fileFail);
+        });
+        it("should handle missing file", function() {
+            var uploadWin = createDoNotCallSpy('uploadWin');
+
+            var remoteFile = server + "/upload";
+            var localFileName = "does_not_exist.txt";
+
+            var uploadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+            });
+
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.upload(root.fullPath + "/" + localFileName, remoteFile, uploadWin, uploadFail);
+            });
+
+            waitsForAny(uploadWin, uploadFail);
+        });
+        it("should handle bad file path", function() {
+            var uploadWin = createDoNotCallSpy('uploadWin');
+
+            var remoteFile = server + "/upload";
+
+            var uploadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+            });
+
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.upload("/usr/local/bad/file/path.txt", remoteFile, uploadWin, uploadFail);
+            });
+
+            waitsForAny(uploadWin, uploadFail);
+        });
+        it("should be able to set custom headers", function() {
+            var remoteFile = "http://whatheaders.com";
+            var localFileName = "upload.txt";
+
+            var fileFail = function() {};
+            var uploadFail = createDoNotCallSpy('uploadFail', "Ensure " + remoteFile + " is in the white list and that Content-Length header is being set.");
+
+            var uploadWin = jasmine.createSpy().andCallFake(function(uploadResult) {
+                expect(uploadResult.bytesSent).toBeGreaterThan(0);
+                expect(uploadResult.responseCode).toBe(200);
+                expect(uploadResult.response).toBeDefined();
+                var responseHtml = decodeURIComponent(uploadResult.response);
+                expect(responseHtml).toMatch(/CustomHeader1[\s\S]*CustomValue1/i);
+                expect(responseHtml).toMatch(/CustomHeader2[\s\S]*CustomValue2[\s\S]*CustomValue3/i, "Should allow array values");
+            });
+
+            var fileWin = function(fileEntry) {
+                ft = new FileTransfer();
+
+                var options = new FileUploadOptions();
+                options.fileKey = "file";
+                options.fileName = localFileName;
+                options.mimeType = "text/plain";
+
+                var params = new Object();
+                params.value1 = "test";
+                params.value2 = "param";
+                options.params = params;
+                options.headers = {
+                    "CustomHeader1": "CustomValue1",
+                    "CustomHeader2": ["CustomValue2", "CustomValue3"],
+                };
+
+                // removing options cause Android to timeout
+                ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options);
+            };
+
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                writeFile(localFileName, "this file should upload", fileWin, fileFail);
+            });
+
+            waitsForAny(uploadWin, uploadFail);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/geolocation.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/geolocation.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/geolocation.tests.js
new file mode 100644
index 0000000..7d1d6ce
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/geolocation.tests.js
@@ -0,0 +1,119 @@
+describe('Geolocation (navigator.geolocation)', function () {
+    it("should exist", function() {
+        expect(navigator.geolocation).toBeDefined();
+    });
+
+    it("should contain a getCurrentPosition function", function() {
+        expect(typeof navigator.geolocation.getCurrentPosition).toBeDefined();
+        expect(typeof navigator.geolocation.getCurrentPosition == 'function').toBe(true);
+    });
+
+    it("should contain a watchPosition function", function() {
+        expect(typeof navigator.geolocation.watchPosition).toBeDefined();
+        expect(typeof navigator.geolocation.watchPosition == 'function').toBe(true);
+    });
+
+    it("should contain a clearWatch function", function() {
+        expect(typeof navigator.geolocation.clearWatch).toBeDefined();
+        expect(typeof navigator.geolocation.clearWatch == 'function').toBe(true);
+    });
+
+    describe('getCurrentPosition method', function() {
+        describe('error callback', function() {
+            it("should be called if we set timeout to 0 and maximumAge to a very small number", function() {
+                console.log("Here I am");
+                var win = jasmine.createSpy(),
+                    fail = jasmine.createSpy();
+
+                runs(function () {
+                    navigator.geolocation.getCurrentPosition(win, fail, {
+                        maximumAge: 0,
+                        timeout: 0
+                    });
+                });
+
+                waitsFor(function () { return fail.wasCalled; }, "fail never called", 250); //small timeout as this should fire very fast
+
+                runs(function () {
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
+        });
+
+        describe('success callback', function() {
+            it("should be called with a Position object", function() {
+                var win = jasmine.createSpy().andCallFake(function(p) {
+                          expect(p.coords).toBeDefined();
+                          expect(p.timestamp).toBeDefined();
+                      }),
+                      fail = jasmine.createSpy();
+
+                runs(function () {
+                    navigator.geolocation.getCurrentPosition(win, fail, {
+                        maximumAge:300000 // 5 minutes maximum age of cached position
+                    });
+                });
+
+                waitsFor(function () { return win.wasCalled; }, "win never called", 20000);
+
+                runs(function () {
+                    expect(fail).not.toHaveBeenCalled();
+                });
+            });
+        });
+    });
+
+    describe('watchPosition method', function() {
+        describe('error callback', function() {
+            var errorWatch = null;
+
+            afterEach(function() {
+                navigator.geolocation.clearWatch(errorWatch);
+            });
+            it("should be called if we set timeout to 0 and maximumAge to a very small number", function() {
+                var win = jasmine.createSpy(),
+                    fail = jasmine.createSpy();
+
+                runs(function () {
+                    errorWatch = navigator.geolocation.watchPosition(win, fail, {
+                        maximumAge: 0,
+                        timeout: 0
+                    });
+                });
+
+                waitsFor(function () { return fail.wasCalled; }, "fail never called", 250); // small timeout as this hsould fire very quickly
+
+                runs(function () {
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
+        });
+
+        describe('success callback', function() {
+            var successWatch = null;
+
+            afterEach(function() {
+                navigator.geolocation.clearWatch(successWatch);
+            });
+            it("should be called with a Position object", function() {
+                var win = jasmine.createSpy().andCallFake(function(p) {
+                          expect(p.coords).toBeDefined();
+                          expect(p.timestamp).toBeDefined();
+                      }),
+                      fail = jasmine.createSpy();
+
+                runs(function () {
+                    successWatch = navigator.geolocation.watchPosition(win, fail, {
+                        maximumAge:(5 * 60 * 1000) // 5 minutes maximum age of cached position
+                    });
+                });
+
+                waitsFor(function () { return win.wasCalled; }, "win never called", 20000);
+
+                runs(function () {
+                    expect(fail).not.toHaveBeenCalled();
+                });
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/globalization.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/globalization.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/globalization.tests.js
new file mode 100644
index 0000000..6903aee
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/globalization.tests.js
@@ -0,0 +1,808 @@
+describe('Globalization (navigator.globalization)', function () {
+    it("should exist", function() {
+        expect(navigator.globalization).toBeDefined();
+    });
+    
+    describe("getLocaleName", function() {
+        it("should exist", function() {
+            expect(typeof navigator.globalization.getLocaleName).toBeDefined();
+            expect(typeof navigator.globalization.getLocaleName == 'function').toBe(true);
+        });
+        it("getLocaleName success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getLocaleName(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("dateToString", function() {
+        it("should exist", function() {
+            expect(typeof navigator.globalization.dateToString).toBeDefined();
+            expect(typeof navigator.globalization.dateToString == 'function').toBe(true);
+        });
+        it("dateToString using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("dateToString using formatLength=short and selector=date options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail, {formatLength: 'short', selector: 'date'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("dateToString using formatLength=full and selector=date options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail, {formatLength: 'full', selector: 'date'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("dateToString using formatLength=medium and selector=date and time(default) options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail, {formatLength: 'medium'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("dateToString using formatLength=long and selector=date and time(default) options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail, {formatLength: 'long'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("dateToString using formatLength=full and selector=date and time(default) options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail, {formatLength: 'full'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("stringToDate", function() {
+        it("should exist", function() {
+            expect(typeof navigator.globalization.stringToDate).toBeDefined();
+            expect(typeof navigator.globalization.stringToDate == 'function').toBe(true);
+        });
+        it("stringToDate using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.year).toBeDefined();
+                    expect(typeof a.year).toBe('number');
+                    expect(a.year >= 0 && a.year <=9999).toBe(true);
+                    expect(a.month).toBeDefined();
+                    expect(typeof a.month).toBe('number');
+                    expect(a.month >= 0 && a.month <=11).toBe(true);
+                    expect(a.day).toBeDefined();
+                    expect(typeof a.day).toBe('number');
+                    expect(a.day >= 1 && a.day <=31).toBe(true);
+                    expect(a.hour).toBeDefined();
+                    expect(typeof a.hour).toBe('number');
+                    expect(a.hour >= 0 && a.hour <=23).toBe(true);
+                    expect(a.minute).toBeDefined();
+                    expect(typeof a.minute).toBe('number');
+                    expect(a.minute >= 0 && a.minute <=59).toBe(true);
+                    expect(a.second).toBeDefined();
+                    expect(typeof a.second).toBe('number');
+                    expect(a.second >= 0 && a.second <=59).toBe(true);
+                    expect(a.millisecond).toBeDefined();
+                    expect(typeof a.millisecond).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+                
+            var win2 = function(a) {
+                navigator.globalization.stringToDate(a.value, win, fail);                
+            };
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win2, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("stringToDate using formatLength=short and selector=date options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.year).toBeDefined();
+                    expect(typeof a.year).toBe('number');
+                    expect(a.year >= 0 && a.year <=9999).toBe(true);
+                    expect(a.month).toBeDefined();
+                    expect(typeof a.month).toBe('number');
+                    expect(a.month >= 0 && a.month <=11).toBe(true);
+                    expect(a.day).toBeDefined();
+                    expect(typeof a.day).toBe('number');
+                    expect(a.day >= 1 && a.day <=31).toBe(true);
+                    expect(a.hour).toBeDefined();
+                    expect(typeof a.hour).toBe('number');
+                    expect(a.hour >= 0 && a.hour <=23).toBe(true);
+                    expect(a.minute).toBeDefined();
+                    expect(typeof a.minute).toBe('number');
+                    expect(a.minute >= 0 && a.minute <=59).toBe(true);
+                    expect(a.second).toBeDefined();
+                    expect(typeof a.second).toBe('number');
+                    expect(a.second >= 0 && a.second <=59).toBe(true);
+                    expect(a.millisecond).toBeDefined();
+                    expect(typeof a.millisecond).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+                
+            var win2 = function(a) {
+                navigator.globalization.stringToDate(a.value, win, fail, {formatLength: 'short', selector: 'date'});
+            };
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win2, fail, {formatLength: 'short', selector: 'date'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("stringToDate using formatLength=full and selector=date options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.year).toBeDefined();
+                    expect(typeof a.year).toBe('number');
+                    expect(a.year >= 0 && a.year <=9999).toBe(true);
+                    expect(a.month).toBeDefined();
+                    expect(typeof a.month).toBe('number');
+                    expect(a.month >= 0 && a.month <=11).toBe(true);
+                    expect(a.day).toBeDefined();
+                    expect(typeof a.day).toBe('number');
+                    expect(a.day >= 1 && a.day <=31).toBe(true);
+                    expect(a.hour).toBeDefined();
+                    expect(typeof a.hour).toBe('number');
+                    expect(a.hour >= 0 && a.hour <=23).toBe(true);
+                    expect(a.minute).toBeDefined();
+                    expect(typeof a.minute).toBe('number');
+                    expect(a.minute >= 0 && a.minute <=59).toBe(true);
+                    expect(a.second).toBeDefined();
+                    expect(typeof a.second).toBe('number');
+                    expect(a.second >= 0 && a.second <=59).toBe(true);
+                    expect(a.millisecond).toBeDefined();
+                    expect(typeof a.millisecond).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            var win2 = function(a) {
+                navigator.globalization.stringToDate(a.value, win, fail, {formatLength: 'full', selector: 'date'});
+            };
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win2, fail, {formatLength: 'full', selector: 'date'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("stringToDate using invalid date, error callback should be called with a GlobalizationError object", function() {
+            var win = jasmine.createSpy(),
+                fail = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.code).toBeDefined();
+                    expect(typeof a.code).toBe('number');
+                    expect(a.code === GlobalizationError.PARSING_ERROR).toBe(true);
+                    expect(a.message).toBeDefined();
+                    expect(typeof a.message).toBe('string');
+                    expect(a.message !== "").toBe(true);
+                });
+
+            runs(function () {
+                navigator.globalization.stringToDate('notADate', win, fail, {selector:'foobar'});
+            });
+
+            waitsFor(function () { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe("getDatePattern", function() {
+        it("should exist", function() {
+            expect(typeof navigator.globalization.getDatePattern).toBeDefined();
+            expect(typeof navigator.globalization.getDatePattern == 'function').toBe(true);
+        });
+        it("getDatePattern using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(a.timezone).toBeDefined();
+                    expect(typeof a.timezone).toBe('string');
+                    expect(a.timezone.length > 0).toBe(true);
+                    expect(a.utc_offset).toBeDefined();
+                    expect(typeof a.utc_offset).toBe('number');
+                    expect(a.dst_offset).toBeDefined();
+                    expect(typeof a.dst_offset).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDatePattern(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("getDatePattern using formatLength=medium and selector=date options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(a.timezone).toBeDefined();
+                    expect(typeof a.timezone).toBe('string');
+                    expect(a.timezone.length > 0).toBe(true);
+                    expect(a.utc_offset).toBeDefined();
+                    expect(typeof a.utc_offset).toBe('number');
+                    expect(a.dst_offset).toBeDefined();
+                    expect(typeof a.dst_offset).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDatePattern(win, fail, {formatLength: 'medium', selector: 'date'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });    
+
+    describe("getDateNames", function() {
+        it("should exist", function() {
+            expect(typeof navigator.globalization.getDateNames).toBeDefined();
+            expect(typeof navigator.globalization.getDateNames == 'function').toBe(true);
+        });
+        it("getDateNames using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(a.value instanceof Array).toBe(true);
+                    expect(a.value.length > 0).toBe(true);
+                    expect(typeof a.value[0]).toBe('string');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDateNames(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("getDateNames using type=narrow and item=days options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(a.value instanceof Array).toBe(true);
+                    expect(a.value.length > 0).toBe(true);
+                    expect(typeof a.value[0]).toBe('string');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDateNames(win, fail, {type: 'narrow', item: 'days'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("getDateNames using type=narrow and item=months options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(a.value instanceof Array).toBe(true);
+                    expect(a.value.length > 0).toBe(true);
+                    expect(typeof a.value[0]).toBe('string');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDateNames(win, fail, {type: 'narrow', item: 'months'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("getDateNames using type=wide and item=days options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(a.value instanceof Array).toBe(true);
+                    expect(a.value.length > 0).toBe(true);
+                    expect(typeof a.value[0]).toBe('string');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDateNames(win, fail, {item: 'days'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("getDateNames using type=wide and item=months options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(a.value instanceof Array).toBe(true);
+                    expect(a.value.length > 0).toBe(true);
+                    expect(typeof a.value[0]).toBe('string');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDateNames(win, fail, {item: 'months'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("isDayLightSavingsTime", function() {
+        it("should exist", function() {
+            expect(typeof navigator.globalization.isDayLightSavingsTime).toBeDefined();
+            expect(typeof navigator.globalization.isDayLightSavingsTime == 'function').toBe(true);
+        });
+        it("isDayLightSavingsTime using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.dst).toBeDefined();
+                    expect(typeof a.dst).toBe('boolean');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.isDayLightSavingsTime(new Date(), win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("getFirstDayOfWeek", function() {
+        it("should exist", function() {
+            expect(typeof navigator.globalization.getFirstDayOfWeek).toBeDefined();
+            expect(typeof navigator.globalization.getFirstDayOfWeek == 'function').toBe(true);
+        });
+        it("getFirstDayOfWeek success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getFirstDayOfWeek(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("numberToString", function() {
+        it("should exist", function() {
+            expect(typeof navigator.globalization.numberToString).toBeDefined();
+            expect(typeof navigator.globalization.numberToString == 'function').toBe(true);
+        });
+        it("numberToString using default options, should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.numberToString(3.25, win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("numberToString using type=percent options, should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.numberToString(.25, win, fail, {type: 'percent'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("numberToString using type=currency options, should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.numberToString(5.20, win, fail, {type: 'currency'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("stringToNumber", function() {
+        it("should exist", function() {
+            expect(typeof navigator.globalization.stringToNumber).toBeDefined();
+            expect(typeof navigator.globalization.stringToNumber == 'function').toBe(true);
+        });
+        it("stringToNumber using default options, should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('number');
+                    expect(a.value > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            var win2 = function(a) {
+                navigator.globalization.stringToNumber(a.value, win, fail);
+            };
+            
+            runs(function () {
+                navigator.globalization.numberToString(3.25, win2, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("stringToNumber using type=percent options, should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('number');
+                    expect(a.value > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            var win2 = function(a) {
+                navigator.globalization.stringToNumber(a.value, win, fail, {type: 'percent'});
+            };
+            
+            runs(function () {
+                navigator.globalization.numberToString(.25, win2, fail, {type: 'percent'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("getNumberPattern", function() {
+        it("should exist", function() {
+            expect(typeof navigator.globalization.getNumberPattern).toBeDefined();
+            expect(typeof navigator.globalization.getNumberPattern == 'function').toBe(true);
+        });
+        it("getNumberPattern using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(typeof a.symbol).toBe('string');
+                    expect(typeof a.fraction).toBe('number');
+                    expect(typeof a.rounding).toBe('number');
+                    expect(a.positive).toBeDefined();
+                    expect(typeof a.positive).toBe('string');
+                    expect(a.positive.length >= 0).toBe(true);
+                    expect(a.negative).toBeDefined();
+                    expect(typeof a.negative).toBe('string');
+                    expect(a.negative.length >= 0).toBe(true);
+                    expect(a.decimal).toBeDefined();
+                    expect(typeof a.decimal).toBe('string');
+                    expect(a.decimal.length > 0).toBe(true);
+                    expect(a.grouping).toBeDefined();
+                    expect(typeof a.grouping).toBe('string');
+                    expect(a.grouping.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getNumberPattern(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("getNumberPattern using type=percent, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(typeof a.symbol).toBe('string');
+                    expect(typeof a.fraction).toBe('number');
+                    expect(typeof a.rounding).toBe('number');
+                    expect(a.positive).toBeDefined();
+                    expect(typeof a.positive).toBe('string');
+                    expect(a.positive.length >= 0).toBe(true);
+                    expect(a.negative).toBeDefined();
+                    expect(typeof a.negative).toBe('string');
+                    expect(a.negative.length >= 0).toBe(true);
+                    expect(a.decimal).toBeDefined();
+                    expect(typeof a.decimal).toBe('string');
+                    expect(a.decimal.length > 0).toBe(true);
+                    expect(a.grouping).toBeDefined();
+                    expect(typeof a.grouping).toBe('string');
+                    expect(a.grouping.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getNumberPattern(win, fail, {type: 'percent'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("getNumberPattern using type=currency, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(typeof a.symbol).toBe('string');
+                    expect(typeof a.fraction).toBe('number');
+                    expect(typeof a.rounding).toBe('number');
+                    expect(a.positive).toBeDefined();
+                    expect(typeof a.positive).toBe('string');
+                    expect(a.positive.length >= 0).toBe(true);
+                    expect(a.negative).toBeDefined();
+                    expect(typeof a.negative).toBe('string');
+                    expect(a.negative.length >= 0).toBe(true);
+                    expect(a.decimal).toBeDefined();
+                    expect(typeof a.decimal).toBe('string');
+                    expect(a.decimal.length > 0).toBe(true);
+                    expect(a.grouping).toBeDefined();
+                    expect(typeof a.grouping).toBe('string');
+                    expect(a.grouping.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getNumberPattern(win, fail, {type: 'currency'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("getCurrencyPattern", function() {
+        it("should exist", function() {
+            expect(typeof navigator.globalization.getCurrencyPattern).toBeDefined();
+            expect(typeof navigator.globalization.getCurrencyPattern == 'function').toBe(true);
+        });
+        it("getCurrencyPattern using EUR for currency, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(a.code).toBeDefined();
+                    expect(typeof a.code).toBe('string');
+                    expect(a.code.length > 0).toBe(true);
+                    expect(typeof a.fraction).toBe('number');
+                    expect(typeof a.rounding).toBe('number');                   
+                    expect(a.decimal).toBeDefined();
+                    expect(typeof a.decimal).toBe('string');
+                    expect(a.decimal.length >= 0).toBe(true);                    
+                    expect(a.grouping).toBeDefined();
+                    expect(typeof a.grouping).toBe('string');
+                    expect(a.grouping.length >= 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getCurrencyPattern("EUR", win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/media.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/media.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/media.tests.js
new file mode 100644
index 0000000..d0e6c4f
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/media.tests.js
@@ -0,0 +1,144 @@
+describe('Media', function () {
+	it("should exist", function() {
+        expect(Media).toBeDefined();
+		expect(typeof Media).toBe("function");
+	});
+
+    it("should have the following properties", function() {
+        var media1 = new Media("dummy");
+        expect(media1.id).toBeDefined();
+        expect(media1.src).toBeDefined();
+        expect(media1._duration).toBeDefined();
+        expect(media1._position).toBeDefined();
+        media1.release();
+    });
+
+	it("should define constants for Media errors", function() {
+        expect(MediaError).toBeDefined();
+        expect(MediaError.MEDIA_ERR_NONE_ACTIVE).toBe(0);
+        expect(MediaError.MEDIA_ERR_ABORTED).toBe(1);
+		expect(MediaError.MEDIA_ERR_NETWORK).toBe(2);
+		expect(MediaError.MEDIA_ERR_DECODE).toBe(3);
+		expect(MediaError.MEDIA_ERR_NONE_SUPPORTED).toBe(4);
+	});
+
+    it("should contain a play function", function() {
+        var media1 = new Media();
+        expect(media1.play).toBeDefined();
+        expect(typeof media1.play).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a stop function", function() {
+        var media1 = new Media();
+        expect(media1.stop).toBeDefined();
+        expect(typeof media1.stop).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a seekTo function", function() {
+        var media1 = new Media();
+        expect(media1.seekTo).toBeDefined();
+        expect(typeof media1.seekTo).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a pause function", function() {
+        var media1 = new Media();
+        expect(media1.pause).toBeDefined();
+        expect(typeof media1.pause).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a getDuration function", function() {
+        var media1 = new Media();
+        expect(media1.getDuration).toBeDefined();
+        expect(typeof media1.getDuration).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a getCurrentPosition function", function() {
+        var media1 = new Media();
+        expect(media1.getCurrentPosition).toBeDefined();
+        expect(typeof media1.getCurrentPosition).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a startRecord function", function() {
+        var media1 = new Media();
+        expect(media1.startRecord).toBeDefined();
+        expect(typeof media1.startRecord).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a stopRecord function", function() {
+        var media1 = new Media();
+        expect(media1.stopRecord).toBeDefined();
+        expect(typeof media1.stopRecord).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a release function", function() {
+        var media1 = new Media();
+        expect(media1.release).toBeDefined();
+        expect(typeof media1.release).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a setVolume function", function() {
+        var media1 = new Media();
+        expect(media1.setVolume).toBeDefined();
+        expect(typeof media1.setVolume).toBe('function');
+        media1.release();
+    });
+
+	it("should return MediaError for bad filename", function() {
+		var badMedia = null,
+            win = jasmine.createSpy(),
+            fail = jasmine.createSpy().andCallFake(function (result) {
+                expect(result).toBeDefined();
+                expect(result.code).toBe(MediaError.MEDIA_ERR_ABORTED);
+            });
+            
+        runs(function () {
+            badMedia = new Media("invalid.file.name", win,fail);
+            badMedia.play();
+        });
+
+        waitsFor(function () { return fail.wasCalled; }, Tests.TEST_TIMEOUT);
+
+        runs(function () {
+            expect(win).not.toHaveBeenCalled();
+            badMedia.release();
+        });
+	});
+
+    it("position should be set properly", function() {
+        var media1 = new Media("http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3"),
+            test = jasmine.createSpy().andCallFake(function(position) {
+                    console.log("position = " + position);
+                    expect(position).toBeGreaterThan(0.0);
+                    media1.stop()
+                    media1.release();
+                });
+
+        media1.play();
+
+        waits(5000);
+
+        runs(function () {
+            media1.getCurrentPosition(test, function () {});
+        });
+
+        waitsFor(function () { return test.wasCalled; }, Tests.TEST_TIMEOUT);
+    });
+
+    it("duration should be set properly", function() {
+        var media1 = new Media("http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3");
+        media1.play();
+        waits(5000);
+        runs(function () {
+            expect(media1.getDuration()).toBeGreaterThan(0.0);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/network.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/network.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/network.tests.js
new file mode 100644
index 0000000..780097f
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/network.tests.js
@@ -0,0 +1,25 @@
+describe('Network (navigator.network)', function () {
+	it("should exist", function() {
+        expect(navigator.network).toBeDefined();
+	});
+
+    describe('Network Information API', function () {
+        it("connection should exist", function() {
+            expect(navigator.network.connection).toBeDefined();
+        });
+
+        it("should contain connection properties", function() {
+            expect(navigator.network.connection.type).toBeDefined();
+        });
+
+        it("should define constants for connection status", function() {
+            expect(Connection.UNKNOWN).toBe("unknown");
+            expect(Connection.ETHERNET).toBe("ethernet");
+            expect(Connection.WIFI).toBe("wifi");
+            expect(Connection.CELL_2G).toBe("2g");
+            expect(Connection.CELL_3G).toBe("3g");
+            expect(Connection.CELL_4G).toBe("4g");
+            expect(Connection.NONE).toBe("none");
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/notification.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/notification.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/notification.tests.js
new file mode 100644
index 0000000..61c795d
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/notification.tests.js
@@ -0,0 +1,20 @@
+describe('Notification (navigator.notification)', function () {
+	it("should exist", function() {
+        expect(navigator.notification).toBeDefined();
+	});
+
+	it("should contain a vibrate function", function() {
+		expect(typeof navigator.notification.vibrate).toBeDefined();
+		expect(typeof navigator.notification.vibrate).toBe("function");
+	});
+
+	it("should contain a beep function", function() {
+		expect(typeof navigator.notification.beep).toBeDefined();
+		expect(typeof navigator.notification.beep).toBe("function");
+	});
+
+	it("should contain a alert function", function() {
+		expect(typeof navigator.notification.alert).toBeDefined();
+		expect(typeof navigator.notification.alert).toBe("function");
+	});
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/platform.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/platform.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/platform.tests.js
new file mode 100644
index 0000000..f44fcb3
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/platform.tests.js
@@ -0,0 +1,10 @@
+describe('Platform (cordova)', function () {
+    it("should exist", function() {
+        expect(cordova).toBeDefined();
+    });
+
+    it("exec method should exist", function() {
+        expect(cordova.exec).toBeDefined();
+        expect(typeof cordova.exec).toBe('function');
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/storage.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/storage.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/storage.tests.js
new file mode 100644
index 0000000..08ea608
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/storage.tests.js
@@ -0,0 +1,174 @@
+describe("Session Storage", function () {
+    it("should exist", function () {
+        expect(window.sessionStorage).toBeDefined();
+        expect(typeof window.sessionStorage.length).not.toBe('undefined');
+        expect(typeof(window.sessionStorage.key)).toBe('function');
+        expect(typeof(window.sessionStorage.getItem)).toBe('function');
+        expect(typeof(window.sessionStorage.setItem)).toBe('function');
+        expect(typeof(window.sessionStorage.removeItem)).toBe('function');
+        expect(typeof(window.sessionStorage.clear)).toBe('function');
+    });
+
+    it("check length", function () {
+        expect(window.sessionStorage.length).toBe(0);
+        window.sessionStorage.setItem("key","value");
+        expect(window.sessionStorage.length).toBe(1);
+        window.sessionStorage.removeItem("key");   
+        expect(window.sessionStorage.length).toBe(0);
+    });
+
+    it("check key", function () {
+        expect(window.sessionStorage.key(0)).toBe(null);
+        window.sessionStorage.setItem("test","value");
+        expect(window.sessionStorage.key(0)).toBe("test");
+        window.sessionStorage.removeItem("test");   
+        expect(window.sessionStorage.key(0)).toBe(null);
+    });
+
+    it("check getItem", function() {
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+        window.sessionStorage.setItem("item","value");
+        expect(window.sessionStorage.getItem("item")).toBe("value");
+        window.sessionStorage.removeItem("item");   
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+    });
+
+    it("check setItem", function() {
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+        window.sessionStorage.setItem("item","value");
+        expect(window.sessionStorage.getItem("item")).toBe("value");
+        window.sessionStorage.setItem("item","newval");
+        expect(window.sessionStorage.getItem("item")).toBe("newval");
+        window.sessionStorage.removeItem("item");   
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+    });
+
+    it("can remove an item", function () {
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+        window.sessionStorage.setItem("item","value");
+        expect(window.sessionStorage.getItem("item")).toBe("value");
+        window.sessionStorage.removeItem("item");   
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+    });
+
+    it("check clear", function() {
+        window.sessionStorage.setItem("item1","value");
+        window.sessionStorage.setItem("item2","value");
+        window.sessionStorage.setItem("item3","value");
+        expect(window.sessionStorage.length).toBe(3);
+        window.sessionStorage.clear();
+        expect(window.sessionStorage.length).toBe(0);
+    });
+
+    it("check dot notation", function() {
+        expect(window.sessionStorage.item).not.toBeDefined();
+        window.sessionStorage.item = "value";
+        expect(window.sessionStorage.item).toBe("value");
+        window.sessionStorage.removeItem("item");   
+        expect(window.sessionStorage.item).not.toBeDefined();
+    });
+
+    describe("Local Storage", function () {
+        it("should exist", function() {
+            expect(window.localStorage).toBeDefined();
+            expect(window.localStorage.length).toBeDefined();
+            expect(typeof window.localStorage.key).toBe("function");
+            expect(typeof window.localStorage.getItem).toBe("function");
+            expect(typeof window.localStorage.setItem).toBe("function");
+            expect(typeof window.localStorage.removeItem).toBe("function");
+            expect(typeof window.localStorage.clear).toBe("function");
+        });  
+
+        it("check length", function() {
+            expect(window.localStorage.length).toBe(0);
+            window.localStorage.setItem("key","value");
+            expect(window.localStorage.length).toBe(1);
+            window.localStorage.removeItem("key");   
+            expect(window.localStorage.length).toBe(0);
+        });
+
+        it("check key", function() {
+            expect(window.localStorage.key(0)).toBe(null);
+            window.localStorage.setItem("test","value");
+            expect(window.localStorage.key(0)).toBe("test");
+            window.localStorage.removeItem("test");   
+            expect(window.localStorage.key(0)).toBe(null);
+        });
+
+        it("check getItem", function() {
+            expect(window.localStorage.getItem("item")).toBe(null);
+            window.localStorage.setItem("item","value");
+            expect(window.localStorage.getItem("item")).toBe("value");
+            window.localStorage.removeItem("item");   
+            expect(window.localStorage.getItem("item")).toBe(null);
+        });
+
+        it("check setItem", function() {
+            expect(window.localStorage.getItem("item")).toBe(null);
+            window.localStorage.setItem("item","value");
+            expect(window.localStorage.getItem("item")).toBe("value");
+            window.localStorage.setItem("item","newval");
+            expect(window.localStorage.getItem("item")).toBe("newval");
+            window.localStorage.removeItem("item");   
+            expect(window.localStorage.getItem("item")).toBe(null);
+        });
+
+        it("check removeItem", function() {
+            expect(window.localStorage.getItem("item")).toBe(null);
+            window.localStorage.setItem("item","value");
+            expect(window.localStorage.getItem("item")).toBe("value");
+            window.localStorage.removeItem("item");   
+            expect(window.localStorage.getItem("item")).toBe(null);
+        });
+
+        it("check clear", function() {
+            expect(window.localStorage.getItem("item1")).toBe(null);
+            expect(window.localStorage.getItem("item2")).toBe(null);
+            expect(window.localStorage.getItem("item3")).toBe(null);
+            window.localStorage.setItem("item1","value");
+            window.localStorage.setItem("item2","value");
+            window.localStorage.setItem("item3","value");
+            expect(window.localStorage.getItem("item1")).toBe("value");
+            expect(window.localStorage.getItem("item2")).toBe("value");
+            expect(window.localStorage.getItem("item3")).toBe("value");
+            expect(window.localStorage.length).toBe(3);
+            window.localStorage.clear();
+            expect(window.localStorage.length).toBe(0);
+            expect(window.localStorage.getItem("item1")).toBe(null);
+            expect(window.localStorage.getItem("item2")).toBe(null);
+            expect(window.localStorage.getItem("item3")).toBe(null);
+        });
+
+        it("check dot notation", function() {
+            expect(window.localStorage.item).not.toBeDefined();
+            window.localStorage.item = "value";
+            expect(window.localStorage.item).toBe("value");
+            window.localStorage.removeItem("item");   
+            expect(window.localStorage.item).not.toBeDefined();
+        });
+    });
+
+    describe("HTML 5 Storage", function () {
+        it("should exist", function() {
+            expect(window.openDatabase).toBeDefined();
+        });
+
+        it("Should be able to create and drop tables", function() {
+            var win = jasmine.createSpy('win');
+            var fail1 = createDoNotCallSpy('fail1');
+            var fail2 = createDoNotCallSpy('fail2');
+            var db = openDatabase("Database", "1.0", "HTML5 Database API example", 5*1024*1024);
+            db.transaction(function(t) {
+                t.executeSql('CREATE TABLE IF NOT EXISTS foo(id int, name varchar(255));');
+                t.executeSql('CREATE TABLE IF NOT EXISTS foo2(id int, name varchar(255));');
+            }, fail1, step2);
+            function step2() {
+              db.transaction(function(t) {
+                  t.executeSql('DROP TABLE foo;');
+                  t.executeSql('DROP TABLE foo2');
+              }, fail2, win);
+            }
+            waitsForAny(win, fail1, fail2);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/battery/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/battery/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/battery/index.html
new file mode 100644
index 0000000..394988c
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/battery/index.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+            if (!deviceReady) {
+                alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+            }
+        },1000);
+    }
+
+    /* Battery */
+    function updateInfo(info) {
+        document.getElementById('level').innerText = info.level;
+        document.getElementById('isPlugged').innerText = info.isPlugged;
+        if (info.level > 5) {
+            document.getElementById('crit').innerText = "false";
+        }
+        if (info.level > 20) {
+            document.getElementById('low').innerText = "false";
+        }
+    }
+    
+    function batteryLow(info) {
+        document.getElementById('low').innerText = "true";
+    }
+    
+    function batteryCritical(info) {
+        document.getElementById('crit').innerText = "true";
+    }
+    
+    function addBattery() {
+        window.addEventListener("batterystatus", updateInfo, false);
+    }
+    
+    function removeBattery() {
+        window.removeEventListener("batterystatus", updateInfo, false);
+    }
+    
+    function addLow() {
+        window.addEventListener("batterylow", batteryLow, false);
+    }
+    
+    function removeLow() {
+        window.removeEventListener("batterylow", batteryLow, false);
+    }
+    
+    function addCritical() {
+        window.addEventListener("batterycritical", batteryCritical, false);
+    }
+    
+    function removeCritical() {
+        window.removeEventListener("batterycritical", batteryCritical, false);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Battery</h1>
+    <div id="info">
+        <b>Status:</b> <span id="battery_status"></span><br>
+        Level: <span id="level"></span><br/>
+        Plugged: <span id="isPlugged"></span><br/>
+        Low: <span id="low"></span><br/>
+        Critical: <span id="crit"></span><br/>
+    </div>
+    <h2>Action</h2>
+    <div class="btn large" onclick="addBattery();">Add "batterystatus" listener</div>
+    <div class="btn large" onclick="removeBattery();">Remove "batterystatus" listener</div>
+    <div class="btn large" onclick="addLow();">Add "batterylow" listener</div>
+    <div class="btn large" onclick="removeLow();">Remove "batterylow" listener</div>
+    <div class="btn large" onclick="addCritical();">Add "batterycritical" listener</div>
+    <div class="btn large" onclick="removeCritical();">Remove "batterycritical" listener</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      


[28/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/CordovaDeploy.csproj
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/CordovaDeploy.csproj b/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/CordovaDeploy.csproj
new file mode 100644
index 0000000..fb9949f
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/CordovaDeploy.csproj
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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. 
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{E752165B-AF59-4FF0-8601-A2A69FE09E0E}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>CordovaDeploy</RootNamespace>
+    <AssemblyName>CordovaDeploy</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkProfile>Client</TargetFrameworkProfile>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Smartdevice.Connectivity, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>C:\Program Files (x86)\Common Files\microsoft shared\Phone Tools\CoreCon\10.0\Bin\Microsoft.Smartdevice.Connectivity.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Program.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Program.cs b/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Program.cs
new file mode 100644
index 0000000..2e70187
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Program.cs
@@ -0,0 +1,235 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.SmartDevice.Connectivity;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Xml.XPath;
+using System.Xml;
+using System.Xml.Linq;
+
+
+namespace CordovaDeploy
+{
+
+    class DeployTool
+    {
+
+        static void Usage()
+        {
+            Log("Usage: CordovaDeploy [ -devices  BuildOutputPath -d:DeviceIndex ]");
+            Log("    -devices : lists the devices and exits");
+            Log("    BuildOutputPath : path to the built application, typically Bin/Debug/ or Bin/Release/");
+            Log("    -d : index of the device to deploy, default is 0 ");
+            Log("examples:");
+            Log("  CordovaDeploy -devices");
+            Log("  CordovaDeploy Bin/Debug");
+            Log("  CordovaDeploy Bin/Release -d:1");
+
+        }
+
+        static void ReadWait()
+        {
+            //Console.WriteLine("\nPress ENTER to continue...");
+            //Console.Read();
+        }
+
+        static void Log(string msg, bool error = false)
+        {
+            Debug.WriteLine(msg);
+            if (error)
+            {
+                Console.Error.WriteLine(msg);
+            }
+            else
+            {
+                Console.Out.WriteLine(msg);
+            }
+        }
+
+        static Guid ReadAppId(string root)
+        {
+            Guid appID = Guid.Empty;
+            string manifestFilePath = root + @"\Properties\WMAppManifest.xml";
+
+            if (File.Exists(manifestFilePath))
+            {
+                XDocument xdoc = XDocument.Load(manifestFilePath);
+                var appNode = xdoc.Root.Descendants("App").FirstOrDefault();
+                if (appNode != null)
+                {
+                    string guidStr = appNode.Attribute("ProductID").Value;
+                    appID = new Guid(guidStr);
+                }
+                else
+                {
+                    Log(string.Format("Unable to find appID, expected to find an App.ProductID property defined in the file {0}", manifestFilePath), true);
+                }
+            }
+            else
+            {
+                Log(string.Format("Error: the file {0} does not exist", manifestFilePath), true);
+            }
+
+
+            return appID;
+        }
+
+
+
+        static void ListDevices()
+        {
+            // Get CoreCon WP7 SDK
+            DatastoreManager dsmgrObj = new DatastoreManager(1033);
+            Platform WP7SDK = dsmgrObj.GetPlatforms().Single(p => p.Name == "Windows Phone 7");
+            Collection<Device> devices = WP7SDK.GetDevices();
+            for (int index = 0; index < devices.Count; index++)
+            {
+                Device d = devices[index];
+                string info = string.Format("{0} : {1} : {2}", index.ToString(), d.Id, d.Name);
+                Log(info);
+            }
+        }
+
+        static void Main(string[] args)
+        {
+            int deviceIndex = 0;
+
+            string iconFilePath = "";
+            string xapFilePath = "";
+            Guid appID = Guid.Empty;
+
+            string root = Directory.GetCurrentDirectory();
+
+            if (args.Length < 1)
+            {
+                Usage();
+                ReadWait();
+                return;
+            }
+            else if (args[0] == "-devices")
+            {
+                ListDevices();
+                return;
+            }
+            else if (args.Length > 1 && args[1].StartsWith("-d:"))
+            {
+                deviceIndex = int.Parse(args[1].Substring(3));
+            }
+
+
+            if (Directory.Exists(args[0]))
+            {
+                DirectoryInfo info = new DirectoryInfo(args[0]);
+                root = info.FullName;
+            }
+
+            appID = ReadAppId(root);
+            if (appID == Guid.Empty)
+            {
+                // Logging of errors is done in ReadAppId
+                return;
+            }
+
+            if (File.Exists(root + @"\ApplicationIcon.png"))
+            {
+                iconFilePath = root + @"\ApplicationIcon.png";
+            }
+            else
+            {
+                Log(string.Format("Error: could not find application icon at {0}", root + @"\ApplicationIcon.png"), true);
+                ReadWait();
+                return;
+            }
+
+            try {
+                xapFilePath = Directory.GetFiles(root + @"\Bin\Debug", "*.xap").FirstOrDefault();
+            } catch (DirectoryNotFoundException e) {
+                try {
+                    xapFilePath = Directory.GetFiles(root + @"\Bin\Release", "*.xap").FirstOrDefault();
+                } catch (DirectoryNotFoundException ex) {
+                    Log(string.Format("Error: could not find project build directoy in {0}", root), true);
+                    Log("make sure your app has been successfully built before deploying.", true);
+                }
+            }
+
+            if (string.IsNullOrEmpty(xapFilePath))
+            {
+                Log(string.Format("Error: could not find application .xap in folder {0}", root), true);
+                ReadWait();
+                return;
+            }
+
+
+            // Get CoreCon WP7 SDK
+            DatastoreManager dsmgrObj = new DatastoreManager(1033);
+            Collection<Platform> WP7SDKs = dsmgrObj.GetPlatforms();
+            Platform WP7SDK = dsmgrObj.GetPlatforms().Single(p => p.Name == "Windows Phone 7");
+
+            Collection<Device> devices = null;
+
+            devices = WP7SDK.GetDevices();
+
+            //// Get Emulator / Device
+            Device WP7Device = devices[deviceIndex];
+
+            if (WP7Device != null)
+            {
+                RemoteApplication app;
+                bool isConnected = WP7Device.IsConnected();
+
+                Debug.WriteLine(WP7Device.ToString());
+
+                if (!isConnected)
+                {
+                    try
+                    {
+                        WP7Device.Connect();
+                    }
+                    catch (Exception ex)
+                    {
+                        Log("Error: " + ex.Message, true);
+                        ReadWait();
+                        return;
+                    }
+                }
+
+                if (WP7Device.IsApplicationInstalled(appID))
+                {
+                    Log("Uninstalling XAP from " + WP7Device.Name);
+                    app = WP7Device.GetApplication(appID);
+                    app.Uninstall();
+                }
+
+                Log("Installing app on " + WP7Device.Name);
+                app = WP7Device.InstallApplication(appID, appID, "NormalApp", iconFilePath, xapFilePath);
+
+                Log("Launching app on " + WP7Device.Name);
+                app.Launch();
+
+                ReadWait();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Properties/AssemblyInfo.cs b/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..3c26c87
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CordovaDeploy")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("CordovaDeploy")]
+[assembly: AssemblyCopyright("Copyright ©  2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("256b11aa-d4bb-48cf-8024-7c040421fa8d")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/build.js b/lib/cordova-wp7/templates/standalone/cordova/lib/build.js
new file mode 100644
index 0000000..9986a7e
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/build.js
@@ -0,0 +1,181 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject');
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments;
+
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\build.js').join('');
+
+// help/usage function
+function Usage() {
+    Log("");
+    Log("Usage: build [ --debug | --release ]");
+    Log("    --help    : Displays this dialog.");
+    Log("    --debug   : Cleans and builds project in debug mode.");
+    Log("    --release : Cleans and builds project in release mode.");
+    Log("examples:");
+    Log("    build ");
+    Log("    build --debug");
+    Log("    build --release");
+    Log("");
+}
+
+// logs messaged to stdout and stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    //Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if (!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oShell.StdErr.AtEndOfStream) {
+        var line = oShell.StdErr.ReadAll();
+        Log(line, true);
+        WScript.Quit(2);
+    }
+}
+
+// checks to see if a .csproj file exists in the project root
+function is_cordova_project(path) {
+    if (fso.FolderExists(path)) {
+        var proj_folder = fso.GetFolder(path);
+        var proj_files = new Enumerator(proj_folder.Files);
+        for (;!proj_files.atEnd(); proj_files.moveNext()) {
+            if (fso.GetExtensionName(proj_files.item()) == 'csproj') {
+                return true;  
+            }
+        }
+    }
+    return false;
+}
+
+// builds the project and .xap in release mode
+function build_xap_release(path) {
+    Log("Building Cordova-WP8 Project:");
+    Log("\tConfiguration : Release");
+    Log("\tDirectory : " + path);
+    
+    wscript_shell.CurrentDirectory = path;
+    exec_verbose('msbuild /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo /p:Configuration=Release');
+    
+    // check if file xap was created
+    if (fso.FolderExists(path + '\\Bin\\Release')) {
+        var out_folder = fso.GetFolder(path + '\\Bin\\Release');
+        var out_files = new Enumerator(out_folder.Files);
+        for (;!out_files.atEnd(); out_files.moveNext()) {
+            if (fso.GetExtensionName(out_files.item()) == 'xap') {
+                Log("BUILD SUCCESS.");
+                return;  
+            }
+        }
+    }
+    Log('ERROR: MSBuild failed to create .xap when building cordova-wp8 for release.', true);
+    WScript.Quit(2);
+}
+
+// builds the project and .xap in debug mode
+function build_xap_debug(path) {
+    Log("Building Cordova-WP8 Project:");
+    Log("\tConfiguration : Debug");
+    Log("\tDirectory : " + path);
+    
+    wscript_shell.CurrentDirectory = path;
+    exec_verbose('msbuild /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo /p:Configuration=Debug');
+    
+    // check if file xap was created
+    if (fso.FolderExists(path + '\\Bin\\Debug')) {
+        var out_folder = fso.GetFolder(path + '\\Bin\\Debug');
+        var out_files = new Enumerator(out_folder.Files);
+        for (;!out_files.atEnd(); out_files.moveNext()) {
+            if (fso.GetExtensionName(out_files.item()) == 'xap') {
+                Log("BUILD SUCCESS.");
+                return;  
+            }
+        }
+    }
+    Log('ERROR: MSBuild failed to create .xap when building cordova-wp8 for debugging.', true);
+    WScript.Quit(2);
+}
+
+
+Log("");
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help") {
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (args.Count() > 1) {
+        Log("Error: Too many arguments.", true);
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (fso.FolderExists(ROOT)) {
+        if (!is_cordova_project(ROOT)) {
+            Log('Error: .csproj file not found in ' + ROOT, true);
+            Log('could not build project.', true);
+            WScript.Quit(2);
+        }
+
+        if (args(0) == "--debug" || args(0) == "-d") {
+            exec_verbose('%comspec% /c ' + ROOT + '\\cordova\\clean');
+            build_xap_debug(ROOT);
+        }
+        else if (args(0) == "--release" || args(0) == "-r") {
+            exec_verbose('%comspec% /c ' + ROOT + '\\cordova\\clean');
+            build_xap_release(ROOT);
+        }
+        else {
+            Log("Error: \"" + args(0) + "\" is not recognized as a build option", true);
+            Usage();
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log("Error: Project directory not found,", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
+else {
+    Log("WARNING: [ --debug | --release ] not specified, defaulting to debug...");
+    build_xap_debug(ROOT);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/clean.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/clean.js b/lib/cordova-wp7/templates/standalone/cordova/lib/clean.js
new file mode 100644
index 0000000..3a8c871
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/clean.js
@@ -0,0 +1,124 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject');
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+var args = WScript.Arguments;
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\clean.js').join('');
+
+
+// help function
+function Usage() {
+    Log("");
+    Log("Usage: clean [ --debug | --release ]");
+    Log("    --debug   : Cleans generated debug files in project.");
+    Log("    --release : Cleans generated release files in project.");
+    Log("examples:");
+    Log("    clean --debug");
+    Log("    clean");
+    Log("         - deletes all generated files in project");
+    Log("");
+}
+
+//  logs to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// cleans any generated files in the cordova project
+function clean_project(path) {
+    if (fso.FolderExists(path + "\\obj")) {
+        fso.DeleteFolder(path + "\\obj");
+    }
+    if (fso.FolderExists(path + "\\Bin")) {
+        fso.DeleteFolder(path + "\\Bin");
+    }
+    //TODO: delete CordovaAppProj.csproj.user as well? Service References?
+}
+
+// cleans any files generated by build --debug
+function clean_debug(path) {
+    if (fso.FolderExists(path + "\\obj\\Debug")) {
+        fso.DeleteFolder(path + "\\obj\\Debug");
+    }
+    if (fso.FolderExists(path + "\\Bin\\Debug")) {
+        fso.DeleteFolder(path + "\\Bin\\Debug");
+    }
+}
+
+// cleans any files generated by build --release
+function clean_release(path) {
+    if (fso.FolderExists(path + "\\obj\\Release")) {
+        fso.DeleteFolder(path + "\\obj\\Release");
+    }
+    if (fso.FolderExists(path + "\\Bin\\Release")) {
+        fso.DeleteFolder(path + "\\Bin\\Release");
+    }
+}
+
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help") {
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (args.Count() > 1) {
+        Log("Error: Too many arguments.", true);
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (fso.FolderExists(ROOT)) {
+        if (args(0) == "--debug" || args(0) == "-d") {
+            clean_debug(ROOT);
+        }
+        else if (args(0) == "--release" || args(0) == "-r") {
+            clean_release(ROOT);
+        }
+        else {
+            Log("Error: \"" + args(0) + "\" is not recognized as a build option", true);
+            Usage();
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log("Error: Project directory not found,", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
+else {
+   if (fso.FolderExists(ROOT)) {
+        Log("Cleaning cordova project...");
+        clean_project(ROOT);
+    }
+    else {
+        Log("Error: Project directory not found,", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/deploy.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/deploy.js b/lib/cordova-wp7/templates/standalone/cordova/lib/deploy.js
new file mode 100644
index 0000000..29a3f7d
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/deploy.js
@@ -0,0 +1,326 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject');
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments;
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\deploy.js').join('');
+    // path to CordovaDeploy.exe
+var CORDOVA_DEPLOY_EXE = '\\cordova\\lib\\CordovaDeploy\\CordovaDeploy\\bin\\Debug\\CordovaDeploy.exe';
+    // path to CordovaDeploy
+var CORDOVA_DEPLOY = '\\cordova\\lib\\CordovaDeploy';
+
+//build types
+var NONE = 0,
+    DEBUG = 1,
+    RELEASE = 2,
+    NO_BUILD = 3;
+var build_type = NONE;
+
+
+// help function
+function Usage() {
+    Log("");
+    Log("Usage: run [ --device | --emulator | --target=<id> ] [ --debug | --release | --nobuild ]");
+    Log("    --device      : Deploys and runs the project on the connected device.");
+    Log("    --emulator    : Deploys and runs the project on an emulator.");
+    Log("    --target=<id> : Deploys and runs the project on the specified target.");
+    Log("    --debug       : Builds project in debug mode.");
+    Log("    --release     : Builds project in release mode.");
+    Log("    --nobuild     : Ueses pre-built xap, or errors if project is not built.");
+    Log("examples:");
+    Log("    run");
+    Log("    run --emulator");
+    Log("    run --device");
+    Log("    run --target=7988B8C3-3ADE-488d-BA3E-D052AC9DC710");
+    Log("    run --device --release");
+    Log("    run --emulator --debug");
+    Log("");
+}
+
+// log to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+} 
+
+var ForReading = 1, ForWriting = 2, ForAppending = 8;
+var TristateUseDefault = 2, TristateTrue = 1, TristateFalse = 0;
+
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        WScript.sleep(100);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    //Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if (!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadAll();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oShell.StdErr.AtEndOfStream) {
+        var line = oShell.StdErr.ReadAll();
+        Log(line, true);
+        WScript.Quit(2);
+    }
+}
+
+// returns the contents of a file
+function read(filename) {
+    if (fso.FileExists(filename)) {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else {
+        Log('Cannot read non-existant file : ' + filename, true);
+        WScript.Quit(2);
+    }
+    return null;
+}
+
+// builds the CordovaDeploy.exe if it does not already exist 
+function cordovaDeploy(path) {
+    if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        return;
+    }
+
+    Log('CordovaDeploy.exe not found, attempting to build CordovaDeploy.exe...');
+
+    // build CordovaDeploy.exe
+    if (fso.FolderExists(path + '\\cordova') && fso.FolderExists(path + CORDOVA_DEPLOY) && 
+        fso.FileExists(path + CORDOVA_DEPLOY + '\\CordovaDeploy.sln')) {
+        // delete any previously generated files
+        if (fso.FolderExists(path + CORDOVA_DEPLOY + '\\CordovaDeploy\\obj')) {
+            fso.DeleteFolder(path + CORDOVA_DEPLOY + '\\CordovaDeploy\\obj');
+        }
+        if (fso.FolderExists(path + CORDOVA_DEPLOY + '\\CordovaDeploy\\Bin')) {
+            fso.DeleteFolder(path + CORDOVA_DEPLOY + '\\CordovaDeploy\\Bin');
+        }
+        exec_verbose('msbuild ' + path + CORDOVA_DEPLOY + '\\CordovaDeploy.sln');
+
+        if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+            Log('CordovaDeploy.exe compiled, SUCCESS.');
+        }
+        else {
+            Log('ERROR: MSBUILD FAILED TO COMPILE CordovaDeploy.exe', true);
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log('ERROR: CordovaDeploy.sln not found, unable to compile CordovaDeploy tool.', true);
+        WScript.Quit(2);
+    }
+}
+
+// launches project on device
+function device(path)
+{
+    cordovaDeploy(path);
+    if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        Log('Deploying to device ...');
+        //TODO: get device ID from list-devices and deploy to first one
+        exec_verbose('%comspec% /c ' + path + CORDOVA_DEPLOY_EXE + ' ' + path + ' -d:0');
+    }
+    else
+    {
+        Log('Error: Failed to find CordovaDeploy.exe in ' + path, true);
+        Log('DEPLOY FAILED.', true);
+        WScript.Quit(2);
+    }
+}
+
+// launches project on emulator
+function emulator(path)
+{
+    cordovaDeploy(path);
+    if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        Log('Deploying to emulator ...');
+        //TODO: get emulator ID from list-emulators and deploy to first one
+        exec_verbose('%comspec% /c ' + path + CORDOVA_DEPLOY_EXE + ' ' + path + ' -d:1');
+    }
+    else
+    {
+        Log('Error: Failed to find CordovaDeploy.exe in ' + path, true);
+        Log('DEPLOY FAILED.', true);
+        WScript.Quit(2);
+    }
+}
+
+// builds and launches the project on the specified target
+function target(path, device_id) {
+    if (!fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        cordovaDeploy(path);
+    }
+    wscript_shell.CurrentDirectory = path + CORDOVA_DEPLOY + '\\CordovaDeploy\\bin\\Debug';
+    var cmd = 'CordovaDeploy -devices';
+    var out = wscript_shell.Exec(cmd);
+    while(out.Status == 0) {
+        WScript.Sleep(100);
+    }
+    if (!out.StdErr.AtEndOfStream) {
+        var line = out.StdErr.ReadAll();
+        Log("Error calling CordovaDeploy : ", true);
+        Log(line, true);
+        WScript.Quit(2);
+    }
+    else {
+        if (!out.StdOut.AtEndOfStream) {
+            var line = out.StdOut.ReadAll();
+            var targets = line.split('\r\n');
+            var check_id = new RegExp(device_id);
+            for (target in targets) {
+                if (targets[target].match(check_id)) {
+                    //TODO: this only gets single digit index, account for device index of 10+?
+                    var index = targets[target].substr(0,1);
+                    exec_verbose(path + CORDOVA_DEPLOY_EXE + ' ' + path + ' -d:' + index);
+                    return;
+                }
+            }
+            Log('Error : target ' + device_id + ' was not found.', true);
+            Log('DEPLOY FAILED.', true);
+            WScript.Quit(2);
+        }
+        else {
+            Log('Error : CordovaDeploy Failed to find any devices', true);
+            Log('DEPLOY FAILED.', true);
+            WScript.Quit(2);
+        }
+    }
+}
+
+function build(path) {
+    switch (build_type) {
+        case DEBUG :
+            exec_verbose('%comspec% /c ' + ROOT + '\\cordova\\build --debug');
+            break;
+        case RELEASE :
+            exec_verbose('%comspec% /c ' + ROOT + '\\cordova\\build --release');
+            break;
+        case NO_BUILD :
+            break;
+        case NONE :
+            Log("WARNING: [ --debug | --release | --nobuild ] not specified, defaulting to --debug.");
+            exec_verbose('%comspec% /c ' + ROOT + '\\cordova\\build --debug');
+            break;
+        default :
+            Log("Build option not recognized: " + build_type, true);
+            WScript.Quit(2);
+            break;
+    }
+}
+
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help") {
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (args.Count() > 2) {
+        Log('Error: Too many arguments.', true);
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (fso.FolderExists(ROOT)) {
+        if (args.Count() > 1) {
+            if (args(1) == "--release") {
+                build_type = RELEASE;
+            }
+            else if (args(1) == "--debug") {
+                build_type = DEBUG;
+            }
+            else if (args(1) == "--nobuild") {
+                build_type = NO_BUILD;
+            }
+            else {
+                Log('Error: \"' + args(1) + '\" is not recognized as a deploy option', true);
+                Usage();
+                WScript.Quit(2);
+            }
+        }
+
+        if (args(0) == "--emulator" || args(0) == "-e") {
+            build(ROOT);
+            emulator(ROOT);
+        }
+        else if (args(0) == "--device" || args(0) == "-d") {
+            build(ROOT);
+            device(ROOT);
+        }
+        else if (args(0).substr(0,9) == "--target=") {
+            build(ROOT);
+            var device_id = args(0).split("--target=").join("");
+            target(ROOT, device_id);
+        }
+        else {
+            Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, defaulting to --emulator");
+            if (args(0) == "--release") {
+                build_type = RELEASE;
+                build(ROOT);
+                emulator(ROOT);
+            }
+            else if (args(0) == "--debug") {
+                build_type = DEBUG;
+                build(ROOT);
+                emulator(ROOT);
+            }
+            else if (args(0) == "--nobuild") {
+                build_type = NO_BUILD;
+                emulator(ROOT);
+            }
+            else {
+                Log('Error: \"' + args(0) + '\" is not recognized as a deploy option', true);
+                Usage();
+                WScript.Quit(2);
+            }
+        }
+    }
+    else {
+        Log('Error: Project directory not found,', true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
+else {
+    Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, defaulting to --emulator");
+    build(ROOT);
+    emulator(ROOT);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/install-device.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/install-device.bat b/lib/cordova-wp7/templates/standalone/cordova/lib/install-device.bat
new file mode 100644
index 0000000..9507c36
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/install-device.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%deploy.js (
+    cscript "%full_path%deploy.js" %* --device --nobuild //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'deploy.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/install-emulator.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/install-emulator.bat b/lib/cordova-wp7/templates/standalone/cordova/lib/install-emulator.bat
new file mode 100644
index 0000000..b3ee451
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/install-emulator.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%deploy.js (
+    cscript "%full_path%deploy.js" %* --emulator --nobuild //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'deploy.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/list-devices.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/list-devices.bat b/lib/cordova-wp7/templates/standalone/cordova/lib/list-devices.bat
new file mode 100644
index 0000000..bf4492b
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/list-devices.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%target-list.js (
+    cscript "%full_path%target-list.js" %* --devices //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'target-list.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/list-emulator-images.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/list-emulator-images.bat b/lib/cordova-wp7/templates/standalone/cordova/lib/list-emulator-images.bat
new file mode 100644
index 0000000..3f571c7
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/list-emulator-images.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%target-list.js (
+    cscript "%full_path%target-list.js" %* --emulators //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'target-list.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/list-started-emulators.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/list-started-emulators.bat b/lib/cordova-wp7/templates/standalone/cordova/lib/list-started-emulators.bat
new file mode 100644
index 0000000..d779b5d
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/list-started-emulators.bat
@@ -0,0 +1,3 @@
+@ECHO OFF
+ECHO Sorry, list-started-emulators is not availible yet for Windows Phone. 1>&2
+EXIT /B 1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/log.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/log.js b/lib/cordova-wp7/templates/standalone/cordova/lib/log.js
new file mode 100644
index 0000000..0b4ea7d
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/log.js
@@ -0,0 +1,77 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject');
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+var args = WScript.Arguments;
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\log.js').join('');
+
+
+// help function
+function Usage() {
+    Log("");
+    Log("Usage: log");
+    Log("examples:");
+    Log("    log");
+    Log("         - logs output from running application  *NOT IMPLIMENTED*");
+    Log("");
+}
+
+//  logs to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// log output from running projects *NOT IMPLEMENTED*
+function log_output(path) {
+    Log("ERROR: Logging is not supported on Windows Phone", true);
+    WScript.Quit(1);
+}
+
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help") {
+        Usage();
+        WScript.Quit(2);
+    }
+    else {
+        Log("Error: \"" + args(0) + "\" is not recognized as a log option.", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
+else {
+   if (fso.FolderExists(ROOT)) {
+        log_output(ROOT);
+    }
+    else {
+        Log("Error: Project directory not found,", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/start-emulator.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/start-emulator.bat b/lib/cordova-wp7/templates/standalone/cordova/lib/start-emulator.bat
new file mode 100644
index 0000000..19983fd
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/start-emulator.bat
@@ -0,0 +1,3 @@
+@ECHO OFF
+ECHO Sorry, start-emulator is not availible yet for Windows Phone. 1>&2
+EXIT /B 1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/target-list.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/target-list.js b/lib/cordova-wp7/templates/standalone/cordova/lib/target-list.js
new file mode 100644
index 0000000..805eea5
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/target-list.js
@@ -0,0 +1,233 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject');
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments;
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\cordova\\lib\\target-list.js').join('');
+    // path to CordovaDeploy.exe
+var CORDOVA_DEPLOY_EXE = '\\cordova\\lib\\CordovaDeploy\\CordovaDeploy\\bin\\Debug\\CordovaDeploy.exe';
+    // path to CordovaDeploy
+var CORDOVA_DEPLOY = '\\cordova\\lib\\CordovaDeploy';
+
+// help/usage function
+function Usage() {
+    Log("");
+    Log("Usage: cscript target-list.js  [ --emulators | --devices | --started_emulators | --all ]");
+    Log("    --emulators         : List the possible target emulators availible.");
+    Log("    --devices           : List the possible target devices availible. *NOT IMPLEMENTED YET*");
+    Log("    --started_emulators : List any started emulators availible. *NOT IMPLEMENTED YET*");
+    Log("    --all               : List all devices returned by CordovaDeploy.exe -devices ");
+    Log("examples:");
+    Log("    cscript target-list.js --emulators");
+    Log("    cscript target-list.js --devices");
+    Log("    cscript target-list.js --started_emulators");
+    Log("    cscript target-list.js --all");
+    Log("");
+}
+
+// logs messaged to stdout and stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print output? Naa.....
+        if (!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadAll();
+            //Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oShell.StdErr.AtEndOfStream) {
+        var line = oShell.StdErr.ReadAll();
+        Log(line, true);
+        WScript.Quit(2);
+    }
+}
+
+// returns all possible targets generated by the CordovaDeploy tool
+function get_targets(path) {
+    if (!fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        cordovaDeploy(path);
+    }
+    wscript_shell.CurrentDirectory = path + CORDOVA_DEPLOY + '\\CordovaDeploy\\bin\\Debug';
+    var cmd = 'CordovaDeploy -devices';
+    var out = wscript_shell.Exec(cmd);
+    while(out.Status == 0) {
+        WScript.Sleep(100);
+    }
+    //Check to make sure our script did not encounter an error
+    if (!out.StdErr.AtEndOfStream) {
+        var line = out.StdErr.ReadAll();
+        Log("Error calling CordovaDeploy : ", true);
+        Log(line, true);
+        WScript.Quit(2);
+    }
+    else {
+        if (!out.StdOut.AtEndOfStream) {
+            var line = out.StdOut.ReadAll();
+            var targets = line.split('\r\n');
+            //format (ID DESCRIPTION)
+            for (i in targets) {
+                // remove device index and separator colen
+                targets[i] = targets[i].replace(/\d*\s\:\s/, '').replace(/\:\s/, '');
+            }
+            return targets;
+        }
+        else {
+            Log('Error : CordovaDeploy Failed to find any devices', true);
+            WScript.Quit(2);
+        }
+    }
+}
+
+function list_targets(path) {
+    var targets = get_targets(path);
+    for (i in targets) {
+        Log(targets[i]);
+    }
+}
+
+// lists the Device returned by CordovaDeploy (NOTE: this does not indicate that a device is connected)
+function list_devices(path) {
+    var targets = get_targets(path);
+    var device_found = false;
+    for (i in targets) {
+        if (targets[i].match(/Device/)) {
+            Log(targets[i]);
+            device_found = true;
+        }
+    }
+    if (device_found) {
+        Log('');
+        Log('WARNING : This does not mean that a device is connected, make');
+        Log(' sure your device is connected before deploying to it.');
+    }
+}
+
+// lists the emulators availible to CordovaDeploy
+function list_emulator_images(path) {
+    var targets = get_targets(path);
+    for (i in targets) {
+        if (targets[i].match(/Emulator/)) {
+            Log(targets[i]);
+        }
+    }
+}
+
+// lists any started emulators *NOT IMPLEMENTED*
+function list_started_emulators(path) {
+    Log('ERROR : list-started-emulators is not supported on Windows Phone.', true);
+    WScript.Quit(1);
+}
+
+// builds the CordovaDeploy.exe if it does not already exist 
+function cordovaDeploy(path) {
+    if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+        return;
+    }
+
+    // build CordovaDeploy.exe
+    if (fso.FolderExists(path + '\\cordova') && fso.FolderExists(path + CORDOVA_DEPLOY) && 
+        fso.FileExists(path + CORDOVA_DEPLOY + '\\CordovaDeploy.sln')) {
+        // delete any previously generated files
+        if (fso.FolderExists(path + CORDOVA_DEPLOY + "\\CordovaDeploy\\obj")) {
+            fso.DeleteFolder(path + CORDOVA_DEPLOY + "\\CordovaDeploy\\obj");
+        }
+        if (fso.FolderExists(path + CORDOVA_DEPLOY + "\\CordovaDeploy\\Bin")) {
+            fso.DeleteFolder(path + CORDOVA_DEPLOY + "\\CordovaDeploy\\Bin");
+        }
+        exec('msbuild ' + path + CORDOVA_DEPLOY + '\\CordovaDeploy.sln');
+
+        if (fso.FileExists(path + CORDOVA_DEPLOY_EXE)) {
+            return;
+        }
+        else {
+            Log("ERROR: MSBUILD FAILED TO COMPILE CordovaDeploy.exe", true);
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log("ERROR: CordovaDeploy.sln not found, unable to compile CordovaDeploy tool.", true);
+        WScript.Quit(2);
+    }
+}
+
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help") {
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (args.Count() > 1) {
+        Log("Error: Too many arguments.", true);
+        Usage();
+        WScript.Quit(2);
+    }
+    else if (fso.FolderExists(ROOT)) {
+        if (!fso.FolderExists(ROOT + '\\cordova')) {
+            Log("Error: cordova tooling folder not found in project directory,", true);
+            Log("could not lsit targets.", true);
+            WScript.Quit(2);
+        }
+
+        if (args(0) == "--emulators" || args(0) == "-e") {
+            list_emulator_images(ROOT);
+        }
+        else if (args(0) == "--devices" || args(0) == "-d") {
+            list_devices(ROOT);
+        }
+        else if (args(0) == "--started_emulators" || args(0) == "-s") {
+            list_started_emulators(ROOT);
+        }
+        else if (args(0) == "--all" || args(0) == "-a") {
+            list_targets(ROOT);
+        }
+        else {
+            Log("Error: \"" + args(0) + "\" is not recognized as a target-list option", true);
+            Usage();
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log("Error: Project directory not found,", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
+else {
+    Log("WARNING: target list not specified, showing all targets...");
+    list_targets(ROOT);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/log.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/log.bat b/lib/cordova-wp7/templates/standalone/cordova/log.bat
new file mode 100644
index 0000000..46dbe5c
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/log.bat
@@ -0,0 +1,3 @@
+@ECHO OFF
+ECHO Sorry, loging is yet supported for Windows Phone. 1>&2
+EXIT /B 1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/run.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/run.bat b/lib/cordova-wp7/templates/standalone/cordova/run.bat
new file mode 100644
index 0000000..b966856
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/run.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%lib\deploy.js (
+        cscript "%full_path%lib\deploy.js" %* //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'deploy.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/BrowserMouseHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/BrowserMouseHelper.cs b/lib/cordova-wp7/templates/standalone/cordovalib/BrowserMouseHelper.cs
new file mode 100644
index 0000000..acd1bcd
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/BrowserMouseHelper.cs
@@ -0,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. 
+ */
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Phone.Controls;
+using System.Windows.Input;
+using System.Diagnostics;
+using System.Windows.Media;
+using System;
+using System.Collections.Generic;
+
+namespace WPCordovaClassLib
+{
+
+    /// <summary>
+    /// Suppresses pinch zoom and optionally scrolling of the WebBrowser control
+    /// </summary>
+    public class BrowserMouseHelper
+    {
+
+        /**
+         * 
+         * Full Script below, in use it is minified.
+        */
+        /*
+        private static string mouseScript =
+        @"(function(win,doc){
+            var mPro = MouseEvent.prototype;
+            var def = Object.defineProperty;
+            def( mPro, 'pageX', {
+               configurable: true,
+               get: function(){ return this.clientX }
+            });
+            def( mPro, 'pageY', {
+               configurable: true,
+               get: function(){ return this.clientY }
+            });
+
+            win.onNativeMouseEvent = function(type,x,y){
+                try {
+                    var xMod = screen.logicalXDPI / screen.deviceXDPI;
+                    var yMod = screen.logicalYDPI / screen.deviceYDPI;
+                    var evt = doc.createEvent('MouseEvents');
+                    var xPos =  doc.body.scrollLeft + Math.round(xMod * x);
+                    var yPos =  doc.body.scrollTop + Math.round(yMod * y);
+                    var element = doc.elementFromPoint(xPos,yPos);
+                    
+                    evt.initMouseEvent(type, true, true, win, 1, xPos, yPos, xPos, yPos, false, false, false, false, 0, element);
+                    evt.timeStamp = +new Date;
+                    evt.isCordovaEvent = true;
+                    
+                    var canceled = element ? !element.dispatchEvent(evt) : !doc.dispatchEvent(evt);
+                    return canceled ? 'true' : 'false';
+                }
+                catch(e) { return e;}
+            }
+        })(window,document);";
+        */
+
+        private static string MinifiedMouseScript = "(function(g,a){var c=MouseEvent.prototype,d=Object.defineProperty;d(c,'pageX',{configurable:!0,get:function(){return this.clientX}});d(c,'pageY',{configurable:!0,get:function(){return this.clientY}});g.onNativeMouseEvent=function(c,d,i)"
+        + "{try{var j=screen.logicalXDPI/screen.deviceXDPI,k=screen.logicalYDPI/screen.deviceYDPI,b=a.createEvent('MouseEvents'),e=a.body.scrollLeft+Math.round(j*d),f=a.body.scrollTop+Math.round(k*i),h=a.elementFromPoint(e,f);b.initMouseEvent(c,!0,!0,g,1,e,f,e,f,!1,!1,!1,!1,0,"
+        + "h);b.timeStamp=+new Date;b.isCordovaEvent=!0;return(h?!h.dispatchEvent(b):!a.dispatchEvent(b))?'true':'false'}catch(l){return l}}})(window,document);";
+
+
+        private WebBrowser _browser;
+
+        /// <summary>
+        /// Gets or sets whether to suppress the scrolling of
+        /// the WebBrowser control;
+        /// </summary>
+        public bool ScrollDisabled { get; set; }
+
+        private bool userScalable = true;
+        private double maxScale = 2.0;
+        private double minScale = 0.5;
+        protected Border border;
+        private bool firstMouseMove = false;
+
+        /// <summary>
+        /// Represents last known mouse down position. 
+        /// Used to determine mouse move delta to avoid duplicate mouse events.
+        /// </summary>
+        private Point mouseDownPos;
+
+        /// <summary>
+        /// Represent min delta value to consider event as a mouse move. Experimental calculated.
+        /// </summary>
+        private const int MouseMoveDeltaThreshold = 10;
+
+
+        public BrowserMouseHelper(ref WebBrowser browser)
+        {
+            _browser = browser;
+            browser.Loaded += new RoutedEventHandler(browser_Loaded);
+        }
+
+        private void browser_Loaded(object sender, RoutedEventArgs e)
+        {
+            var border0 = VisualTreeHelper.GetChild(_browser, 0);
+            var border1 = VisualTreeHelper.GetChild(border0, 0);
+            var panZoom = VisualTreeHelper.GetChild(border1, 0);
+            var grid = VisualTreeHelper.GetChild(panZoom, 0);
+            border = VisualTreeHelper.GetChild(grid, 0) as Border;
+
+            if (border != null)
+            {
+                border.ManipulationStarted += Border_ManipulationStarted;
+                border.ManipulationDelta += Border_ManipulationDelta;
+                border.ManipulationCompleted += Border_ManipulationCompleted;
+                border.DoubleTap += Border_DoubleTap;
+                border.Tap += Border_Tap;
+                border.Hold += Border_Hold;
+                border.MouseLeftButtonDown += Border_MouseLeftButtonDown;
+            }
+
+            _browser.LoadCompleted += Browser_LoadCompleted;
+
+        }
+
+
+
+
+        void ParseViewportMeta()
+        {
+            string metaScript = "(function() { return document.querySelector('meta[name=viewport]').content; })()";
+
+            try
+            {
+                string metaContent = _browser.InvokeScript("eval", new string[] { metaScript }) as string;
+                string[] arr = metaContent.Split(new[] { ' ', ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
+                Dictionary<string, string> metaDictionary = new Dictionary<string, string>();
+                foreach (string val in arr)
+                {
+                    string[] keyVal = val.Split('=');
+                    metaDictionary.Add(keyVal[0], keyVal[1]);
+                }
+
+                this.userScalable = false; // reset to default
+                if (metaDictionary.ContainsKey("user-scalable"))
+                {
+                    this.userScalable = metaDictionary["user-scalable"] == "yes";
+                }
+
+                this.maxScale = 2.0;// reset to default
+                if (metaDictionary.ContainsKey("maximum-scale"))
+                {
+                    this.maxScale = double.Parse(metaDictionary["maximum-scale"]);
+                }
+
+                this.minScale = 0.5;// reset to default
+                if (metaDictionary.ContainsKey("minimum-scale"))
+                {
+                    this.minScale = double.Parse(metaDictionary["minimum-scale"]);
+                }
+            }
+            catch (Exception)
+            {
+
+            }
+        }
+
+        void Browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            ParseViewportMeta();
+
+            try
+            {
+                _browser.InvokeScript("execScript", MinifiedMouseScript);
+            }
+            catch (Exception)
+            {
+                Debug.WriteLine("BrowserHelper Failed to install mouse script in WebBrowser");
+            }
+        }
+
+        bool InvokeSimulatedMouseEvent(string eventName, Point pos)
+        {
+            bool bCancelled = false;
+            try
+            {
+                string strCancelled = _browser.InvokeScript("onNativeMouseEvent", new string[] { eventName, pos.X.ToString(), pos.Y.ToString() }) as string;
+                if (bool.TryParse(strCancelled, out bCancelled))
+                {
+                    return bCancelled;
+                }
+            }
+            catch (Exception)
+            {
+                // script error
+            }
+
+            return bCancelled;
+        }
+
+        #region Hold
+
+        void Border_Hold(object sender, GestureEventArgs e)
+        {
+            //Debug.WriteLine("Border_Hold");
+            e.Handled = true;
+        }
+
+        #endregion
+
+        #region DoubleTap
+
+        void Border_DoubleTap(object sender, GestureEventArgs e)
+        {
+            //Debug.WriteLine("Border_DoubleTap");
+            e.Handled = true;
+        }
+
+        #endregion
+
+        #region Tap
+
+        void Border_Tap(object sender, GestureEventArgs e)
+        {
+            // prevents generating duplicated mouse events
+            // firstMouseMove == FALSE means we already handled this situation and generated mouse events
+            e.Handled = !this.firstMouseMove;
+        }
+        #endregion
+
+        #region MouseEvents
+
+        void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+        {
+            //Debug.WriteLine("Border_MouseLeftButtonDown");
+            border.MouseMove += new MouseEventHandler(Border_MouseMove);
+            border.MouseLeftButtonUp += new MouseButtonEventHandler(Border_MouseLeftButtonUp);
+
+            this.mouseDownPos = e.GetPosition(_browser);
+            // don't fire the down event until we know if this is a 'move' or not
+            firstMouseMove = true;
+        }
+        //
+        void Border_MouseMove(object sender, MouseEventArgs e)
+        {
+            //Debug.WriteLine("Border_MouseMove");
+            Point pos = e.GetPosition(_browser);
+            // only the return value from the first mouse move event should be used to determine if scrolling is prevented.
+            if (firstMouseMove)
+            {
+                // even for simple tap there are situations where ui control generates move with some little delta value
+                // we should avoid such situations allowing to browser control generate native js mousedown/up/click events
+                if (Math.Abs(pos.X - mouseDownPos.X) + Math.Abs(pos.Y - mouseDownPos.Y) <= MouseMoveDeltaThreshold)
+                {
+                    return;
+                }
+
+                InvokeSimulatedMouseEvent("mousedown", pos);
+                firstMouseMove = false;
+                ScrollDisabled = InvokeSimulatedMouseEvent("mousemove", pos);
+            }
+            else
+            {
+                InvokeSimulatedMouseEvent("mousemove", pos);
+            }
+
+        }
+
+        void Border_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+        {
+            //Debug.WriteLine("Border_MouseLeftButtonUp");
+            border.MouseMove -= new MouseEventHandler(Border_MouseMove);
+            border.MouseLeftButtonUp -= new MouseButtonEventHandler(Border_MouseLeftButtonUp);
+            // if firstMouseMove is false, then we have sent our simulated mousedown, so we should also send a matching mouseup 
+            if (!firstMouseMove)
+            {
+                Point pos = e.GetPosition(_browser);
+                e.Handled = InvokeSimulatedMouseEvent("mouseup", pos);
+            }
+            ScrollDisabled = false;
+        }
+
+
+        #endregion
+
+        #region ManipulationEvents
+
+        void Border_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
+        {
+            //Debug.WriteLine("Border_ManipulationStarted");
+
+            if (ScrollDisabled)
+            {
+                e.Handled = true;
+                e.Complete();
+            }
+        }
+
+        private void Border_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
+        {
+            //Debug.WriteLine("Border_ManipulationDelta");
+            // optionally suppress zoom
+            if ((ScrollDisabled || !userScalable) && (e.DeltaManipulation.Scale.X != 0.0 || e.DeltaManipulation.Scale.Y != 0.0))
+            {
+                e.Handled = true;
+                e.Complete();
+            }
+            // optionally suppress scrolling
+            if (ScrollDisabled && (e.DeltaManipulation.Translation.X != 0.0 || e.DeltaManipulation.Translation.Y != 0.0))
+            {
+                e.Handled = true;
+                e.Complete();
+            }
+        }
+
+        private void Border_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
+        {
+            //Debug.WriteLine("Border_ManipulationCompleted");
+            // suppress zoom
+            if (!userScalable && e.FinalVelocities != null)
+            {
+                if (e.FinalVelocities.ExpansionVelocity.X != 0.0 ||
+                   e.FinalVelocities.ExpansionVelocity.Y != 0.0)
+                {
+                    e.Handled = true;
+                }
+            }
+        }
+
+
+        #endregion
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/CommandFactory.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/CommandFactory.cs b/lib/cordova-wp7/templates/standalone/cordovalib/CommandFactory.cs
new file mode 100644
index 0000000..893ce80
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/CommandFactory.cs
@@ -0,0 +1,112 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Collections.Generic;
+using WPCordovaClassLib.Cordova.Commands;
+using System.Reflection;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova
+{
+    /// <summary>
+    /// Provides functionality to create phone gap command by name.
+    /// </summary>
+    public static class CommandFactory
+    {
+        /// <summary>
+        /// Represents predefined namespace name for custom plugins
+        /// </summary>
+        private static readonly string CustomPluginNamespacePrefix = "Cordova.Extension.Commands.";
+
+        private static readonly string BaseCommandNamespacePrefix = "WPCordovaClassLib.Cordova.Commands.";
+
+        /// <summary>
+        /// Cache instantiated commands in a map.
+        /// </summary>
+
+        private static Dictionary<string, BaseCommand> commandMap = new Dictionary<string, BaseCommand>();
+
+        /// <summary>
+        /// Creates command using command class name. Returns null for unknown commands.
+        /// </summary>
+        /// <param name="service">Command class name, for example Device or Notification</param>
+        /// <returns>Command class instance or null</returns>
+        public static BaseCommand CreateByServiceName(string service)
+        {
+
+            if (string.IsNullOrEmpty(service))
+            {
+                throw new ArgumentNullException("service", "service to create can't be null");
+            }
+
+            if (!commandMap.ContainsKey(service))
+            {
+
+                Type t = Type.GetType(BaseCommandNamespacePrefix + service);
+
+                // custom plugin could be defined in own namespace and assembly
+                if (t == null)
+                {
+                    string serviceFullName = service.Contains(".") ? service : CustomPluginNamespacePrefix + service;
+
+                    foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
+                    {
+                        // in this case service name represents full type name including namespace
+                        t = a.GetType(serviceFullName);
+
+                        if (t == null) // try the Commands Namespace
+                        {
+                            t = a.GetType(BaseCommandNamespacePrefix + service);
+                        }
+
+                        if (t != null)
+                        {
+                            break;
+                        }
+                    }
+
+                }
+
+                // unknown command, still didn't find it
+                if (t == null)
+                {
+                    Debug.WriteLine("Unable to locate command :: " + service);
+                    return null;
+                }
+
+                commandMap[service] = Activator.CreateInstance(t) as BaseCommand;
+            }
+
+            return commandMap[service];
+        }
+
+        public static void ResetAllCommands()
+        {
+            foreach (BaseCommand bc in commandMap.Values)
+            {
+                bc.OnReset();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/Commands/BaseCommand.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/Commands/BaseCommand.cs b/lib/cordova-wp7/templates/standalone/cordovalib/Commands/BaseCommand.cs
new file mode 100644
index 0000000..ac1d2d6
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/Commands/BaseCommand.cs
@@ -0,0 +1,187 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Reflection;
+using Microsoft.Phone.Shell;
+using System.Diagnostics;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public abstract class BaseCommand : IDisposable
+    {
+        /*
+         *  All commands + plugins must extend BaseCommand, because they are dealt with as BaseCommands in CordovaView.xaml.cs
+         *  
+         **/
+
+        public event EventHandler<PluginResult> OnCommandResult;
+
+        public event EventHandler<ScriptCallback> OnCustomScript;
+
+        public string CurrentCommandCallbackId { get; set; }
+
+        public BaseCommand()
+        {
+            ResultHandlers = new Dictionary<string, EventHandler<PluginResult>>();
+            PhoneApplicationService service = PhoneApplicationService.Current;
+            service.Activated += this.OnResume;
+            service.Deactivated += this.OnPause;
+        }
+
+        protected Dictionary<string, EventHandler<PluginResult>> ResultHandlers;
+        public void AddResultHandler(string callbackId, EventHandler<PluginResult> handler)
+        {
+            ResultHandlers.Add(callbackId, handler);
+        }
+        public bool RemoveResultHandler(string callbackId)
+        {
+            return ResultHandlers.Remove(callbackId);
+        }
+
+        /*
+         *  InvokeMethodNamed will call the named method of a BaseCommand subclass if it exists and pass the variable arguments list along.
+         **/
+
+        public object InvokeMethodNamed(string callbackId, string methodName, params object[] args)
+        {
+            //Debug.WriteLine(string.Format("InvokeMethodNamed:{0} callbackId:{1}",methodName,callbackId));
+            this.CurrentCommandCallbackId = callbackId;
+            return InvokeMethodNamed(methodName, args);
+        }
+
+        public object InvokeMethodNamed(string methodName, params object[] args)
+        {
+            MethodInfo mInfo = this.GetType().GetMethod(methodName);
+
+            if (mInfo != null)
+            {
+                // every function handles DispatchCommandResult by itself
+                return mInfo.Invoke(this, args);
+            }
+
+            // actually methodName could refer to a property
+            if (args == null || args.Length == 0 ||
+               (args.Length == 1 && "undefined".Equals(args[0])))
+            {
+                PropertyInfo pInfo = this.GetType().GetProperty(methodName);
+                if (pInfo != null)
+                {
+                    object res = pInfo.GetValue(this, null);
+
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, res));
+
+                    return res;
+                }
+            }
+
+            throw new MissingMethodException(methodName);
+
+        }
+
+        [Obsolete]
+        public void InvokeCustomScript(ScriptCallback script, bool removeHandler)
+        {
+            if (this.OnCustomScript != null)
+            {
+                this.OnCustomScript(this, script);
+                if (removeHandler)
+                {
+                    this.OnCustomScript = null;
+                }
+            }
+        }
+
+        public void DispatchCommandResult()
+        {
+            this.DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
+        }
+
+        public void DispatchCommandResult(PluginResult result, string callbackId = "")
+        {
+            if (!string.IsNullOrEmpty(callbackId)) 
+            {
+                result.CallbackId = callbackId;
+            }
+            else
+            {
+                result.CallbackId = this.CurrentCommandCallbackId;
+            }
+
+            if (ResultHandlers.ContainsKey(result.CallbackId))
+            {
+                ResultHandlers[result.CallbackId](this, result);
+            }
+            else if (this.OnCommandResult != null)
+            {
+                OnCommandResult(this, result);
+            }
+            else
+            {
+                Debug.WriteLine("Failed to locate callback for id : " + result.CallbackId);
+            }
+
+            if (!result.KeepCallback)
+            {
+                this.Dispose();
+            }
+
+        }
+
+
+        /// <summary>
+        /// Occurs when the application is being deactivated.
+        /// </summary>        
+        public virtual void OnReset() {}
+
+        /// <summary>
+        /// Occurs when the application is being loaded, and the config.xml has an autoload entry
+        /// </summary>    
+        public virtual void OnInit() {}
+
+
+        /// <summary>
+        /// Occurs when the application is being deactivated.
+        /// </summary>        
+        public virtual void OnPause(object sender, DeactivatedEventArgs e) {}
+
+        /// <summary>
+        /// Occurs when the application is being made active after previously being put
+        /// into a dormant state or tombstoned.
+        /// </summary>        
+        public virtual void OnResume(object sender, Microsoft.Phone.Shell.ActivatedEventArgs e) {}
+
+        public void Dispose()
+        {
+            PhoneApplicationService service = PhoneApplicationService.Current;
+            service.Activated -= this.OnResume;
+            service.Deactivated -= this.OnPause;
+            this.OnCommandResult = null;
+        }
+
+        public static string GetBaseURL()
+        {
+#if CORDOVA_CLASSLIB
+            return "/WPCordovaClassLib;component/";
+#else
+            return "./";
+#endif
+        }
+    }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/ConfigHandler.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/ConfigHandler.cs b/lib/cordova-wp7/templates/standalone/cordovalib/ConfigHandler.cs
new file mode 100644
index 0000000..d90e9b5
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/ConfigHandler.cs
@@ -0,0 +1,249 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows;
+using System.Windows.Resources;
+using System.Xml.Linq;
+
+namespace WPCordovaClassLib.CordovaLib
+{
+    class ConfigHandler
+    {
+        protected List<string> AllowedPlugins;
+        protected List<string> AllowedDomains;
+        protected Dictionary<string, string> Preferences;
+
+        protected bool AllowAllDomains = false;
+        protected bool AllowAllPlugins = false;
+
+        public ConfigHandler()
+        {
+            AllowedPlugins = new List<string>();
+            AllowedDomains = new List<string>();
+            Preferences = new Dictionary<string, string>();
+        }
+
+        public string GetPreference(string key)
+        {
+            return Preferences[key];
+        }
+
+/*
+    - (BOOL)URLIsAllowed:(NSURL*)url
+{
+    if (self.expandedWhitelist == nil) {
+        return NO;
+    }
+
+    if (self.allowAll) {
+        return YES;
+    }
+
+    // iterate through settings ExternalHosts, check for equality
+    NSEnumerator* enumerator = [self.expandedWhitelist objectEnumerator];
+    id regex = nil;
+    NSString* urlHost = [url host];
+
+    // if the url host IS found in the whitelist, load it in the app (however UIWebViewNavigationTypeOther kicks it out to Safari)
+    // if the url host IS NOT found in the whitelist, we do nothing
+    while (regex = [enumerator nextObject]) {
+        NSPredicate* regex_test = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
+
+        if ([regex_test evaluateWithObject:urlHost] == YES) {
+            // if it matches at least one rule, return
+            return YES;
+        }
+    }
+
+    NSLog(@"%@", [self errorStringForURL:url]);
+    // if we got here, the url host is not in the white-list, do nothing
+    return NO;
+}*/
+        protected static string[] AllowedSchemes = {"http","https","ftp","ftps"};
+        protected bool SchemeIsAllowed(string scheme)
+        {
+            return AllowedSchemes.Contains(scheme);
+        }
+
+        protected string PathAndQuery(Uri uri)
+        {
+            string result = uri.LocalPath;
+            if (uri.Query.Length > 0)
+            {
+                result +=  uri.Query;
+            }
+            return result;
+        }
+
+        protected void AddWhiteListEntry(string origin, bool allowSubdomains)
+        {
+
+            if (origin == "*")
+            {
+                AllowAllDomains = true;
+            }
+
+            if (AllowAllDomains)
+            {
+                return;
+            }
+
+            string hostMatchingRegex = "";
+            string hostName;
+
+            try
+            {
+
+                Uri uri = new Uri(origin.Replace("*", "replaced-text"), UriKind.Absolute);
+
+                string tempHostName = uri.Host.Replace("replaced-text", "*");
+                //if (uri.HostNameType == UriHostNameType.Dns){}        
+                // starts with wildcard match - we make the first '.' optional (so '*.org.apache.cordova' will match 'org.apache.cordova')
+                if (tempHostName.StartsWith("*."))
+                {    //"(\\s{0}|*.)"
+                    hostName = @"\w*.*" + tempHostName.Substring(2).Replace(".", @"\.").Replace("*", @"\w*");
+                }
+                else
+                {
+                    hostName = tempHostName.Replace(".", @"\.").Replace("*", @"\w*");
+                }
+
+                //  "^https?://"
+                hostMatchingRegex = uri.Scheme + "://" + hostName + PathAndQuery(uri);
+                //Debug.WriteLine("Adding regex :: " + hostMatchingRegex);
+                AllowedDomains.Add(hostMatchingRegex);
+
+            }
+            catch (Exception)
+            {
+                Debug.WriteLine("Invalid Whitelist entry (probably missing the protocol):: " + origin);
+            }
+
+        }
+
+        /**   
+         
+         An access request is granted for a given URI if there exists an item inside the access-request list such that:
+
+            - The URI's scheme component is the same as scheme; and
+            - if subdomains is false or if the URI's host component is not a domain name (as defined in [RFC1034]), the URI's host component is the same as host; or
+            - if subdomains is true, the URI's host component is either the same as host, or is a subdomain of host (as defined in [RFC1034]); and
+            - the URI's port component is the same as port.
+         
+         **/
+
+        public bool URLIsAllowed(string url)
+        {
+            // easy case first
+            if (AllowAllDomains )
+            {
+                return true;
+            }
+            else
+            {
+                // start simple
+                Uri uri = new Uri(url,UriKind.RelativeOrAbsolute);
+                if (uri.IsAbsoluteUri)
+                {
+                    if (this.SchemeIsAllowed(uri.Scheme))
+                    {
+                        // additional test because our pattern will always have a trailing '/'
+                        string matchUrl = url;
+                        if (PathAndQuery(uri) == "/")
+                        {
+                            matchUrl = url + "/";
+                        }
+                        foreach (string pattern in AllowedDomains)
+                        {
+                            if (Regex.IsMatch(matchUrl, pattern))
+                            {
+                                // make sure it is at the start, and not part of the query string
+                                // special case :: http://some.other.domain/page.html?x=1&g=http://build.apache.org/
+                                if ( Regex.IsMatch(uri.Scheme + "://" +  uri.Host + "/", pattern) ||
+                                     (!Regex.IsMatch(PathAndQuery(uri), pattern)))
+                                {
+                                    return true;
+                                }
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public bool IsPluginAllowed(string key)
+        {
+            return AllowAllPlugins || AllowedPlugins.Contains(key);
+        }
+
+
+        public void LoadAppPackageConfig()
+        {
+            StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("config.xml", UriKind.Relative));
+
+            if (streamInfo != null)
+            {
+                StreamReader sr = new StreamReader(streamInfo.Stream);
+                //This will Read Keys Collection for the xml file
+                XDocument document = XDocument.Parse(sr.ReadToEnd());
+
+                var plugins = from results in document.Descendants("plugin")
+                              select new { name = (string)results.Attribute("name") };
+
+
+                foreach (var plugin in plugins)
+                {
+                    Debug.WriteLine("plugin " + plugin.name);
+                    if (plugin.name == "*")
+                    {
+                        AllowAllPlugins = true;
+                        break;
+                    }
+                    else
+                    {
+                        AllowedPlugins.Add(plugin.name);
+                    }
+                }
+
+                var preferences = from results in document.Descendants("preference")
+                                  select new
+                                  {
+                                      name = (string)results.Attribute("name"),
+                                      value = (string)results.Attribute("value")
+                                  };
+
+                foreach (var pref in preferences)
+                {
+                    Debug.WriteLine("pref" + pref.name + ", " + pref.value);
+                }
+
+                var accessList = from results in document.Descendants("access")
+                                 select new
+                                 {
+                                     origin = (string)results.Attribute("origin"),
+                                     subdomains = (string)results.Attribute("subdomains") == "true"
+                                 };
+
+                foreach (var accessElem in accessList)
+                {
+                    AddWhiteListEntry(accessElem.origin, accessElem.subdomains);
+                }
+            }
+            else
+            {
+                // no config.xml, allow all
+                AllowAllDomains = true;
+                AllowAllPlugins = true;
+            }
+        }
+    }
+}


[13/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/contacts.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/contacts.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/contacts.html
new file mode 100644
index 0000000..2575e13
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/contacts.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Contacts API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/contacts.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/datauri.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/datauri.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/datauri.html
new file mode 100644
index 0000000..3d5ffd2
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/datauri.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<!--
+
+ 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.
+
+-->
+<html>
+
+<head>
+  <title>Cordova: Data URI tests</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/datauri.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/device.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/device.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/device.html
new file mode 100644
index 0000000..ed25d81
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/device.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Device API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/device.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/file.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/file.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/file.html
new file mode 100644
index 0000000..d9e21ca
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/file.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Cordova: File API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/file.tests.js"></script>
+
+  <script type="text/javascript">
+      var root, temp_root, persistent_root;
+
+      document.addEventListener('deviceready', function () {
+          // one-time retrieval of the root file system entry
+          var onError = function(e) {
+              console.log('[ERROR] Problem setting up root filesystem for test running! Error to follow.');
+              console.log(JSON.stringify(e));
+          };
+
+          window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting PERSISTENT FS.');
+                  root = fileSystem.root; // set in file.tests.js
+                  persistent_root = root;
+
+                  // Once root is set up, fire off tests
+                  var jasmineEnv = jasmine.getEnv();
+                  jasmineEnv.updateInterval = 1000;
+
+                  var htmlReporter = new jasmine.HtmlReporter();
+
+                  jasmineEnv.addReporter(htmlReporter);
+
+                  jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                  };
+
+                  jasmineEnv.execute();
+              }, onError);
+          window.requestFileSystem(LocalFileSystem.TEMPORARY, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting TEMPORARY FS.');
+                  temp_root = fileSystem.root; // set in file.tests.js
+              }, onError);
+      }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/filetransfer.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/filetransfer.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/filetransfer.html
new file mode 100644
index 0000000..0fde591
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/filetransfer.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Cordova: File API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/filetransfer.tests.js"></script>
+
+  <script type="text/javascript">
+      var root, temp_root, persistent_root;
+
+      document.addEventListener('deviceready', function () {
+          // one-time retrieval of the root file system entry
+          var onError = function(e) {
+              console.log('[ERROR] Problem setting up root filesystem for test running! Error to follow.');
+              console.log(JSON.stringify(e));
+          };
+
+          window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting PERSISTENT FS.');
+                  root = fileSystem.root; // set in file.tests.js
+                  persistent_root = root;
+
+                  // Once root is set up, fire off tests
+                  var jasmineEnv = jasmine.getEnv();
+                  jasmineEnv.updateInterval = 1000;
+
+                  var htmlReporter = new jasmine.HtmlReporter();
+
+                  jasmineEnv.addReporter(htmlReporter);
+
+                  jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                  };
+
+                  jasmineEnv.execute();
+              }, onError);
+          window.requestFileSystem(LocalFileSystem.TEMPORARY, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting TEMPORARY FS.');
+                  temp_root = fileSystem.root; // set in file.tests.js
+              }, onError);
+      }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/geolocation.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/geolocation.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/geolocation.html
new file mode 100644
index 0000000..578c6f4
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/geolocation.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Geolocation API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/geolocation.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/globalization.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/globalization.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/globalization.html
new file mode 100644
index 0000000..79c5acd
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/globalization.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<!--
+
+ 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.
+
+-->
+
+
+<html>
+<head>
+  <title>Cordova: Globalization API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/globalization.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/media.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/media.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/media.html
new file mode 100644
index 0000000..48d9e2d
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/media.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Media API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/media.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/network.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/network.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/network.html
new file mode 100644
index 0000000..627320c
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/network.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Network API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/network.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/notification.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/notification.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/notification.html
new file mode 100644
index 0000000..ef9d4a2
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/notification.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Notification API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/notification.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/platform.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/platform.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/platform.html
new file mode 100644
index 0000000..884ba45
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/platform.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Platform API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/platform.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/storage.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/storage.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/storage.html
new file mode 100644
index 0000000..eb0703b
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/storage.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Storage API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/storage.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/test-runner.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/test-runner.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/test-runner.js
new file mode 100644
index 0000000..e85578c
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/test-runner.js
@@ -0,0 +1,41 @@
+if (window.sessionStorage != null) {
+    window.sessionStorage.clear();
+}
+
+// Timeout is 2 seconds to allow physical devices enough
+// time to query the response. This is important for some
+// Android devices.
+var Tests = function() {};
+Tests.TEST_TIMEOUT = 7500;
+
+// Creates a spy that will fail if called.
+function createDoNotCallSpy(name, opt_extraMessage) {
+    return jasmine.createSpy().andCallFake(function() {
+        var errorMessage = name + ' should not have been called.';
+        if (arguments.length) {
+            errorMessage += ' Got args: ' + JSON.stringify(arguments);
+        }
+        if (opt_extraMessage) {
+            errorMessage += '\n' + opt_extraMessage;
+        }
+        expect(false).toBe(true, errorMessage);
+    });
+}
+
+// Waits for any of the given spys to be called.
+// Last param may be a custom timeout duration.
+function waitsForAny() {
+    var spys = [].slice.call(arguments);
+    var timeout = Tests.TEST_TIMEOUT;
+    if (typeof spys[spys.length - 1] == 'number') {
+        timeout = spys.pop();
+    }
+    waitsFor(function() {
+        for (var i = 0; i < spys.length; ++i) {
+            if (spys[i].wasCalled) {
+                return true;
+            }
+        }
+        return false;
+    }, "Expecting callbacks to be called.", timeout);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/accelerometer.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/accelerometer.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/accelerometer.tests.js
new file mode 100644
index 0000000..0b61ac3
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/accelerometer.tests.js
@@ -0,0 +1,193 @@
+describe('Accelerometer (navigator.accelerometer)', function () {
+    it("should exist", function () {
+        expect(navigator.accelerometer).toBeDefined();
+    });
+
+    describe("getCurrentAcceleration", function() {
+        it("should exist", function() {
+            expect(typeof navigator.accelerometer.getCurrentAcceleration).toBeDefined();
+            expect(typeof navigator.accelerometer.getCurrentAcceleration == 'function').toBe(true);
+        });
+
+        it("success callback should be called with an Acceleration object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(a.x).toBeDefined();
+                    expect(typeof a.x == 'number').toBe(true);
+                    expect(a.y).toBeDefined();
+                    expect(typeof a.y == 'number').toBe(true);
+                    expect(a.z).toBeDefined();
+                    expect(typeof a.z == 'number').toBe(true);
+                    expect(a.timestamp).toBeDefined();
+                    expect(typeof a.timestamp).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.accelerometer.getCurrentAcceleration(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("success callback Acceleration object should have (reasonable) values for x, y and z expressed in m/s^2", function() {
+            var reasonableThreshold = 15;
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a.x).toBeLessThan(reasonableThreshold);
+                    expect(a.x).toBeGreaterThan(reasonableThreshold * -1);
+                    expect(a.y).toBeLessThan(reasonableThreshold);
+                    expect(a.y).toBeGreaterThan(reasonableThreshold * -1);
+                    expect(a.z).toBeLessThan(reasonableThreshold);
+                    expect(a.z).toBeGreaterThan(reasonableThreshold * -1);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.accelerometer.getCurrentAcceleration(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("success callback Acceleration object should return a recent timestamp", function() {
+            var veryRecently = (new Date()).getTime();
+            // Need to check that dates returned are not vastly greater than a recent time stamp.
+            // In case the timestamps returned are ridiculously high
+            var reasonableTimeLimit = veryRecently + 5000; // 5 seconds from now
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a.timestamp).toBeGreaterThan(veryRecently);
+                    expect(a.timestamp).toBeLessThan(reasonableTimeLimit);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.accelerometer.getCurrentAcceleration(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe("watchAcceleration", function() {
+        var id;
+
+        afterEach(function() {
+            navigator.accelerometer.clearWatch(id);
+        });
+
+        it("should exist", function() {
+            expect(navigator.accelerometer.watchAcceleration).toBeDefined();
+            expect(typeof navigator.accelerometer.watchAcceleration == 'function').toBe(true);
+        });
+        it("success callback should be called with an Acceleration object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(a.x).toBeDefined();
+                    expect(typeof a.x == 'number').toBe(true);
+                    expect(a.y).toBeDefined();
+                    expect(typeof a.y == 'number').toBe(true);
+                    expect(a.z).toBeDefined();
+                    expect(typeof a.z == 'number').toBe(true);
+                    expect(a.timestamp).toBeDefined();
+                    expect(typeof a.timestamp).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                id = navigator.accelerometer.watchAcceleration(win, fail, {frequency:500});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("success callback Acceleration object should have (reasonable) values for x, y and z expressed in m/s^2", function() {
+            var reasonableThreshold = 15;
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a.x).toBeLessThan(reasonableThreshold);
+                    expect(a.x).toBeGreaterThan(reasonableThreshold * -1);
+                    expect(a.y).toBeLessThan(reasonableThreshold);
+                    expect(a.y).toBeGreaterThan(reasonableThreshold * -1);
+                    expect(a.z).toBeLessThan(reasonableThreshold);
+                    expect(a.z).toBeGreaterThan(reasonableThreshold * -1);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                id = navigator.accelerometer.watchAcceleration(win, fail, {frequency:500});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("success callback Acceleration object should return a recent timestamp", function() {
+            var veryRecently = (new Date()).getTime();
+            // Need to check that dates returned are not vastly greater than a recent time stamp.
+            // In case the timestamps returned are ridiculously high
+            var reasonableTimeLimit = veryRecently + 5000; // 5 seconds from now
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a.timestamp).toBeGreaterThan(veryRecently);
+                    expect(a.timestamp).toBeLessThan(reasonableTimeLimit);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                id = navigator.accelerometer.watchAcceleration(win, fail, {frequency:500});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe("clearWatch", function() {
+        it("should exist", function() {
+            expect(navigator.accelerometer.clearWatch).toBeDefined();
+            expect(typeof navigator.accelerometer.clearWatch == 'function').toBe(true);
+        });
+
+        it("should clear an existing watch", function() {
+            var id,
+                win = jasmine.createSpy();
+
+            runs(function() {
+                id = navigator.accelerometer.watchAcceleration(win, function() {}, {frequency:100});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                win.reset();
+                navigator.accelerometer.clearWatch(id);
+            });
+
+            waits(201);
+
+            runs(function() {
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/battery.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/battery.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/battery.tests.js
new file mode 100644
index 0000000..7bb25af
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/battery.tests.js
@@ -0,0 +1,5 @@
+describe('Battery (navigator.battery)', function () {;
+    it("should exist", function() {
+        expect(navigator.battery).toBeDefined();
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/bridge.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/bridge.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/bridge.tests.js
new file mode 100644
index 0000000..ec363d7
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/bridge.tests.js
@@ -0,0 +1,128 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+/* This test requires some extra code to run, because we want benchmark results */
+
+/*
+ It's never going to be OVER 9000
+ http://youtu.be/SiMHTK15Pik 
+*/
+var FENCEPOST = 9000;
+
+var exec = cordova.require('cordova/exec'),
+    echo = cordova.require('cordova/plugin/echo'),
+    startTime,
+    endTime,
+    callCount,
+    durationMs = 1000,
+    asyncEcho,
+    useSetTimeout,
+    payloadSize,
+    payload;
+
+var vanillaWin = function(result) {
+    callCount++;
+    if (result != payload) {
+        console.log('Wrong echo data!');
+    }
+    var elapsedMs = new Date - startTime;
+    if (elapsedMs < durationMs) {
+        if (useSetTimeout) {
+            setTimeout(echoMessage, 0);
+        } else {
+            echoMessage();
+        }
+    } else {
+        endTime = +new Date;
+    }
+}
+
+var reset = function()
+{
+    endTime = null;
+    callCount = 0;
+    useSetTimeout = false;
+    payloadSize = 5;
+    callsPerSecond = 0;
+}
+
+var echoMessage = function()
+{
+    echo(vanillaWin, fail, payload, asyncEcho);
+}
+
+var fail = function() {
+    expect(false).toBe(true);
+};
+
+function createTestCase(jsToNativeModeName, nativeToJsModeName, testAsyncEcho) {
+    it(jsToNativeModeName + '+' + nativeToJsModeName, function() {
+        expect(exec.jsToNativeModes[jsToNativeModeName]).toBeDefined();
+        expect(exec.nativeToJsModes[nativeToJsModeName]).toBeDefined();
+        reset();
+        payload = new Array(payloadSize * 10 + 1).join('012\n\n 6789');
+        asyncEcho = testAsyncEcho;
+        exec.setJsToNativeBridgeMode(exec.jsToNativeModes[jsToNativeModeName]);
+        exec.setNativeToJsBridgeMode(exec.nativeToJsModes[nativeToJsModeName]);
+
+        waits(300);
+        runs(function() {
+            startTime = +new Date,
+            echoMessage();
+        });
+        waitsFor(function() { return endTime; }, "never completed", durationMs * 2);
+        runs(function() {
+            var elapsedMs = endTime - startTime,
+                callsPerSecond = callCount * 1000 / elapsedMs;
+            expect(callsPerSecond).toBeGreaterThan(FENCEPOST);
+        });
+    });
+};
+
+// Wait so that the first benchmark doesn't have contention.
+describe('Wait for page to load.', function() {
+    it('waiting...', function() {
+        waits(1000);
+    });
+});
+
+// Before running on Android, set the following constants in NativeToJsMessagingBridge:
+// - ENABLE_LOCATION_CHANGE_EXEC_MODE = true
+// - DISABLE_EXEC_CHAINING = true
+describe('Android bridge with', function() {
+    var testAsyncEcho = false;
+    createTestCase('PROMPT', 'POLLING', testAsyncEcho);
+    createTestCase('JS_OBJECT', 'POLLING', testAsyncEcho);
+    createTestCase('LOCATION_CHANGE', 'ONLINE_EVENT', testAsyncEcho);
+
+    testAsyncEcho = true;
+    createTestCase('PROMPT', 'POLLING', testAsyncEcho);
+    createTestCase('PROMPT', 'HANGING_GET', testAsyncEcho);
+    createTestCase('PROMPT', 'LOAD_URL', testAsyncEcho);
+    createTestCase('PROMPT', 'ONLINE_EVENT', testAsyncEcho);
+    createTestCase('PROMPT', 'PRIVATE_API', testAsyncEcho);
+
+    createTestCase('JS_OBJECT', 'POLLING', testAsyncEcho);
+    createTestCase('JS_OBJECT', 'HANGING_GET', testAsyncEcho);
+    createTestCase('JS_OBJECT', 'LOAD_URL', testAsyncEcho);
+    createTestCase('JS_OBJECT', 'ONLINE_EVENT', testAsyncEcho);
+    createTestCase('JS_OBJECT', 'PRIVATE_API', testAsyncEcho);
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/camera.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/camera.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/camera.tests.js
new file mode 100644
index 0000000..9b6b04c
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/camera.tests.js
@@ -0,0 +1,47 @@
+describe('Camera (navigator.camera)', function () {
+	it("should exist", function() {
+        expect(navigator.camera).toBeDefined();
+	});
+
+	it("should contain a getPicture function", function() {
+        expect(navigator.camera.getPicture).toBeDefined();
+		expect(typeof navigator.camera.getPicture == 'function').toBe(true);
+	});
+});
+
+describe('Camera Constants (window.Camera + navigator.camera)', function () {
+    it("window.Camera should exist", function() {
+        expect(window.Camera).toBeDefined();
+    });
+
+    it("should contain two DestinationType constants", function() {
+        expect(Camera.DestinationType.DATA_URL).toBe(0);
+        expect(Camera.DestinationType.FILE_URI).toBe(1);
+        expect(navigator.camera.DestinationType.DATA_URL).toBe(0);
+        expect(navigator.camera.DestinationType.FILE_URI).toBe(1);
+    });
+
+    it("should contain two EncodingType constants", function() {
+        expect(Camera.EncodingType.JPEG).toBe(0);
+        expect(Camera.EncodingType.PNG).toBe(1);
+        expect(navigator.camera.EncodingType.JPEG).toBe(0);
+        expect(navigator.camera.EncodingType.PNG).toBe(1);
+    });
+
+    it("should contain three MediaType constants", function() {
+        expect(Camera.MediaType.PICTURE).toBe(0);
+        expect(Camera.MediaType.VIDEO).toBe(1);
+        expect(Camera.MediaType.ALLMEDIA).toBe(2);
+        expect(navigator.camera.MediaType.PICTURE).toBe(0);
+        expect(navigator.camera.MediaType.VIDEO).toBe(1);
+        expect(navigator.camera.MediaType.ALLMEDIA).toBe(2);
+    });
+    it("should contain three PictureSourceType constants", function() {
+        expect(Camera.PictureSourceType.PHOTOLIBRARY).toBe(0);
+        expect(Camera.PictureSourceType.CAMERA).toBe(1);
+        expect(Camera.PictureSourceType.SAVEDPHOTOALBUM).toBe(2);
+        expect(navigator.camera.PictureSourceType.PHOTOLIBRARY).toBe(0);
+        expect(navigator.camera.PictureSourceType.CAMERA).toBe(1);
+        expect(navigator.camera.PictureSourceType.SAVEDPHOTOALBUM).toBe(2);
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/capture.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/capture.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/capture.tests.js
new file mode 100644
index 0000000..bffced8
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/capture.tests.js
@@ -0,0 +1,95 @@
+describe('Capture (navigator.device.capture)', function () {
+    it("should exist", function() {
+        expect(navigator.device).toBeDefined();
+        expect(navigator.device.capture).toBeDefined();
+    });
+
+    it("should have the correct properties ", function() {
+        expect(navigator.device.capture.supportedAudioModes).toBeDefined();
+        expect(navigator.device.capture.supportedImageModes).toBeDefined();
+        expect(navigator.device.capture.supportedVideoModes).toBeDefined();
+    });
+
+    it("should contain a captureAudio function", function() {
+        expect(navigator.device.capture.captureAudio).toBeDefined();
+        expect(typeof navigator.device.capture.captureAudio == 'function').toBe(true);
+    });
+
+    it("should contain a captureImage function", function() {
+        expect(navigator.device.capture.captureImage).toBeDefined();
+        expect(typeof navigator.device.capture.captureImage == 'function').toBe(true);
+    });
+
+    it("should contain a captureVideo function", function() {
+        expect(navigator.device.capture.captureVideo).toBeDefined();
+        expect(typeof navigator.device.capture.captureVideo == 'function').toBe(true);
+    });
+
+    describe('CaptureAudioOptions', function () {
+        it("CaptureAudioOptions constructor should exist", function() {
+            var options = new CaptureAudioOptions();
+            expect(options).toBeDefined();
+            expect(options.limit).toBeDefined();
+            expect(options.duration).toBeDefined();
+            expect(options.mode).toBeDefined();
+        });
+    });
+
+    describe('CaptureImageOptions', function () {
+        it("CaptureImageOptions constructor should exist", function() {
+            var options = new CaptureImageOptions();
+            expect(options).toBeDefined();
+            expect(options.limit).toBeDefined();
+            expect(options.mode).toBeDefined();
+        });
+    });
+
+    describe('CaptureVideoOptions', function () {
+        it("CaptureVideoOptions constructor should exist", function() {
+            var options = new CaptureVideoOptions();
+            expect(options).toBeDefined();
+            expect(options.limit).toBeDefined();
+            expect(options.duration).toBeDefined();
+            expect(options.mode).toBeDefined();
+        });
+    });
+
+    describe('CaptureError interface', function () {
+        it("CaptureError constants should be defined", function() {
+            expect(CaptureError.CAPTURE_INTERNAL_ERR).toBe(0);
+            expect(CaptureError.CAPTURE_APPLICATION_BUSY).toBe(1);
+            expect(CaptureError.CAPTURE_INVALID_ARGUMENT).toBe(2);
+            expect(CaptureError.CAPTURE_NO_MEDIA_FILES).toBe(3);
+        });
+
+        it("CaptureError properties should exist", function() {
+            var error = new CaptureError();
+            expect(error).toBeDefined();
+            expect(error.code).toBeDefined();
+        });
+    });
+
+    describe('MediaFileData', function () {
+        it("MediaFileData constructor should exist", function() {
+            var fileData = new MediaFileData();
+            expect(fileData).toBeDefined();
+            expect(fileData.bitrate).toBeDefined();
+            expect(fileData.codecs).toBeDefined();
+            expect(fileData.duration).toBeDefined();
+            expect(fileData.height).toBeDefined();
+            expect(fileData.width).toBeDefined();
+        });
+    });
+
+    describe('MediaFile', function () {
+        it("MediaFile constructor should exist", function() {
+            var fileData = new MediaFile();
+            expect(fileData).toBeDefined();
+            expect(fileData.name).toBeDefined();
+            expect(fileData.fullPath).toBeDefined();
+            expect(fileData.type).toBeDefined();
+            expect(fileData.lastModifiedDate).toBeDefined();
+            expect(fileData.size).toBeDefined();
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/compass.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/compass.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/compass.tests.js
new file mode 100644
index 0000000..a16ec0e
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/compass.tests.js
@@ -0,0 +1,76 @@
+describe('Compass (navigator.compass)', function () {
+    it("should exist", function() {
+        expect(navigator.compass).toBeDefined();
+    });
+
+    it("should contain a getCurrentHeading function", function() {
+        expect(navigator.compass.getCurrentHeading).toBeDefined();
+		expect(typeof navigator.compass.getCurrentHeading == 'function').toBe(true);
+	});
+
+    it("getCurrentHeading success callback should be called with a Heading object", function() {
+        var win = jasmine.createSpy().andCallFake(function(a) {
+                expect(a instanceof CompassHeading).toBe(true);
+                expect(a.magneticHeading).toBeDefined();
+                expect(typeof a.magneticHeading == 'number').toBe(true);
+                expect(a.trueHeading).not.toBe(undefined);
+                expect(typeof a.trueHeading == 'number' || a.trueHeading === null).toBe(true);
+                expect(a.headingAccuracy).not.toBe(undefined);
+                expect(typeof a.headingAccuracy == 'number' || a.headingAccuracy === null).toBe(true);
+                expect(typeof a.timestamp == 'number').toBe(true);
+            }),
+            fail = jasmine.createSpy();
+
+        runs(function () {
+            navigator.compass.getCurrentHeading(win, fail);
+        });
+
+        waitsFor(function () { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+        runs(function () {
+            expect(fail).not.toHaveBeenCalled();
+            expect(win).toHaveBeenCalled();
+        });
+	});
+
+    it("should contain a watchHeading function", function() {
+        expect(navigator.compass.watchHeading).toBeDefined();
+        expect(typeof navigator.compass.watchHeading == 'function').toBe(true);
+    });
+
+    it("should contain a clearWatch function", function() {
+        expect(navigator.compass.clearWatch).toBeDefined();
+        expect(typeof navigator.compass.clearWatch == 'function').toBe(true);
+    });
+
+    describe('Compass Constants (window.CompassError)', function () {
+        it("should exist", function() {
+            expect(window.CompassError).toBeDefined();
+            expect(window.CompassError.COMPASS_INTERNAL_ERR).toBe(0);
+            expect(window.CompassError.COMPASS_NOT_SUPPORTED).toBe(20);
+        });
+    });
+
+    describe('Compass Heading model (CompassHeading)', function () {
+        it("should exist", function() {
+            expect(CompassHeading).toBeDefined();
+        });
+
+        it("should be able to create a new CompassHeading instance with no parameters", function() {
+            var h = new CompassHeading();
+            expect(h.magneticHeading).toBeDefined();
+            expect(h.trueHeading).toBeDefined();
+            expect(h.headingAccuracy).toBeDefined();
+            expect(typeof h.timestamp == 'number').toBe(true);
+        });
+
+        it("should be able to creat a new CompassHeading instance with parameters", function() {
+            var h = new CompassHeading(1,2,3,4);
+            expect(h.magneticHeading).toBe(1);
+            expect(h.trueHeading).toBe(2);
+            expect(h.headingAccuracy).toBe(3);
+            expect(h.timestamp.valueOf()).toBe(4);
+            expect(typeof h.timestamp == 'number').toBe(true);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/contacts.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/contacts.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/contacts.tests.js
new file mode 100644
index 0000000..a5f41a5
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/contacts.tests.js
@@ -0,0 +1,451 @@
+// global to store a contact so it doesn't have to be created or retrieved multiple times
+// all of the setup/teardown test methods can reference the following variables to make sure to do the right cleanup
+var gContactObj = null;
+var gContactId = null;
+
+var removeContact = function(){
+    if (gContactObj) {
+        gContactObj.remove(function(){},function(){
+            console.log("[CONTACTS ERROR]: removeContact cleanup method failed to clean up test artifacts.");
+        });
+        gContactObj = null;
+    }
+};
+
+describe("Contacts (navigator.contacts)", function () {
+    it("should exist", function() {
+        expect(navigator.contacts).toBeDefined();
+    });
+
+    it("should contain a find function", function() {
+        expect(navigator.contacts.find).toBeDefined();
+        expect(typeof navigator.contacts.find).toBe('function');
+    });
+
+    describe("find method", function() {
+        it("success callback should be called with an array", function() {
+            var win = jasmine.createSpy().andCallFake(function(result) {
+                    expect(result).toBeDefined();
+                    expect(result instanceof Array).toBe(true);
+                }),
+                fail = jasmine.createSpy(),
+                obj = new ContactFindOptions();
+
+            runs(function () {
+                obj.filter="";
+                obj.multiple=true;
+                navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], win, fail, obj);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("should throw an exception if success callback is empty", function() {
+            var fail = function() {};
+            var obj = new ContactFindOptions();
+            obj.filter="";
+            obj.multiple=true;
+
+            expect(function () {
+                navigator.contacts.find(["displayName", "name", "emails", "phoneNumbers"], null, fail, obj);
+            }).toThrow();
+        });
+
+        it("error callback should be called when no fields are specified", function() {
+            var win = jasmine.createSpy(),
+                fail = jasmine.createSpy(function(result) {
+                    expect(result).toBeDefined();
+                    expect(result.code).toBe(ContactError.INVALID_ARGUMENT_ERROR);
+                }),
+                obj = new ContactFindOptions();
+
+            runs(function () {
+                obj.filter="";
+                obj.multiple=true;
+                navigator.contacts.find([], win, fail, obj);
+            });
+
+            waitsFor(function () { return fail.wasCalled; }, Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(win).not.toHaveBeenCalled();
+                expect(fail).toHaveBeenCalled();
+            });
+        });
+
+        describe("with newly-created contact", function () {
+
+            afterEach(removeContact);
+
+            it("should be able to find a contact by name", function() {
+                var foundName = jasmine.createSpy().andCallFake(function(result) {
+                        var bFound = false;
+                        try {
+                            for (var i=0; i < result.length; i++) {
+                                if (result[i].name.familyName == "Delete") {
+                                    bFound = true;
+                                    break;
+                                }
+                            }
+                        } catch(e) {
+                            return false;
+                        }
+                        return bFound;
+                    }),
+                    fail = jasmine.createSpy(),
+                    test = jasmine.createSpy().andCallFake(function(savedContact) {
+                        console.log('in test');
+                        // update so contact will get removed
+                        gContactObj = savedContact;
+                        // ----
+                        // Find asserts
+                        // ---
+                        var findWin = jasmine.createSpy().andCallFake(function(object) {
+                                console.log('in findwin');
+                                expect(object instanceof Array).toBe(true);
+                                expect(object.length >= 1).toBe(true);
+                                expect(foundName(object)).toBe(true);
+                            }),
+                            findFail = jasmine.createSpy(),
+                            obj = new ContactFindOptions();
+
+                        obj.filter="Delete";
+                        obj.multiple=true;
+
+                        runs(function () {
+                            navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWin, findFail, obj);
+                        });
+
+                        waitsFor(function () { return foundName.wasCalled; }, "foundName not done", Tests.TEST_TIMEOUT);
+
+                        runs(function () {
+                            expect(findFail).not.toHaveBeenCalled();
+                            expect(fail).not.toHaveBeenCalled();
+                        });
+                    });
+
+                runs(function () {
+                    gContactObj = new Contact();
+                    gContactObj.name = new ContactName();
+                    gContactObj.name.familyName = "Delete";
+                    gContactObj.save(test, fail);
+                });
+
+                waitsFor(function () { return test.wasCalled; }, "test not done", Tests.TEST_TIMEOUT);
+            });
+        });
+    });
+
+    describe('create method', function() {
+
+        it("should exist", function() {
+            expect(navigator.contacts.create).toBeDefined();
+            expect(typeof navigator.contacts.create).toBe('function');
+        });
+
+        it("should return a Contact object", function() {
+            var bDay = new Date(1976, 7,4);
+            var obj = navigator.contacts.create({"displayName": "test name", "gender": "male", "note": "my note", "name": {"formatted": "Mr. Test Name"}, "emails": [{"value": "here@there.com"}, {"value": "there@here.com"}], "birthday": bDay});
+
+            expect(obj).toBeDefined();
+            expect(obj.displayName).toBe('test name');
+            expect(obj.note).toBe('my note');
+            expect(obj.name.formatted).toBe('Mr. Test Name');
+            expect(obj.emails.length).toBe(2);
+            expect(obj.emails[0].value).toBe('here@there.com');
+            expect(obj.emails[1].value).toBe('there@here.com');
+            expect(obj.nickname).toBe(null);
+            expect(obj.birthday).toBe(bDay);
+        });
+    });
+
+    describe("Contact object", function () {
+        it("should be able to create instance", function() {
+            var contact = new Contact("a", "b", new ContactName("a", "b", "c", "d", "e", "f"), "c", [], [], [], [], [], "f", "i",
+                [], [], []);
+            expect(contact).toBeDefined();
+            expect(contact.id).toBe("a");
+            expect(contact.displayName).toBe("b");
+            expect(contact.name.formatted).toBe("a");
+            expect(contact.nickname).toBe("c");
+            expect(contact.phoneNumbers).toBeDefined();
+            expect(contact.emails).toBeDefined();
+            expect(contact.addresses).toBeDefined();
+            expect(contact.ims).toBeDefined();
+            expect(contact.organizations).toBeDefined();
+            expect(contact.birthday).toBe("f");
+            expect(contact.note).toBe("i");
+            expect(contact.photos).toBeDefined();
+            expect(contact.categories).toBeDefined();
+            expect(contact.urls).toBeDefined();
+        });
+
+        it("should be able to define a ContactName object", function() {
+            var contactName = new ContactName("Dr. First Last Jr.", "Last", "First", "Middle", "Dr.", "Jr.");
+            expect(contactName).toBeDefined();
+            expect(contactName.formatted).toBe("Dr. First Last Jr.");
+            expect(contactName.familyName).toBe("Last");
+            expect(contactName.givenName).toBe("First");
+            expect(contactName.middleName).toBe("Middle");
+            expect(contactName.honorificPrefix).toBe("Dr.");
+            expect(contactName.honorificSuffix).toBe("Jr.");
+        });
+
+        it("should be able to define a ContactField object", function() {
+            var contactField = new ContactField("home", "8005551212", true);
+            expect(contactField).toBeDefined();
+            expect(contactField.type).toBe("home");
+            expect(contactField.value).toBe("8005551212");
+            expect(contactField.pref).toBe(true);
+        });
+
+        it("ContactField object should coerce type and value properties to strings", function() {
+            var contactField = new ContactField(12345678, 12345678, true);
+            expect(contactField.type).toBe("12345678");
+            expect(contactField.value).toBe("12345678");
+        });
+
+        it("should be able to define a ContactAddress object", function() {
+            var contactAddress = new ContactAddress(true, "home", "a","b","c","d","e","f");
+            expect(contactAddress).toBeDefined();
+            expect(contactAddress.pref).toBe(true);
+            expect(contactAddress.type).toBe("home");
+            expect(contactAddress.formatted).toBe("a");
+            expect(contactAddress.streetAddress).toBe("b");
+            expect(contactAddress.locality).toBe("c");
+            expect(contactAddress.region).toBe("d");
+            expect(contactAddress.postalCode).toBe("e");
+            expect(contactAddress.country).toBe("f");
+        });
+
+        it("should be able to define a ContactOrganization object", function() {
+            var contactOrg = new ContactOrganization(true, "home", "a","b","c","d","e","f","g");
+            expect(contactOrg).toBeDefined();
+            expect(contactOrg.pref).toBe(true);
+            expect(contactOrg.type).toBe("home");
+            expect(contactOrg.name).toBe("a");
+            expect(contactOrg.department).toBe("b");
+            expect(contactOrg.title).toBe("c");
+        });
+
+        it("should be able to define a ContactFindOptions object", function() {
+            var contactFindOptions = new ContactFindOptions("a", true, "b");
+            expect(contactFindOptions).toBeDefined();
+            expect(contactFindOptions.filter).toBe("a");
+            expect(contactFindOptions.multiple).toBe(true);
+        });
+
+        it("should contain a clone function", function() {
+            var contact = new Contact();
+            expect(contact.clone).toBeDefined();
+            expect(typeof contact.clone).toBe('function');
+        });
+
+        it("clone function should make deep copy of Contact Object", function() {
+            var contact = new Contact();
+            contact.id=1;
+            contact.displayName="Test Name";
+            contact.nickname="Testy";
+            contact.gender="male";
+            contact.note="note to be cloned";
+            contact.name = new ContactName("Mr. Test Name");
+
+            var clonedContact = contact.clone();
+
+            expect(contact.id).toBe(1);
+            expect(clonedContact.id).toBe(null);
+            expect(clonedContact.displayName).toBe(contact.displayName);
+            expect(clonedContact.nickname).toBe(contact.nickname);
+            expect(clonedContact.gender).toBe(contact.gender);
+            expect(clonedContact.note).toBe(contact.note);
+            expect(clonedContact.name.formatted).toBe(contact.name.formatted);
+            expect(clonedContact.connected).toBe(contact.connected);
+        });
+
+        it("should contain a save function", function() {
+            var contact = new Contact();
+            expect(contact.save).toBeDefined();
+            expect(typeof contact.save).toBe('function');
+        });
+
+        it("should contain a remove function", function() {
+            var contact = new Contact();
+            expect(contact.remove).toBeDefined();
+            expect(typeof contact.remove).toBe('function');
+        });
+    });
+
+    describe('save method', function () {
+        it("should be able to save a contact", function() {
+            var bDay = new Date(1976, 6,4);
+            gContactObj = navigator.contacts.create({"gender": "male", "note": "my note", "name": {"familyName": "Delete", "givenName": "Test"}, "emails": [{"value": "here@there.com"}, {"value": "there@here.com"}], "birthday": bDay});
+
+            var saveSuccess = jasmine.createSpy().andCallFake(function(obj) {
+                    expect(obj).toBeDefined();
+                    expect(obj.note).toBe('my note');
+                    expect(obj.name.familyName).toBe('Delete');
+                    expect(obj.name.givenName).toBe('Test');
+                    expect(obj.emails.length).toBe(2);
+                    expect(obj.emails[0].value).toBe('here@there.com');
+                    expect(obj.emails[1].value).toBe('there@here.com');
+                    expect(obj.birthday.toDateString()).toBe(bDay.toDateString());
+                    expect(obj.addresses).toBe(null);
+                    // must store returned object in order to have id for update test below
+                    gContactObj = obj;
+                }),
+                saveFail = jasmine.createSpy();
+
+            runs(function () {
+                gContactObj.save(saveSuccess, saveFail);
+            });
+
+            waitsFor(function () { return saveSuccess.wasCalled; }, "saveSuccess never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(saveFail).not.toHaveBeenCalled();
+            });
+         });
+        // HACK: there is a reliance between the previous and next test. This is bad form.
+        it("update a contact", function() {
+            expect(gContactObj).toBeDefined();
+
+            var bDay = new Date(1975, 5,4);
+            var noteText = "an UPDATED note";
+
+            var win = jasmine.createSpy().andCallFake(function(obj) {
+                    expect(obj).toBeDefined();
+                    expect(obj.id).toBe(gContactObj.id);
+                    expect(obj.note).toBe(noteText);
+                    expect(obj.birthday.toDateString()).toBe(bDay.toDateString());
+                    expect(obj.emails.length).toBe(1);
+                    expect(obj.emails[0].value).toBe('here@there.com');
+                    removeContact();         // Clean up contact object
+                }), fail = jasmine.createSpy().andCallFake(removeContact);
+
+            runs(function () {
+                // remove an email
+                gContactObj.emails[1].value = "";
+                // change birthday
+                gContactObj.birthday = bDay;
+                // update note
+                gContactObj.note = noteText;
+                gContactObj.save(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "saveSuccess never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('Contact.remove method', function () {
+        afterEach(removeContact);
+
+        it("calling remove on a contact has an id of null should return ContactError.UNKNOWN_ERROR", function() {
+            var win = jasmine.createSpy();
+            var fail = jasmine.createSpy().andCallFake(function(result) {
+                expect(result.code).toBe(ContactError.UNKNOWN_ERROR);
+            });
+
+            runs(function () {
+                var rmContact = new Contact();
+                rmContact.remove(win, fail);
+            });
+
+            waitsFor(function () { return fail.wasCalled; }, Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+
+        it("calling remove on a contact that does not exist should return ContactError.UNKNOWN_ERROR", function() {
+            var win = jasmine.createSpy();
+            var fail = jasmine.createSpy().andCallFake(function(result) {
+                expect(result.code).toBe(ContactError.UNKNOWN_ERROR);
+            });
+
+            runs(function () {
+                var rmContact = new Contact();
+                // this is a bit risky as some devices may have contact ids that large
+                var contact = new Contact("this string is supposed to be a unique identifier that will never show up on a device");
+                contact.remove(win, fail);
+            });
+
+            waitsFor(function () { return fail.wasCalled; }, Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe("Round trip Contact tests (creating + save + delete + find).", function () {
+        afterEach(removeContact);
+
+        it("Creating, saving, finding a contact should work, removing it should work, after which we should not be able to find it, and we should not be able to delete it again.", function() {
+            var done = false;
+            runs(function () {
+                gContactObj = new Contact();
+                gContactObj.name = new ContactName();
+                gContactObj.name.familyName = "DeleteMe";
+                gContactObj.save(function(c_obj) {
+                    var findWin = function(cs) {
+                        expect(cs.length).toBe(1);
+                        // update to have proper saved id
+                        gContactObj = cs[0];
+                        gContactObj.remove(function() {
+                            var findWinAgain = function(seas) {
+                                expect(seas.length).toBe(0);
+                                gContactObj.remove(function() {
+                                    throw("success callback called after non-existent Contact object called remove(). Test failed.");
+                                }, function(e) {
+                                    expect(e.code).toBe(ContactError.UNKNOWN_ERROR);
+                                    done = true;
+                                });
+                            };
+                            var findFailAgain = function(e) {
+                                throw("find error callback invoked after delete, test failed.");
+                            };
+                            var obj = new ContactFindOptions();
+                            obj.filter="DeleteMe";
+                            obj.multiple=true;
+                            navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWinAgain, findFailAgain, obj);
+                        }, function(e) {
+                            throw("Newly created contact's remove function invoked error callback. Test failed.");
+                        });
+                    };
+                    var findFail = function(e) {
+                        throw("Failure callback invoked in navigator.contacts.find call, test failed.");
+                    };
+                    var obj = new ContactFindOptions();
+                    obj.filter="DeleteMe";
+                    obj.multiple=true;
+                    navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWin, findFail, obj);
+                }, function(e) {
+                    throw("Contact creation failed, error callback was invoked.");
+                });
+            });
+
+            waitsFor(function () { return done; }, Tests.TEST_TIMEOUT);
+        });
+    });
+
+    describe('ContactError interface', function () {
+        it("ContactError constants should be defined", function() {
+            expect(ContactError.UNKNOWN_ERROR).toBe(0);
+            expect(ContactError.INVALID_ARGUMENT_ERROR).toBe(1);
+            expect(ContactError.TIMEOUT_ERROR).toBe(2);
+            expect(ContactError.PENDING_OPERATION_ERROR).toBe(3);
+            expect(ContactError.IO_ERROR).toBe(4);
+            expect(ContactError.NOT_SUPPORTED_ERROR).toBe(5);
+            expect(ContactError.PERMISSION_DENIED_ERROR).toBe(20);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/datauri.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/datauri.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/datauri.tests.js
new file mode 100644
index 0000000..2e54810
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/datauri.tests.js
@@ -0,0 +1,36 @@
+describe('data uris', function () {
+    it("should work with iframes", function() {
+        var gotFoo = false,
+            frame = document.createElement('iframe');
+        function onMessage(msg) {
+            gotFoo = gotFoo || msg.data == 'foo';
+        };
+
+        this.after(function() {
+            document.body.removeChild(frame);
+            window.removeEventListener('message', onMessage, false);
+        });
+
+        window.addEventListener('message', onMessage, false);
+        frame.src = 'data:text/html;charset=utf-8,%3Chtml%3E%3Cscript%3Eparent.postMessage%28%27foo%27%2C%27%2A%27%29%3C%2Fscript%3E%3C%2Fhtml%3E'
+        document.body.appendChild(frame);
+        waitsFor(function() {
+            return gotFoo;
+        }, 'iframe did not load.', 1000);
+        runs(function() {
+            expect(gotFoo).toBe(true);
+        });
+    });
+    it("should work with images", function() {
+        var img = new Image();
+        img.onload = jasmine.createSpy('onLoad');
+        img.onerror = jasmine.createSpy('onError');
+        img.src = 'data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7'
+        waitsFor(function() {
+            return img.onload.wasCalled || img.onerror.wasCalled;
+        }, 'image did not load or error', 1000);
+        runs(function() {
+            expect(img.onload).toHaveBeenCalled();
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/device.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/device.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/device.tests.js
new file mode 100644
index 0000000..cc322d6
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/device.tests.js
@@ -0,0 +1,34 @@
+describe('Device Information (window.device)', function () {
+	it("should exist", function() {
+        expect(window.device).toBeDefined();
+	});
+
+	it("should contain a platform specification that is a string", function() {
+        expect(window.device.platform).toBeDefined();
+		expect((new String(window.device.platform)).length > 0).toBe(true);
+	});
+
+	it("should contain a version specification that is a string", function() {
+        expect(window.device.version).toBeDefined();
+		expect((new String(window.device.version)).length > 0).toBe(true);
+	});
+
+	it("should contain a name specification that is a string", function() {
+        expect(window.device.name).toBeDefined();
+		expect((new String(window.device.name)).length > 0).toBe(true);
+	});
+
+	it("should contain a UUID specification that is a string or a number", function() {
+        expect(window.device.uuid).toBeDefined();
+		if (typeof window.device.uuid == 'string' || typeof window.device.uuid == 'object') {
+		    expect((new String(window.device.uuid)).length > 0).toBe(true);
+		} else {
+			expect(window.device.uuid > 0).toBe(true);
+		}
+	});
+
+	it("should contain a cordova specification that is a string", function() {
+        expect(window.device.cordova).toBeDefined();
+		expect((new String(window.device.cordova)).length > 0).toBe(true);
+	});
+});


[34/50] git commit: Add WP7 and WP8 platform files.

Posted by br...@apache.org.
Add WP7 and WP8 platform files.


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

Branch: refs/heads/master2
Commit: 0fea2dd2264ada7f6811a238b71426b5f081af2c
Parents: 693521d
Author: Benn Mapes <be...@gmail.com>
Authored: Tue May 14 15:23:30 2013 -0700
Committer: Ian Clelland <ic...@chromium.org>
Committed: Thu May 23 16:04:02 2013 -0400

----------------------------------------------------------------------
 .gitignore                                         |    2 +
 lib/cordova-wp7/LICENSE                            |   12 +
 lib/cordova-wp7/NOTICE                             |    5 +
 lib/cordova-wp7/README.md                          |   50 +
 lib/cordova-wp7/VERSION                            |    1 +
 lib/cordova-wp7/bin/create.js                      |  252 +
 .../framework/Images/appbar.back.rest.png          |  Bin 0 -> 375 bytes
 .../framework/Images/appbar.close.rest.png         |  Bin 0 -> 359 bytes
 .../framework/Images/appbar.feature.video.rest.png |  Bin 0 -> 433 bytes
 .../framework/Images/appbar.next.rest.png          |  Bin 0 -> 388 bytes
 .../framework/Images/appbar.save.rest.png          |  Bin 0 -> 297 bytes
 .../framework/Images/appbar.stop.rest.png          |  Bin 0 -> 350 bytes
 .../framework/Properties/AssemblyInfo.cs           |   37 +
 lib/cordova-wp7/framework/WPCordovaClassLib.csproj |  297 +
 lib/cordova-wp7/framework/WPCordovaClassLib.sln    |   20 +
 .../framework/resources/notification-beep.wav      |  Bin 0 -> 16630 bytes
 lib/cordova-wp7/templates/standalone/App.xaml      |   37 +
 lib/cordova-wp7/templates/standalone/App.xaml.cs   |  154 +
 .../templates/standalone/ApplicationIcon.png       |  Bin 0 -> 4951 bytes
 .../templates/standalone/Background.png            |  Bin 0 -> 10259 bytes
 .../templates/standalone/BuildManifestProcessor.js |  106 +
 .../templates/standalone/CordovaAppProj.csproj     |  197 +
 .../templates/standalone/CordovaSolution.sln       |   22 +
 .../standalone/CordovaSourceDictionary.xml         |    9 +
 .../standalone/Images/appbar.back.rest.png         |  Bin 0 -> 375 bytes
 .../standalone/Images/appbar.close.rest.png        |  Bin 0 -> 359 bytes
 .../Images/appbar.feature.video.rest.png           |  Bin 0 -> 433 bytes
 .../standalone/Images/appbar.next.rest.png         |  Bin 0 -> 388 bytes
 .../standalone/Images/appbar.save.rest.png         |  Bin 0 -> 297 bytes
 .../standalone/Images/appbar.stop.rest.png         |  Bin 0 -> 350 bytes
 lib/cordova-wp7/templates/standalone/MainPage.xaml |   35 +
 .../templates/standalone/MainPage.xaml.cs          |   72 +
 .../templates/standalone/Plugins/Accelerometer.cs  |  196 +
 .../standalone/Plugins/AudioFormatsHelper.cs       |   89 +
 .../templates/standalone/Plugins/AudioPlayer.cs    |  620 ++
 .../templates/standalone/Plugins/Battery.cs        |   79 +
 .../templates/standalone/Plugins/Camera.cs         |  490 ++
 .../templates/standalone/Plugins/Capture.cs        |  736 ++
 .../templates/standalone/Plugins/Compass.cs        |  362 +
 .../templates/standalone/Plugins/Contacts.cs       |  664 ++
 .../templates/standalone/Plugins/DebugConsole.cs   |   49 +
 .../templates/standalone/Plugins/Device.cs         |  135 +
 .../templates/standalone/Plugins/File.cs           | 1676 ++++
 .../templates/standalone/Plugins/FileTransfer.cs   |  526 ++
 .../templates/standalone/Plugins/GeoLocation.cs    |   34 +
 .../templates/standalone/Plugins/Globalization.cs  | 1178 +++
 .../standalone/Plugins/ImageExifHelper.cs          |  209 +
 .../templates/standalone/Plugins/InAppBrowser.cs   |  268 +
 .../templates/standalone/Plugins/Media.cs          |  532 ++
 .../templates/standalone/Plugins/MimeTypeMapper.cs |   99 +
 .../templates/standalone/Plugins/NetworkStatus.cs  |  129 +
 .../templates/standalone/Plugins/Notification.cs   |  367 +
 .../standalone/Plugins/UI/AudioCaptureTask.cs      |  107 +
 .../standalone/Plugins/UI/AudioRecorder.xaml       |   66 +
 .../standalone/Plugins/UI/AudioRecorder.xaml.cs    |  306 +
 .../standalone/Plugins/UI/ImageCapture.xaml        |   26 +
 .../standalone/Plugins/UI/ImageCapture.xaml.cs     |  109 +
 .../standalone/Plugins/UI/NotificationBox.xaml     |   62 +
 .../standalone/Plugins/UI/NotificationBox.xaml.cs  |   41 +
 .../standalone/Plugins/UI/VideoCaptureTask.cs      |  105 +
 .../standalone/Plugins/UI/VideoRecorder.xaml       |   52 +
 .../standalone/Plugins/UI/VideoRecorder.xaml.cs    |  405 +
 .../standalone/Properties/AppManifest.xml          |    6 +
 .../standalone/Properties/AssemblyInfo.cs          |   38 +
 .../standalone/Properties/WMAppManifest.xml        |   41 +
 lib/cordova-wp7/templates/standalone/README.md     |   13 +
 .../templates/standalone/SplashScreenImage.jpg     |  Bin 0 -> 33248 bytes
 lib/cordova-wp7/templates/standalone/VERSION       |    1 +
 lib/cordova-wp7/templates/standalone/config.xml    |   49 +
 .../templates/standalone/cordova/build.bat         |    9 +
 .../templates/standalone/cordova/clean.bat         |    9 +
 .../cordova/lib/CordovaDeploy/CordovaDeploy.sln    |   20 +
 .../CordovaDeploy/CordovaDeploy.csproj             |   79 +
 .../lib/CordovaDeploy/CordovaDeploy/Program.cs     |  235 +
 .../CordovaDeploy/Properties/AssemblyInfo.cs       |   36 +
 .../templates/standalone/cordova/lib/build.js      |  181 +
 .../templates/standalone/cordova/lib/clean.js      |  124 +
 .../templates/standalone/cordova/lib/deploy.js     |  326 +
 .../standalone/cordova/lib/install-device.bat      |    9 +
 .../standalone/cordova/lib/install-emulator.bat    |    9 +
 .../standalone/cordova/lib/list-devices.bat        |    9 +
 .../cordova/lib/list-emulator-images.bat           |    9 +
 .../cordova/lib/list-started-emulators.bat         |    3 +
 .../templates/standalone/cordova/lib/log.js        |   77 +
 .../standalone/cordova/lib/start-emulator.bat      |    3 +
 .../standalone/cordova/lib/target-list.js          |  233 +
 .../templates/standalone/cordova/log.bat           |    3 +
 .../templates/standalone/cordova/run.bat           |    9 +
 .../standalone/cordovalib/BrowserMouseHelper.cs    |  345 +
 .../standalone/cordovalib/CommandFactory.cs        |  112 +
 .../standalone/cordovalib/Commands/BaseCommand.cs  |  187 +
 .../standalone/cordovalib/ConfigHandler.cs         |  249 +
 .../standalone/cordovalib/CordovaCommandCall.cs    |   98 +
 .../standalone/cordovalib/CordovaView.xaml         |   65 +
 .../standalone/cordovalib/CordovaView.xaml.cs      |  485 ++
 .../standalone/cordovalib/DOMStorageHelper.cs      |  145 +
 .../standalone/cordovalib/JSON/JsonHelper.cs       |   97 +
 .../standalone/cordovalib/NativeExecution.cs       |  246 +
 .../standalone/cordovalib/OrientationHelper.cs     |  128 +
 .../standalone/cordovalib/PluginResult.cs          |  139 +
 .../standalone/cordovalib/ScriptCallback.cs        |   80 +
 .../cordovalib/resources/notification-beep.wav     |  Bin 0 -> 16630 bytes
 .../standalone/resources/notification-beep.wav     |  Bin 0 -> 16630 bytes
 .../templates/standalone/www/cordova-2.7.0.js      | 6700 +++++++++++++++
 .../templates/standalone/www/css/index.css         |  115 +
 .../templates/standalone/www/img/logo.png          |  Bin 0 -> 21814 bytes
 .../templates/standalone/www/index.html            |   42 +
 .../templates/standalone/www/js/index.js           |   49 +
 .../templates/vs/MyTemplateStandAlone.vstemplate   |  115 +
 lib/cordova-wp7/templates/vs/description.txt       |    4 +
 lib/cordova-wp7/templates/vs/pg_templateIcon.png   |  Bin 0 -> 6546 bytes
 .../templates/vs/pg_templatePreview.jpg            |  Bin 0 -> 25875 bytes
 lib/cordova-wp7/tests/README.md                    |    7 +
 lib/cordova-wp7/tooling/scripts/buildjs.bat        |    2 +
 lib/cordova-wp7/tooling/scripts/buildjs.js         |  209 +
 lib/cordova-wp7/tooling/scripts/dist.bat           |    2 +
 lib/cordova-wp7/tooling/scripts/dist.js            |  217 +
 lib/cordova-wp7/tooling/scripts/new.bat            |    2 +
 lib/cordova-wp7/tooling/scripts/new.js             |  151 +
 lib/cordova-wp7/tooling/scripts/package.bat        |    2 +
 lib/cordova-wp7/tooling/scripts/package.js         |  213 +
 lib/cordova-wp7/tooling/scripts/reversion.bat      |    2 +
 lib/cordova-wp7/tooling/scripts/reversion.js       |  277 +
 lib/cordova-wp7/tooling/scripts/win-zip.js         |   32 +
 lib/cordova-wp8/LICENSE                            |   12 +
 lib/cordova-wp8/NOTICE                             |    5 +
 lib/cordova-wp8/README.md                          |   42 +
 lib/cordova-wp8/VERSION                            |    1 +
 .../framework/Images/appbar.back.rest.png          |  Bin 0 -> 375 bytes
 .../framework/Images/appbar.close.rest.png         |  Bin 0 -> 359 bytes
 .../framework/Images/appbar.feature.video.rest.png |  Bin 0 -> 433 bytes
 .../framework/Images/appbar.next.rest.png          |  Bin 0 -> 388 bytes
 .../framework/Images/appbar.save.rest.png          |  Bin 0 -> 297 bytes
 .../framework/Images/appbar.stop.rest.png          |  Bin 0 -> 350 bytes
 .../framework/Properties/AssemblyInfo.cs           |   35 +
 lib/cordova-wp8/framework/WPCordovaClassLib.csproj |  313 +
 lib/cordova-wp8/framework/WPCordovaClassLib.sln    |   32 +
 .../framework/resources/notification-beep.wav      |  Bin 0 -> 16630 bytes
 lib/cordova-wp8/templates/standalone/App.xaml      |   37 +
 lib/cordova-wp8/templates/standalone/App.xaml.cs   |  154 +
 .../templates/standalone/ApplicationIcon.png       |  Bin 0 -> 4951 bytes
 .../templates/standalone/Background.png            |  Bin 0 -> 10259 bytes
 .../templates/standalone/CordovaAppProj.csproj     |  228 +
 .../templates/standalone/CordovaSolution.sln       |   30 +
 .../standalone/Images/appbar.back.rest.png         |  Bin 0 -> 375 bytes
 .../standalone/Images/appbar.close.rest.png        |  Bin 0 -> 359 bytes
 .../Images/appbar.feature.video.rest.png           |  Bin 0 -> 433 bytes
 .../standalone/Images/appbar.next.rest.png         |  Bin 0 -> 388 bytes
 .../standalone/Images/appbar.save.rest.png         |  Bin 0 -> 297 bytes
 .../standalone/Images/appbar.stop.rest.png         |  Bin 0 -> 350 bytes
 lib/cordova-wp8/templates/standalone/MainPage.xaml |   53 +
 .../templates/standalone/MainPage.xaml.cs          |   72 +
 .../templates/standalone/Plugins/Accelerometer.cs  |  196 +
 .../standalone/Plugins/AudioFormatsHelper.cs       |   89 +
 .../templates/standalone/Plugins/AudioPlayer.cs    |  620 ++
 .../templates/standalone/Plugins/Battery.cs        |   79 +
 .../templates/standalone/Plugins/Camera.cs         |  490 ++
 .../templates/standalone/Plugins/Capture.cs        |  736 ++
 .../templates/standalone/Plugins/Compass.cs        |  362 +
 .../templates/standalone/Plugins/Contacts.cs       |  664 ++
 .../templates/standalone/Plugins/DebugConsole.cs   |   49 +
 .../templates/standalone/Plugins/Device.cs         |  135 +
 .../templates/standalone/Plugins/File.cs           | 1676 ++++
 .../templates/standalone/Plugins/FileTransfer.cs   |  526 ++
 .../templates/standalone/Plugins/GeoLocation.cs    |   34 +
 .../templates/standalone/Plugins/Globalization.cs  | 1177 +++
 .../standalone/Plugins/ImageExifHelper.cs          |  209 +
 .../templates/standalone/Plugins/InAppBrowser.cs   |  271 +
 .../templates/standalone/Plugins/Media.cs          |  547 ++
 .../templates/standalone/Plugins/MimeTypeMapper.cs |  101 +
 .../templates/standalone/Plugins/NetworkStatus.cs  |  129 +
 .../templates/standalone/Plugins/Notification.cs   |  361 +
 .../standalone/Plugins/UI/AudioCaptureTask.cs      |  107 +
 .../standalone/Plugins/UI/AudioRecorder.xaml       |   66 +
 .../standalone/Plugins/UI/AudioRecorder.xaml.cs    |  307 +
 .../standalone/Plugins/UI/ImageCapture.xaml        |   26 +
 .../standalone/Plugins/UI/ImageCapture.xaml.cs     |  109 +
 .../standalone/Plugins/UI/NotificationBox.xaml     |   62 +
 .../standalone/Plugins/UI/NotificationBox.xaml.cs  |   41 +
 .../standalone/Plugins/UI/VideoCaptureTask.cs      |  105 +
 .../standalone/Plugins/UI/VideoRecorder.xaml       |   52 +
 .../standalone/Plugins/UI/VideoRecorder.xaml.cs    |  405 +
 .../standalone/Properties/AppManifest.xml          |    6 +
 .../standalone/Properties/AssemblyInfo.cs          |   39 +
 .../standalone/Properties/WMAppManifest.xml        |   53 +
 .../templates/standalone/SplashScreenImage.jpg     |  Bin 0 -> 33248 bytes
 lib/cordova-wp8/templates/standalone/VERSION       |    1 +
 lib/cordova-wp8/templates/standalone/config.xml    |   49 +
 .../templates/standalone/cordova/build.bat         |    9 +
 .../templates/standalone/cordova/clean.bat         |    9 +
 .../cordova/lib/CordovaDeploy/CordovaDeploy.sln    |   20 +
 .../CordovaDeploy/CordovaDeploy.csproj             |   96 +
 .../lib/CordovaDeploy/CordovaDeploy/Program.cs     |  424 +
 .../CordovaDeploy/Properties/AssemblyInfo.cs       |   36 +
 .../lib/CordovaDeploy/CordovaDeploy/app.config     |    3 +
 .../templates/standalone/cordova/lib/build.js      |  181 +
 .../templates/standalone/cordova/lib/clean.js      |  124 +
 .../templates/standalone/cordova/lib/deploy.js     |  326 +
 .../standalone/cordova/lib/install-device.bat      |    9 +
 .../standalone/cordova/lib/install-emulator.bat    |    9 +
 .../standalone/cordova/lib/list-devices.bat        |    9 +
 .../cordova/lib/list-emulator-images.bat           |    9 +
 .../cordova/lib/list-started-emulators.bat         |    3 +
 .../templates/standalone/cordova/lib/log.js        |   77 +
 .../standalone/cordova/lib/start-emulator.bat      |    3 +
 .../standalone/cordova/lib/target-list.js          |  233 +
 .../templates/standalone/cordova/log.bat           |    3 +
 .../templates/standalone/cordova/run.bat           |    9 +
 .../standalone/cordovalib/BrowserMouseHelper.cs    |  162 +
 .../standalone/cordovalib/CommandFactory.cs        |  112 +
 .../standalone/cordovalib/Commands/BaseCommand.cs  |  187 +
 .../standalone/cordovalib/ConfigHandler.cs         |  242 +
 .../standalone/cordovalib/CordovaCommandCall.cs    |   99 +
 .../standalone/cordovalib/CordovaView.xaml         |   65 +
 .../standalone/cordovalib/CordovaView.xaml.cs      |  503 ++
 .../standalone/cordovalib/DOMStorageHelper.cs      |  145 +
 .../standalone/cordovalib/JSON/JsonHelper.cs       |   97 +
 .../standalone/cordovalib/NativeExecution.cs       |  246 +
 .../standalone/cordovalib/OrientationHelper.cs     |  128 +
 .../standalone/cordovalib/PluginResult.cs          |  139 +
 .../standalone/cordovalib/ScriptCallback.cs        |   80 +
 .../cordovalib/resources/notification-beep.wav     |  Bin 0 -> 16630 bytes
 .../standalone/resources/notification-beep.wav     |  Bin 0 -> 16630 bytes
 .../templates/standalone/www/cordova-2.7.0.js      | 6700 +++++++++++++++
 .../templates/standalone/www/css/index.css         |  115 +
 .../templates/standalone/www/img/logo.png          |  Bin 0 -> 21814 bytes
 .../templates/standalone/www/index.html            |   42 +
 .../templates/standalone/www/js/index.js           |   49 +
 .../templates/vs/MyTemplateStandAlone.vstemplate   |  113 +
 lib/cordova-wp8/templates/vs/description.txt       |    8 +
 lib/cordova-wp8/templates/vs/pg_templateIcon.png   |  Bin 0 -> 6546 bytes
 .../templates/vs/pg_templatePreview.jpg            |  Bin 0 -> 25875 bytes
 lib/cordova-wp8/tests/MobileSpecUnitTests/App.xaml |   38 +
 .../tests/MobileSpecUnitTests/App.xaml.cs          |  158 +
 .../tests/MobileSpecUnitTests/ApplicationIcon.png  |  Bin 0 -> 4951 bytes
 .../tests/MobileSpecUnitTests/Background.png       |  Bin 0 -> 10259 bytes
 .../tests/MobileSpecUnitTests/MainPage.xaml        |   52 +
 .../tests/MobileSpecUnitTests/MainPage.xaml.cs     |   42 +
 .../MobileSpecUnitTests/MobileSpecUnitTests.csproj |  247 +
 .../MobileSpecUnitTests/MobileSpecUnitTests.sln    |   44 +
 .../MobileSpecUnitTests/Properties/AppManifest.xml |    6 +
 .../MobileSpecUnitTests/Properties/AssemblyInfo.cs |   35 +
 .../Properties/WMAppManifest.xml                   |   51 +
 .../MobileSpecUnitTests/SplashScreenImage.jpg      |  Bin 0 -> 22066 bytes
 .../www/accelerometer/index.html                   |  138 +
 .../tests/MobileSpecUnitTests/www/audio/index.html |  394 +
 .../www/autotest/html/HtmlReporter.js              |  101 +
 .../www/autotest/html/HtmlReporterHelpers.js       |   60 +
 .../www/autotest/html/ReporterView.js              |  164 +
 .../www/autotest/html/SpecView.js                  |   79 +
 .../www/autotest/html/SuiteView.js                 |   22 +
 .../www/autotest/html/TrivialReporter.js           |  192 +
 .../MobileSpecUnitTests/www/autotest/index.html    |   37 +
 .../MobileSpecUnitTests/www/autotest/jasmine.css   |   81 +
 .../MobileSpecUnitTests/www/autotest/jasmine.js    | 2530 ++++++
 .../www/autotest/pages/accelerometer.html          |   48 +
 .../www/autotest/pages/all.html                    |   85 +
 .../www/autotest/pages/battery.html                |   44 +
 .../www/autotest/pages/bridge.html                 |   71 +
 .../www/autotest/pages/camera.html                 |   49 +
 .../www/autotest/pages/capture.html                |   49 +
 .../www/autotest/pages/compass.html                |   49 +
 .../www/autotest/pages/contacts.html               |   49 +
 .../www/autotest/pages/datauri.html                |   69 +
 .../www/autotest/pages/device.html                 |   49 +
 .../www/autotest/pages/file.html                   |   68 +
 .../www/autotest/pages/filetransfer.html           |   69 +
 .../www/autotest/pages/geolocation.html            |   49 +
 .../www/autotest/pages/globalization.html          |   70 +
 .../www/autotest/pages/media.html                  |   49 +
 .../www/autotest/pages/network.html                |   49 +
 .../www/autotest/pages/notification.html           |   49 +
 .../www/autotest/pages/platform.html               |   49 +
 .../www/autotest/pages/storage.html                |   49 +
 .../www/autotest/test-runner.js                    |   41 +
 .../www/autotest/tests/accelerometer.tests.js      |  193 +
 .../www/autotest/tests/battery.tests.js            |    5 +
 .../www/autotest/tests/bridge.tests.js             |  128 +
 .../www/autotest/tests/camera.tests.js             |   47 +
 .../www/autotest/tests/capture.tests.js            |   95 +
 .../www/autotest/tests/compass.tests.js            |   76 +
 .../www/autotest/tests/contacts.tests.js           |  451 +
 .../www/autotest/tests/datauri.tests.js            |   36 +
 .../www/autotest/tests/device.tests.js             |   34 +
 .../www/autotest/tests/file.tests.js               | 3462 ++++++++
 .../www/autotest/tests/filetransfer.tests.js       |  513 ++
 .../www/autotest/tests/geolocation.tests.js        |  119 +
 .../www/autotest/tests/globalization.tests.js      |  808 ++
 .../www/autotest/tests/media.tests.js              |  144 +
 .../www/autotest/tests/network.tests.js            |   25 +
 .../www/autotest/tests/notification.tests.js       |   20 +
 .../www/autotest/tests/platform.tests.js           |   10 +
 .../www/autotest/tests/storage.tests.js            |  174 +
 .../MobileSpecUnitTests/www/battery/index.html     |   96 +
 .../MobileSpecUnitTests/www/camera/index.html      |   96 +
 .../MobileSpecUnitTests/www/compass/index.html     |  128 +
 .../MobileSpecUnitTests/www/contacts/index.html    |  112 +
 .../tests/MobileSpecUnitTests/www/cordova-2.5.0.js | 6446 ++++++++++++++
 .../tests/MobileSpecUnitTests/www/cordova.js       |   14 +
 .../MobileSpecUnitTests/www/events/index.html      |   88 +
 .../tests/MobileSpecUnitTests/www/index.html       |   38 +
 .../MobileSpecUnitTests/www/location/index.html    |  147 +
 .../tests/MobileSpecUnitTests/www/main.js          |  140 +
 .../tests/MobileSpecUnitTests/www/master.css       |  136 +
 .../tests/MobileSpecUnitTests/www/misc/index.html  |   59 +
 .../tests/MobileSpecUnitTests/www/misc/page2.html  |   25 +
 .../MobileSpecUnitTests/www/network/index.html     |   59 +
 .../www/notification/index.html                    |   81 +
 .../tests/MobileSpecUnitTests/www/sql/index.html   |  132 +
 .../MobileSpecUnitTests/www/storage/index.html     |   50 +
 lib/cordova-wp8/tests/README.md                    |    7 +
 lib/cordova-wp8/tooling/scripts/buildjs.bat        |    2 +
 lib/cordova-wp8/tooling/scripts/buildjs.js         |  214 +
 lib/cordova-wp8/tooling/scripts/dist.bat           |    2 +
 lib/cordova-wp8/tooling/scripts/dist.js            |  202 +
 lib/cordova-wp8/tooling/scripts/new.bat            |    2 +
 lib/cordova-wp8/tooling/scripts/new.js             |  151 +
 lib/cordova-wp8/tooling/scripts/package.bat        |    2 +
 lib/cordova-wp8/tooling/scripts/package.js         |  213 +
 lib/cordova-wp8/tooling/scripts/reversion.bat      |    2 +
 lib/cordova-wp8/tooling/scripts/reversion.js       |  280 +
 lib/cordova-wp8/tooling/scripts/win-zip.js         |   43 +
 322 files changed, 65672 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 6a593f6..07b36d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,8 @@ spec/fixtures/projects/cordova
 lib/cordova-android/framework/bin
 lib/cordova-android/framework/gen
 lib/cordova-android/framework/local.properties
+lib/cordova-wp7/example
+lib/cordova-wp8/example
 .idea/*
 spec/fixtures/*
 .gitcore

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/LICENSE
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/LICENSE b/lib/cordova-wp7/LICENSE
new file mode 100644
index 0000000..6a504ba
--- /dev/null
+++ b/lib/cordova-wp7/LICENSE
@@ -0,0 +1,12 @@
+   
+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.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/NOTICE
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/NOTICE b/lib/cordova-wp7/NOTICE
new file mode 100644
index 0000000..c38e7d7
--- /dev/null
+++ b/lib/cordova-wp7/NOTICE
@@ -0,0 +1,5 @@
+Apache Cordova
+Copyright 2012 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/README.md
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/README.md b/lib/cordova-wp7/README.md
new file mode 100644
index 0000000..0235986
--- /dev/null
+++ b/lib/cordova-wp7/README.md
@@ -0,0 +1,50 @@
+Apache Cordova for Windows Phone 7
+===
+
+Apache Cordova WP7 is a .net application library that lets you create Apache Cordova applications targeting Windows Phone 7 devices.
+Apache Cordova based applications are, at the core, an application written with web technology: HTML, CSS and JavaScript.
+
+Requires
+---
+
+- Windows Phone SDK 7.1 [http://create.msdn.com/en-us/home/getting_started]
+
+
+Getting Started
+---
+
+- copy the file templates/CordovaStarter-x.x.x.zip to the folder : \My Documents\Visual Studio 2010\Templates\ProjectTemplates\
+ - if you have just installed VisualStudio, you should launch it once to create this folder
+ - if you prefer, you may add the project instead to the "Silverlight for Windows Phone" subfolder of "Visual C#".  This is up to you, and only affects where the project template is shown when creating a new project. Also, You may need to create this folder.
+- Launch Visual Studio 2010 and select to create a new project
+ - CordovaStarter should be listed as an option, give your new project a name
+  - Note: The description will let you know the version of Cordova you are targeting, if you have multiple templates.
+ - If you do not see it, you may have to select the top level 'Visual C#' to see it
+- Build and Run it!
+
+Important!!!
+---
+
+When you add or remove files/folders in the www folder you will need to do the following :
+
+- ensure the new item is included in the project ( Content ) This includes ALL images/css/html/js/* and anything that you want available at runtime.
+- Do not modify the CordovaSourceDictionary.xml file which is included in the project, it is auto-generated for you when you build.
+
+Known Problem Areas
+---
+
+Many of the Media APIs will not function as expected when debugging while connect to the device with the Zune software.
+To get around this, you need to use the Windows Phone Connect tool. For details, please check out this [MSDN blog article](http://blogs.msdn.com/b/jaimer/archive/2010/11/03/tips-for-debugging-wp7-media-apps-with-wpconnect.aspx).
+
+
+BUGS?
+-----
+File them at Apache Incubator
+https://issues.apache.org/jira/browse/CB
+
+
+Further Reading
+---
+
+- [http://docs.cordova.io](http://docs.cordova.io)
+- [http://wiki.cordova.io](http://wiki.cordova.io)

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/VERSION
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/VERSION b/lib/cordova-wp7/VERSION
new file mode 100644
index 0000000..9aa3464
--- /dev/null
+++ b/lib/cordova-wp7/VERSION
@@ -0,0 +1 @@
+2.7.0
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/bin/create.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/bin/create.js b/lib/cordova-wp7/bin/create.js
new file mode 100644
index 0000000..dac48de
--- /dev/null
+++ b/lib/cordova-wp7/bin/create.js
@@ -0,0 +1,252 @@
+/*
+       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.
+*/
+
+/*
+ * create a cordova/wp7 project
+ *
+ * USAGE
+ *  ./create [path package activity]
+
+    ./bin/create.bat C:\Users\Me\MyTestProj "test.proj" "TestProject"
+ */
+
+
+var fso=WScript.CreateObject("Scripting.FileSystemObject");
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\bin\\create.js').join('');
+
+var args = WScript.Arguments,
+    FRAMEWORK_PATH = '\\framework',
+    TOOLING_PATH = '\\tooling',
+    TEMPLATES_PATH = '\\templates',
+    // sub folder for standalone project
+    STANDALONE_PATH = TEMPLATES_PATH + '\\standalone',
+    // default template to use when creating the project
+    CREATE_TEMPLATE = STANDALONE_PATH,
+    USE_DLL = false,
+    PROJECT_PATH, 
+    PACKAGE, 
+    NAME;
+
+    // get version number
+var VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
+var BASE_VERSION = VERSION.split('rc', 1) + ".0";
+
+function Usage() {
+    Log("Usage: create PathTONewProject [ PackageName AppName ]");
+    Log("    PathTONewProject : The path to where you wish to create the project");
+    Log("    PackageName      : The namespace for the project (default is Cordova.Example)")
+    Log("    AppName          : The name of the application (default is CordovaAppProj)");
+    Log("examples:");
+    Log("    create C:\\Users\\anonymous\\Desktop\\MyProject");
+    Log("    create C:\\Users\\anonymous\\Desktop\\MyProject io.Cordova.Example AnApp");
+}
+
+// logs messaged to stdout and stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+var ForReading = 1, ForWriting = 2, ForAppending = 8;
+var TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0;
+
+function read(filename) {
+    var f=fso.OpenTextFile(filename, 1,2);
+    var s=f.ReadAll();
+    f.Close();
+    return s;
+}
+
+function write(filename, contents) {
+    var f=fso.OpenTextFile(filename, ForWriting, TristateTrue);
+    f.Write(contents);
+    f.Close();
+}
+
+function replaceInFile(filename, regexp, replacement) {
+    write(filename,read(filename).replace(regexp,replacement));
+}
+
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        WScript.sleep(100);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    //Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if (!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oShell.StdErr.AtEndOfStream) {
+        var line = oShell.StdErr.ReadAll();
+        Log(line, true);
+        WScript.Quit(1);
+    }
+}
+
+//generate guid for the project
+function genGuid() {
+    var TypeLib = WScript.CreateObject("Scriptlet.TypeLib");
+    strGuid = TypeLib.Guid.split("}")[0]; // there is extra crap after the } that is causing file streams to break, probably an EOF ... 
+    strGuid = strGuid.replace(/[\{\}]/g,""); 
+    return strGuid;
+}
+
+// builds the new cordova dll from the framework
+function build_dll(path) {
+    if (fso.FolderExists(path + FRAMEWORK_PATH + '\\Bin')) {
+        fso.DeleteFolder(path + FRAMEWORK_PATH + '\\Bin');
+    }
+    if (fso.FolderExists(path + FRAMEWORK_PATH + '\\obj')) {
+        fso.DeleteFolder(path + FRAMEWORK_PATH + '\\obj');
+    }
+    // move to framework directory
+    wscript_shell.CurrentDirectory = path + FRAMEWORK_PATH;
+    // build .dll in Release
+    exec_verbose('msbuild /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo /p:Configuration=Release;VersionNumber=' + VERSION + ';BaseVersionNumber=' + BASE_VERSION);
+    //Check if file dll was created
+    if (!fso.FileExists(path + FRAMEWORK_PATH + '\\Bin\\Release\\WPCordovaClassLib.dll')) {
+        Log('ERROR: MSBuild failed to create .dll when building WPCordovaClassLib.dll', true);
+        WScript.Quit(1);
+    }
+    Log("SUCCESS BUILDING DLL");
+}
+
+// creates new project in path, with the given package and app name
+function create(path, namespace, name) {
+    Log("Creating Cordova-WP7 Project:");
+    Log("\tApp Name : " + name);
+    Log("\tNamespace : " + namespace);
+    Log("\tPath : " + path);
+
+    // Copy the template source files to the new destination
+    fso.CopyFolder(ROOT + CREATE_TEMPLATE, path);
+
+    var newProjGuid = genGuid();
+    // replace the guid in the AppManifest
+    replaceInFile(path + "\\Properties\\WMAppManifest.xml","$guid1$",newProjGuid);
+    // replace safe-project-name in AppManifest
+    replaceInFile(path + "\\Properties\\WMAppManifest.xml",/\$safeprojectname\$/g,name);
+    replaceInFile(path + "\\Properties\\WMAppManifest.xml",/\$projectname\$/g,name);
+
+
+    replaceInFile(path + "\\App.xaml",/\$safeprojectname\$/g,namespace);
+    replaceInFile(path + "\\App.xaml.cs",/\$safeprojectname\$/g,namespace);
+
+    replaceInFile(path + "\\MainPage.xaml",/\$safeprojectname\$/g,namespace);
+    replaceInFile(path + "\\MainPage.xaml.cs",/\$safeprojectname\$/g,namespace);
+    replaceInFile(path + "\\CordovaAppProj.csproj",/\$safeprojectname\$/g,namespace);
+    if (NAME != "CordovaAppProj") {
+        var valid_name = NAME.replace(/(\.\s|\s\.|\s+|\.+)/g, '_');
+        replaceInFile(path + "\\CordovaSolution.sln", /CordovaAppProj/g, valid_name);
+        // rename project and solution
+        exec('%comspec% /c ren ' + path + "\\CordovaSolution.sln " + valid_name + '.sln');
+        exec('%comspec% /c ren ' + path + "\\CordovaAppProj.csproj " + valid_name + '.csproj');
+    }
+
+    //copy .dll if necessary
+    if (USE_DLL) {
+        var dllPath = ROOT + FRAMEWORK_PATH + '\\Bin\\Release\\WPCordovaClassLib.dll';
+        if (fso.FileExists(dllPath)) {
+            Log("WPCordovaClassLib.dll Found,  creating project");
+        }
+        else {
+            Log("WPCordovaClassLib.dll was not Found in " + dllPath);
+            Log('BUILDING: WPCordovaClassLib.dll');
+            build_dll(ROOT);
+        }
+
+        if (!fso.FolderExists(path + '\\CordovaLib')) {
+            fso.CreateFolder(path + '\\CordovaLib');
+        }
+        exec('%comspec% /c xcopy ' + ROOT + FRAMEWORK_PATH + '\\Bin\\Release\\WPCordovaClassLib.dll ' + path + '\\CordovaLib');
+        if (!fso.FileExists(path + '\\CordovaLib\\WPCordovaClassLib.dll')) {
+            Log('ERROR: Failed to copy WPCordovaClassLib.dll to project from', true);
+            Log('\t' + ROOT + FRAMEWORK_PATH + '\\Bin\\Release\\WPCordovaClassLib.dll', true);
+            Log('\tto', true);
+            Log('\t' + path + '\\CordovaLib', true)
+            WScript.Quit(1);
+        }
+    }
+
+    Log("CREATE SUCCESS : " + path);
+
+    // TODO: Name the project according to the arguments
+    // update the solution to include the new project by name
+    // version BS
+    // index.html title set to project name ?
+
+}
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help" || args(0) == "-h") {
+        Usage();
+        WScript.Quit(1);
+    }
+
+    PROJECT_PATH = args(0);
+    if (fso.FolderExists(PROJECT_PATH)) {
+        Log("Project directory already exists:", true);
+        Log("\t" + PROJECT_PATH, true);
+        Log("CREATE FAILED.", true);
+        WScript.Quit(1);
+    }
+
+    if (args.Count() > 1) {
+        PACKAGE = args(1);
+    }
+    else {
+        PACKAGE = "Cordova.Example";
+    }
+
+    if (args.Count() > 2) {
+        NAME = args(2);
+    }
+    else {
+        NAME = "CordovaAppProj";
+    }
+
+    create(PROJECT_PATH, PACKAGE, NAME);
+}
+else {
+    Usage();
+    WScript.Quit(1);
+}
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/framework/Images/appbar.back.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/framework/Images/appbar.back.rest.png b/lib/cordova-wp7/framework/Images/appbar.back.rest.png
new file mode 100644
index 0000000..4bc2b92
Binary files /dev/null and b/lib/cordova-wp7/framework/Images/appbar.back.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/framework/Images/appbar.close.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/framework/Images/appbar.close.rest.png b/lib/cordova-wp7/framework/Images/appbar.close.rest.png
new file mode 100644
index 0000000..8166a1c
Binary files /dev/null and b/lib/cordova-wp7/framework/Images/appbar.close.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/framework/Images/appbar.feature.video.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/framework/Images/appbar.feature.video.rest.png b/lib/cordova-wp7/framework/Images/appbar.feature.video.rest.png
new file mode 100644
index 0000000..baff565
Binary files /dev/null and b/lib/cordova-wp7/framework/Images/appbar.feature.video.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/framework/Images/appbar.next.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/framework/Images/appbar.next.rest.png b/lib/cordova-wp7/framework/Images/appbar.next.rest.png
new file mode 100644
index 0000000..ed577d7
Binary files /dev/null and b/lib/cordova-wp7/framework/Images/appbar.next.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/framework/Images/appbar.save.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/framework/Images/appbar.save.rest.png b/lib/cordova-wp7/framework/Images/appbar.save.rest.png
new file mode 100644
index 0000000..d49940e
Binary files /dev/null and b/lib/cordova-wp7/framework/Images/appbar.save.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/framework/Images/appbar.stop.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/framework/Images/appbar.stop.rest.png b/lib/cordova-wp7/framework/Images/appbar.stop.rest.png
new file mode 100644
index 0000000..4dd724f
Binary files /dev/null and b/lib/cordova-wp7/framework/Images/appbar.stop.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/framework/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/framework/Properties/AssemblyInfo.cs b/lib/cordova-wp7/framework/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..62c7964
--- /dev/null
+++ b/lib/cordova-wp7/framework/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("WPCordovaClassLib")]
+[assembly: AssemblyDescription("2.7.0")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Apache Cordova")]
+[assembly: AssemblyProduct("WPCordovaClassLib")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("633ee7ad-9a75-4b68-96e9-281528c50275")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("2.7.0.0")]
+[assembly: AssemblyFileVersion("2.6.0rc1")]
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/framework/WPCordovaClassLib.csproj
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/framework/WPCordovaClassLib.csproj b/lib/cordova-wp7/framework/WPCordovaClassLib.csproj
new file mode 100644
index 0000000..1292286
--- /dev/null
+++ b/lib/cordova-wp7/framework/WPCordovaClassLib.csproj
@@ -0,0 +1,297 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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. 
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>10.0.20506</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{FC6A1A70-892D-46AD-9E4A-9793F72AF780}</ProjectGuid>
+    <ProjectTypeGuids>{C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>WPCordovaClassLib</RootNamespace>
+    <AssemblyName>WPCordovaClassLib</AssemblyName>
+    <AssemblyVersion>$(BaseVersionNumber)</AssemblyVersion>
+    <AssemblyFileVersion>$(VersionNumber)</AssemblyFileVersion>
+    <AssemblyDescription>$(VersionNumber)</AssemblyDescription>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
+    <TargetFrameworkProfile>WindowsPhone71</TargetFrameworkProfile>
+    <TargetFrameworkIdentifier>Silverlight</TargetFrameworkIdentifier>
+    <SilverlightApplication>false</SilverlightApplication>
+    <ValidateXaml>true</ValidateXaml>
+    <ThrowErrorsInValidation>true</ThrowErrorsInValidation>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>Bin\Debug</OutputPath>
+    <DefineConstants>TRACE;DEBUG;SILVERLIGHT;WINDOWS_PHONE;CORDOVA_CLASSLIB</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>Bin\Release</OutputPath>
+    <DefineConstants>TRACE;DEBUG;SILVERLIGHT;WINDOWS_PHONE;CORDOVA_CLASSLIB</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <DocumentationFile>
+    </DocumentationFile>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Devices.Sensors" />
+    <Reference Include="Microsoft.Phone" />
+    <Reference Include="Microsoft.Phone.Interop" />
+    <Reference Include="Microsoft.Xna.Framework" />
+    <Reference Include="System.Device" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.Servicemodel" />
+    <Reference Include="System.Servicemodel.Web" />
+    <Reference Include="System.Windows" />
+    <Reference Include="system" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Net" />
+    <Reference Include="System.Xml.Linq" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\templates\standalone\cordovalib\BrowserMouseHelper.cs">
+      <Link>CordovaLib\BrowserMouseHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\CommandFactory.cs">
+      <Link>CordovaLib\CommandFactory.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\Commands\BaseCommand.cs">
+      <Link>CordovaLib\Commands\BaseCommand.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\ConfigHandler.cs">
+      <Link>CordovaLib\ConfigHandler.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\CordovaCommandCall.cs">
+      <Link>CordovaLib\CordovaCommandCall.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\CordovaView.xaml.cs">
+      <Link>CordovaLib\CordovaView.xaml.cs</Link>
+      <DependentUpon>CordovaView.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\DOMStorageHelper.cs">
+      <Link>CordovaLib\DOMStorageHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\JSON\JsonHelper.cs">
+      <Link>CordovaLib\JSON\JsonHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\NativeExecution.cs">
+      <Link>CordovaLib\NativeExecution.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\OrientationHelper.cs">
+      <Link>CordovaLib\OrientationHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\PluginResult.cs">
+      <Link>CordovaLib\PluginResult.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\ScriptCallback.cs">
+      <Link>CordovaLib\ScriptCallback.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Accelerometer.cs">
+      <Link>CordovaLib\Plugins\Accelerometer.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\AudioFormatsHelper.cs">
+      <Link>CordovaLib\Plugins\AudioFormatsHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\AudioPlayer.cs">
+      <Link>CordovaLib\Plugins\AudioPlayer.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Battery.cs">
+      <Link>CordovaLib\Plugins\Battery.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Camera.cs">
+      <Link>CordovaLib\Plugins\Camera.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Capture.cs">
+      <Link>CordovaLib\Plugins\Capture.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Compass.cs">
+      <Link>CordovaLib\Plugins\Compass.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Contacts.cs">
+      <Link>CordovaLib\Plugins\Contacts.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\DebugConsole.cs">
+      <Link>CordovaLib\Plugins\DebugConsole.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Device.cs">
+      <Link>CordovaLib\Plugins\Device.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\File.cs">
+      <Link>CordovaLib\Plugins\File.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\FileTransfer.cs">
+      <Link>CordovaLib\Plugins\FileTransfer.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\GeoLocation.cs">
+      <Link>CordovaLib\Plugins\GeoLocation.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Globalization.cs">
+      <Link>CordovaLib\Plugins\Globalization.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\ImageExifHelper.cs">
+      <Link>CordovaLib\Plugins\ImageExifHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\InAppBrowser.cs">
+      <Link>CordovaLib\Plugins\InAppBrowser.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Media.cs">
+      <Link>CordovaLib\Plugins\Media.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\MimeTypeMapper.cs">
+      <Link>CordovaLib\Plugins\MimeTypeMapper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\NetworkStatus.cs">
+      <Link>CordovaLib\Plugins\NetworkStatus.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Notification.cs">
+      <Link>CordovaLib\Plugins\Notification.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\AudioCaptureTask.cs">
+      <Link>CordovaLib\Plugins\UI\AudioCaptureTask.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\AudioRecorder.xaml.cs">
+      <Link>CordovaLib\Plugins\UI\AudioRecorder.xaml.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\ImageCapture.xaml.cs">
+      <Link>CordovaLib\Plugins\UI\ImageCapture.xaml.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\NotificationBox.xaml.cs">
+      <Link>CordovaLib\Plugins\UI\NotificationBox.xaml.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\VideoCaptureTask.cs">
+      <Link>CordovaLib\Plugins\UI\VideoCaptureTask.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\VideoRecorder.xaml.cs">
+      <Link>CordovaLib\Plugins\UI\VideoRecorder.xaml.cs</Link>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="..\templates\standalone\cordovalib\resources\notification-beep.wav">
+      <Link>CordovaLib\resources\notification-beep.wav</Link>
+    </Content>
+    <Content Include="..\templates\standalone\Images\appbar.back.rest.png">
+      <Link>CordovaLib\Images\appbar.back.rest.png</Link>
+    </Content>
+    <Content Include="..\templates\standalone\Images\appbar.close.rest.png">
+      <Link>CordovaLib\Images\appbar.close.rest.png</Link>
+    </Content>
+    <Content Include="..\templates\standalone\Images\appbar.feature.video.rest.png">
+      <Link>CordovaLib\Images\appbar.feature.video.rest.png</Link>
+    </Content>
+    <Content Include="..\templates\standalone\Images\appbar.next.rest.png">
+      <Link>CordovaLib\Images\appbar.next.rest.png</Link>
+    </Content>
+    <Content Include="..\templates\standalone\Images\appbar.save.rest.png">
+      <Link>CordovaLib\Images\appbar.save.rest.png</Link>
+    </Content>
+    <Content Include="..\templates\standalone\Images\appbar.stop.rest.png">
+      <Link>CordovaLib\Images\appbar.stop.rest.png</Link>
+    </Content>
+    <Content Include="Images\appbar.back.rest.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Images\appbar.close.rest.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Images\appbar.feature.video.rest.png">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Images\appbar.next.rest.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Images\appbar.stop.rest.png">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Images\appbar.save.rest.png">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Resource Include="resources\notification-beep.wav" />
+  </ItemGroup>
+  <ItemGroup>
+    <Page Include="..\templates\standalone\cordovalib\CordovaView.xaml">
+      <Link>CordovaLib\CordovaView.xaml</Link>
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="..\templates\standalone\Plugins\UI\AudioRecorder.xaml">
+      <Link>CordovaLib\Plugins\UI\AudioRecorder.xaml</Link>
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="..\templates\standalone\Plugins\UI\ImageCapture.xaml">
+      <Link>CordovaLib\Plugins\UI\ImageCapture.xaml</Link>
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="..\templates\standalone\Plugins\UI\NotificationBox.xaml">
+      <Link>CordovaLib\Plugins\UI\NotificationBox.xaml</Link>
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="..\templates\standalone\Plugins\UI\VideoRecorder.xaml">
+      <Link>CordovaLib\Plugins\UI\VideoRecorder.xaml</Link>
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.$(TargetFrameworkProfile).Overrides.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.CSharp.targets" />
+  <ProjectExtensions />
+  <PropertyGroup>
+    <PreBuildEvent>
+    </PreBuildEvent>
+  </PropertyGroup>
+  <PropertyGroup>
+    <PostBuildEvent>
+    </PostBuildEvent>
+  </PropertyGroup>
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+    <AssemblyInfo
+      AssemblyVersion="$(BaseVersion)"
+      AssemblyFileVersion="$(BaseVersion)"
+      AssemblyDescription="$(VersionNumber)"
+    >
+    </AssemblyInfo>
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>-->
+</Project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/framework/WPCordovaClassLib.sln
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/framework/WPCordovaClassLib.sln b/lib/cordova-wp7/framework/WPCordovaClassLib.sln
new file mode 100644
index 0000000..2d5e088
--- /dev/null
+++ b/lib/cordova-wp7/framework/WPCordovaClassLib.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPCordovaClassLib", "WPCordovaClassLib.csproj", "{FC6A1A70-892D-46AD-9E4A-9793F72AF780}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/framework/resources/notification-beep.wav
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/framework/resources/notification-beep.wav b/lib/cordova-wp7/framework/resources/notification-beep.wav
new file mode 100644
index 0000000..d0ad085
Binary files /dev/null and b/lib/cordova-wp7/framework/resources/notification-beep.wav differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/App.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/App.xaml b/lib/cordova-wp7/templates/standalone/App.xaml
new file mode 100644
index 0000000..18072fe
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/App.xaml
@@ -0,0 +1,37 @@
+<!--
+ 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. 
+-->
+<Application 
+    x:Class="$safeprojectname$.App"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"       
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone">
+
+    <!--Application Resources-->
+    <Application.Resources>
+    </Application.Resources>
+
+    <Application.ApplicationLifetimeObjects>
+        <!--Required object that handles lifetime events for the application-->
+        <shell:PhoneApplicationService 
+            Launching="Application_Launching" Closing="Application_Closing" 
+            Activated="Application_Activated" Deactivated="Application_Deactivated"/>
+    </Application.ApplicationLifetimeObjects>
+
+</Application>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/App.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/App.xaml.cs b/lib/cordova-wp7/templates/standalone/App.xaml.cs
new file mode 100644
index 0000000..2b7306d
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/App.xaml.cs
@@ -0,0 +1,154 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Shell;
+
+namespace $safeprojectname$
+{
+    public partial class App : Application
+    {
+        /// <summary>
+        /// Provides easy access to the root frame of the Phone Application.
+        /// </summary>
+        /// <returns>The root frame of the Phone Application.</returns>
+        public PhoneApplicationFrame RootFrame { get; private set; }
+
+        /// <summary>
+        /// Constructor for the Application object.
+        /// </summary>
+        public App()
+        {
+            // Global handler for uncaught exceptions. 
+            UnhandledException += Application_UnhandledException;
+
+            // Show graphics profiling information while debugging.
+            if (System.Diagnostics.Debugger.IsAttached)
+            {
+                // Display the current frame rate counters.
+                //Application.Current.Host.Settings.EnableFrameRateCounter = true;
+
+                // Show the areas of the app that are being redrawn in each frame.
+                //Application.Current.Host.Settings.EnableRedrawRegions = true;
+
+                // Enable non-production analysis visualization mode, 
+                // which shows areas of a page that are being GPU accelerated with a colored overlay.
+                //Application.Current.Host.Settings.EnableCacheVisualization = true;
+            }
+
+            // Standard Silverlight initialization
+            InitializeComponent();
+
+            // Phone-specific initialization
+            InitializePhoneApplication();
+        }
+
+        // Code to execute when the application is launching (eg, from Start)
+        // This code will not execute when the application is reactivated
+        private void Application_Launching(object sender, LaunchingEventArgs e)
+        {
+        }
+
+        // Code to execute when the application is activated (brought to foreground)
+        // This code will not execute when the application is first launched
+        private void Application_Activated(object sender, ActivatedEventArgs e)
+        {
+        }
+
+        // Code to execute when the application is deactivated (sent to background)
+        // This code will not execute when the application is closing
+        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
+        {
+        }
+
+        // Code to execute when the application is closing (eg, user hit Back)
+        // This code will not execute when the application is deactivated
+        private void Application_Closing(object sender, ClosingEventArgs e)
+        {
+        }
+
+        // Code to execute if a navigation fails
+        private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
+        {
+            if (System.Diagnostics.Debugger.IsAttached)
+            {
+                // A navigation has failed; break into the debugger
+                System.Diagnostics.Debugger.Break();
+            }
+        }
+
+        // Code to execute on Unhandled Exceptions
+        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
+        {
+            if (System.Diagnostics.Debugger.IsAttached)
+            {
+                // An unhandled exception has occurred; break into the debugger
+                System.Diagnostics.Debugger.Break();
+            }
+        }
+
+        #region Phone application initialization
+
+        // Avoid double-initialization
+        private bool phoneApplicationInitialized = false;
+
+        // Do not add any additional code to this method
+        private void InitializePhoneApplication()
+        {
+            if (phoneApplicationInitialized)
+                return;
+
+            // Create the frame but don't set it as RootVisual yet; this allows the splash
+            // screen to remain active until the application is ready to render.
+            RootFrame = new PhoneApplicationFrame();
+            RootFrame.Navigated += CompleteInitializePhoneApplication;
+
+            // Handle navigation failures
+            RootFrame.NavigationFailed += RootFrame_NavigationFailed;
+
+            // Ensure we don't initialize again
+            phoneApplicationInitialized = true;
+        }
+
+        // Do not add any additional code to this method
+        private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
+        {
+            // Set the root visual to allow the application to render
+            if (RootVisual != RootFrame)
+                RootVisual = RootFrame;
+
+            // Remove this handler since it is no longer needed
+            RootFrame.Navigated -= CompleteInitializePhoneApplication;
+        }
+
+        #endregion
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/ApplicationIcon.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/ApplicationIcon.png b/lib/cordova-wp7/templates/standalone/ApplicationIcon.png
new file mode 100644
index 0000000..6b69046
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/ApplicationIcon.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Background.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Background.png b/lib/cordova-wp7/templates/standalone/Background.png
new file mode 100644
index 0000000..2667df4
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/Background.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/BuildManifestProcessor.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/BuildManifestProcessor.js b/lib/cordova-wp7/templates/standalone/BuildManifestProcessor.js
new file mode 100644
index 0000000..ab2ac9d
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/BuildManifestProcessor.js
@@ -0,0 +1,106 @@
+/*
+ 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 objArgs = WScript.Arguments;
+for (i = 0; i < objArgs.length; i++) {
+    WScript.Echo("Arg :: " + objArgs(i));
+}
+
+var projectFilePath = null;
+if (objArgs && objArgs.length > 0) {
+    projectFilePath = objArgs(0);
+}
+
+
+var fso = WScript.CreateObject("Scripting.FileSystemObject");
+var outFile = fso.CreateTextFile("..\\..\\CordovaSourceDictionary.xml", true);
+
+outFile.WriteLine('<?xml version="1.0" encoding="utf-8"?>');
+outFile.WriteLine('<!-- This file is auto-generated, do not edit! -jm -->');
+outFile.WriteLine('<CordovaSourceDictionary>');
+
+
+function getDirectoryListing(path) {
+    var retList = [];
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    var folder = fso.GetFolder(path);
+    // iterate over the files in the folder
+    for (var files = new Enumerator(folder.files) ; !files.atEnd() ; files.moveNext()) {
+        retList.push(path + files.item().name);
+    }
+    // iterate over the child folders in the folder
+    for (var subFlds = new Enumerator(folder.SubFolders) ; !subFlds.atEnd() ; subFlds.moveNext()) {
+        var subDirList = getDirectoryListing(path + subFlds.item().name + "\\");
+        retList = retList.concat(subDirList);
+    }
+    return retList;
+}
+
+
+// We need to get any Linked files from the project
+
+WScript.Echo("Adding Source Files ...");
+if (projectFilePath != null) {
+    var projXml = WScript.CreateObject("Microsoft.XMLDOM");
+
+    projXml.async = false;
+    if (projXml.load(projectFilePath)) {
+
+        // add linked content ( windows shortcuts )
+        var nodes = projXml.selectNodes("/Project/ItemGroup/Content/Link");
+        WScript.Echo("/Project/ItemGroup/Content/Link nodes.length" + nodes.length);
+        for (var n = 0; n < nodes.length; n++) {
+            outFile.WriteLine('    <FilePath Value="' + nodes[n].text + '"/>');
+        }
+
+        // add files of type Resource
+        nodes = projXml.selectNodes("/Project/ItemGroup/Resource/Link");
+        WScript.Echo("/Project/ItemGroup/Resource/Link nodes.length" + nodes.length);
+        for (n = 0; n < nodes.length; n++) {
+            outFile.WriteLine('    <FilePath Value="' + nodes[n].text + '"/>');
+        }
+
+        // add Content files from www folder
+        nodes = projXml.selectNodes("/Project/ItemGroup/Content[@Include]");
+        WScript.Echo("/Project/ItemGroup/Content nodes.length" + nodes.length);
+        for (n = 0; n < nodes.length; n++) {
+            for (var i = 0; i < nodes[n].attributes.length; i++) {
+
+                if (nodes[n].attributes[i].name == "Include") {
+                    var val = nodes[n].attributes[i].value;
+                    if (val.indexOf("www\\**") == 0 ){
+                        WScript.echo("adding wildcard files");
+                        var fileList = getDirectoryListing("..\\..\\www\\");
+                        for (var index = 0 ; index < fileList.length; index++)
+                        {
+                            outFile.WriteLine('    <FilePath Value="' + fileList[index].replace(/..\\..\\www/g,"www") + '"/>');
+                        }
+                    }
+                    else if (val.indexOf("www") == 0) {
+                        WScript.Echo("adding value :: " + val);
+                        outFile.WriteLine('    <FilePath Value="' + val + '"/>');
+                    }
+                }
+            }
+            
+        }
+    }
+}
+
+outFile.WriteLine('</CordovaSourceDictionary>');
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/CordovaAppProj.csproj
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/CordovaAppProj.csproj b/lib/cordova-wp7/templates/standalone/CordovaAppProj.csproj
new file mode 100644
index 0000000..61b3be1
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/CordovaAppProj.csproj
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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. 
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>10.0.20506</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}</ProjectGuid>
+    <ProjectTypeGuids>{C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>$safeprojectname$</RootNamespace>
+    <AssemblyName>$safeprojectname$</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
+    <TargetFrameworkProfile>WindowsPhone71</TargetFrameworkProfile>
+    <TargetFrameworkIdentifier>Silverlight</TargetFrameworkIdentifier>
+    <SilverlightApplication>true</SilverlightApplication>
+    <SupportedCultures>
+    </SupportedCultures>
+    <XapOutputs>true</XapOutputs>
+    <GenerateSilverlightManifest>true</GenerateSilverlightManifest>
+    <XapFilename>$safeprojectname$.xap</XapFilename>
+    <SilverlightManifestTemplate>Properties\AppManifest.xml</SilverlightManifestTemplate>
+    <SilverlightAppEntry>$safeprojectname$.App</SilverlightAppEntry>
+    <ValidateXaml>true</ValidateXaml>
+    <ThrowErrorsInValidation>true</ThrowErrorsInValidation>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>Bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>Bin\Release</OutputPath>
+    <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Devices.Sensors" />
+    <Reference Include="Microsoft.Phone" />
+    <Reference Include="Microsoft.Phone.Interop" />
+    <Reference Include="Microsoft.Xna.Framework" />
+    <Reference Include="System.Device" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.Servicemodel.Web" />
+    <Reference Include="System.Windows" />
+    <Reference Include="system" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Net" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Xml.Linq" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="App.xaml.cs">
+      <DependentUpon>App.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="cordovalib\BrowserMouseHelper.cs" />
+    <Compile Include="cordovalib\CommandFactory.cs" />
+    <Compile Include="cordovalib\Commands\BaseCommand.cs" />
+    <Compile Include="cordovalib\ConfigHandler.cs" />
+    <Compile Include="cordovalib\CordovaCommandCall.cs" />
+    <Compile Include="cordovalib\CordovaView.xaml.cs">
+      <DependentUpon>CordovaView.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="cordovalib\DOMStorageHelper.cs" />
+    <Compile Include="cordovalib\JSON\JsonHelper.cs" />
+    <Compile Include="cordovalib\NativeExecution.cs" />
+    <Compile Include="cordovalib\OrientationHelper.cs" />
+    <Compile Include="cordovalib\PluginResult.cs" />
+    <Compile Include="cordovalib\ScriptCallback.cs" />
+
+
+
+    <Compile Include="MainPage.xaml.cs">
+      <DependentUpon>MainPage.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="App.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </ApplicationDefinition>
+    <Page Include="cordovalib\CordovaView.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    
+    <Page Include="MainPage.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+
+    <Page Include="Plugins\UI\AudioRecorder.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="Plugins\UI\ImageCapture.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="Plugins\UI\NotificationBox.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="Plugins\UI\VideoRecorder.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="www\**" />
+    <Content Include="config.xml" />
+    <Content Include="Images\**" />
+
+    <Content Include="resources\notification-beep.wav" />
+    <None Include="VERSION" />
+    
+    <Content Include="CordovaSourceDictionary.xml">
+      <SubType>Designer</SubType>
+    </Content>
+    <None Include="BuildManifestProcessor.js" />
+    <None Include="Properties\AppManifest.xml">
+      <SubType>Designer</SubType>
+    </None>
+    <None Include="Properties\WMAppManifest.xml">
+      <SubType>Designer</SubType>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="ApplicationIcon.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Background.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="SplashScreenImage.jpg" />
+  </ItemGroup>
+  <ItemGroup>
+    <WCFMetadata Include="Service References\" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
+  </ItemGroup>
+  <ItemGroup>
+      <Compile Include="Plugins\*" />
+      <Compile Include="Plugins\UI\*.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.$(TargetFrameworkProfile).Overrides.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions />
+  <PropertyGroup>
+    <PreBuildEvent>CScript "$(ProjectDir)/BuildManifestProcessor.js" "$(ProjectPath)"</PreBuildEvent>
+  </PropertyGroup>
+  <PropertyGroup>
+    <PostBuildEvent>
+    </PostBuildEvent>
+  </PropertyGroup>
+</Project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/CordovaSolution.sln
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/CordovaSolution.sln b/lib/cordova-wp7/templates/standalone/CordovaSolution.sln
new file mode 100644
index 0000000..5ce2292
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/CordovaSolution.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010 Express for Windows Phone
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CordovaAppProj", "CordovaAppProj.csproj", "{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Release|Any CPU.Deploy.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/CordovaSourceDictionary.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/CordovaSourceDictionary.xml b/lib/cordova-wp7/templates/standalone/CordovaSourceDictionary.xml
new file mode 100644
index 0000000..74f51b7
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/CordovaSourceDictionary.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This file is auto-generated, do not edit! -jm -->
+<CordovaSourceDictionary>
+    <FilePath Value="www\cordova-2.6.0.js"/>
+    <FilePath Value="www\index.html"/>
+    <FilePath Value="www\css\index.css"/>
+    <FilePath Value="www\img\logo.png"/>
+    <FilePath Value="www\js\index.js"/>
+</CordovaSourceDictionary>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Images/appbar.back.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Images/appbar.back.rest.png b/lib/cordova-wp7/templates/standalone/Images/appbar.back.rest.png
new file mode 100644
index 0000000..4bc2b92
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/Images/appbar.back.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Images/appbar.close.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Images/appbar.close.rest.png b/lib/cordova-wp7/templates/standalone/Images/appbar.close.rest.png
new file mode 100644
index 0000000..8166a1c
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/Images/appbar.close.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Images/appbar.feature.video.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Images/appbar.feature.video.rest.png b/lib/cordova-wp7/templates/standalone/Images/appbar.feature.video.rest.png
new file mode 100644
index 0000000..baff565
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/Images/appbar.feature.video.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Images/appbar.next.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Images/appbar.next.rest.png b/lib/cordova-wp7/templates/standalone/Images/appbar.next.rest.png
new file mode 100644
index 0000000..ed577d7
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/Images/appbar.next.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Images/appbar.save.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Images/appbar.save.rest.png b/lib/cordova-wp7/templates/standalone/Images/appbar.save.rest.png
new file mode 100644
index 0000000..d49940e
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/Images/appbar.save.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Images/appbar.stop.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Images/appbar.stop.rest.png b/lib/cordova-wp7/templates/standalone/Images/appbar.stop.rest.png
new file mode 100644
index 0000000..4dd724f
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/Images/appbar.stop.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/MainPage.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/MainPage.xaml b/lib/cordova-wp7/templates/standalone/MainPage.xaml
new file mode 100644
index 0000000..8b1e84b
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/MainPage.xaml
@@ -0,0 +1,35 @@
+
+<phone:PhoneApplicationPage 
+    x:Class="$safeprojectname$.MainPage"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d" FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    Background="Black"
+    SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
+    shell:SystemTray.IsVisible="True" d:DesignHeight="768" d:DesignWidth="480" 
+    xmlns:my="clr-namespace:WPCordovaClassLib">
+    <Grid x:Name="LayoutRoot" Background="Transparent" HorizontalAlignment="Stretch">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+        <my:CordovaView HorizontalAlignment="Stretch" 
+                   Margin="0,0,0,0"  
+                   x:Name="CordovaView" 
+                   VerticalAlignment="Stretch" />
+        <Image Source="SplashScreenImage.jpg"
+          x:Name="SplashImage"
+          VerticalAlignment="Center"
+          HorizontalAlignment="Stretch">
+            <Image.Projection>
+                <PlaneProjection x:Name="SplashProjector"  CenterOfRotationX="0"/>
+            </Image.Projection>
+        </Image>
+    </Grid>
+
+</phone:PhoneApplicationPage>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/MainPage.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/MainPage.xaml.cs b/lib/cordova-wp7/templates/standalone/MainPage.xaml.cs
new file mode 100644
index 0000000..b9ec7e1
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/MainPage.xaml.cs
@@ -0,0 +1,72 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+using System.IO;
+using System.Windows.Media.Imaging;
+using System.Windows.Resources;
+
+
+namespace $safeprojectname$
+{
+    public partial class MainPage : PhoneApplicationPage
+    {
+        // Constructor
+        public MainPage()
+        {
+            InitializeComponent();
+            this.CordovaView.Loaded += CordovaView_Loaded;
+        }
+
+        private void CordovaView_Loaded(object sender, RoutedEventArgs e)
+        {
+            this.CordovaView.Loaded -= CordovaView_Loaded;
+            // first time load will have an animation
+            Storyboard _storyBoard = new Storyboard();
+            DoubleAnimation animation = new DoubleAnimation()
+            {
+                From = 0,
+                Duration = TimeSpan.FromSeconds(0.6),
+                To = 90
+            };
+            Storyboard.SetTarget(animation, SplashProjector);
+            Storyboard.SetTargetProperty(animation, new PropertyPath("RotationY"));
+            _storyBoard.Children.Add(animation);
+            _storyBoard.Begin();
+            _storyBoard.Completed += Splash_Completed;
+        }
+
+        void Splash_Completed(object sender, EventArgs e)
+        {
+            (sender as Storyboard).Completed -= Splash_Completed;
+            LayoutRoot.Children.Remove(SplashImage);
+        }
+    }
+}


[24/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/framework/WPCordovaClassLib.sln
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/framework/WPCordovaClassLib.sln b/lib/cordova-wp8/framework/WPCordovaClassLib.sln
new file mode 100644
index 0000000..c34cba1
--- /dev/null
+++ b/lib/cordova-wp8/framework/WPCordovaClassLib.sln
@@ -0,0 +1,32 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2012 for Windows Phone
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPCordovaClassLib", "WPCordovaClassLib.csproj", "{FC6A1A70-892D-46AD-9E4A-9793F72AF780}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|ARM = Debug|ARM
+		Debug|Any CPU = Debug|Any CPU
+		Debug|x86 = Debug|x86
+		Release|ARM = Release|ARM
+		Release|Any CPU = Release|Any CPU
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|ARM.ActiveCfg = Debug|ARM
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|ARM.Build.0 = Debug|ARM
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|x86.ActiveCfg = Debug|x86
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Debug|x86.Build.0 = Debug|x86
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|ARM.ActiveCfg = Release|ARM
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|ARM.Build.0 = Release|ARM
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|Any CPU.Build.0 = Release|Any CPU
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|x86.ActiveCfg = Release|x86
+		{FC6A1A70-892D-46AD-9E4A-9793F72AF780}.Release|x86.Build.0 = Release|x86
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/framework/resources/notification-beep.wav
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/framework/resources/notification-beep.wav b/lib/cordova-wp8/framework/resources/notification-beep.wav
new file mode 100644
index 0000000..d0ad085
Binary files /dev/null and b/lib/cordova-wp8/framework/resources/notification-beep.wav differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/App.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/App.xaml b/lib/cordova-wp8/templates/standalone/App.xaml
new file mode 100644
index 0000000..18072fe
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/App.xaml
@@ -0,0 +1,37 @@
+<!--
+ 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. 
+-->
+<Application 
+    x:Class="$safeprojectname$.App"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"       
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone">
+
+    <!--Application Resources-->
+    <Application.Resources>
+    </Application.Resources>
+
+    <Application.ApplicationLifetimeObjects>
+        <!--Required object that handles lifetime events for the application-->
+        <shell:PhoneApplicationService 
+            Launching="Application_Launching" Closing="Application_Closing" 
+            Activated="Application_Activated" Deactivated="Application_Deactivated"/>
+    </Application.ApplicationLifetimeObjects>
+
+</Application>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/App.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/App.xaml.cs b/lib/cordova-wp8/templates/standalone/App.xaml.cs
new file mode 100644
index 0000000..2b7306d
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/App.xaml.cs
@@ -0,0 +1,154 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Shell;
+
+namespace $safeprojectname$
+{
+    public partial class App : Application
+    {
+        /// <summary>
+        /// Provides easy access to the root frame of the Phone Application.
+        /// </summary>
+        /// <returns>The root frame of the Phone Application.</returns>
+        public PhoneApplicationFrame RootFrame { get; private set; }
+
+        /// <summary>
+        /// Constructor for the Application object.
+        /// </summary>
+        public App()
+        {
+            // Global handler for uncaught exceptions. 
+            UnhandledException += Application_UnhandledException;
+
+            // Show graphics profiling information while debugging.
+            if (System.Diagnostics.Debugger.IsAttached)
+            {
+                // Display the current frame rate counters.
+                //Application.Current.Host.Settings.EnableFrameRateCounter = true;
+
+                // Show the areas of the app that are being redrawn in each frame.
+                //Application.Current.Host.Settings.EnableRedrawRegions = true;
+
+                // Enable non-production analysis visualization mode, 
+                // which shows areas of a page that are being GPU accelerated with a colored overlay.
+                //Application.Current.Host.Settings.EnableCacheVisualization = true;
+            }
+
+            // Standard Silverlight initialization
+            InitializeComponent();
+
+            // Phone-specific initialization
+            InitializePhoneApplication();
+        }
+
+        // Code to execute when the application is launching (eg, from Start)
+        // This code will not execute when the application is reactivated
+        private void Application_Launching(object sender, LaunchingEventArgs e)
+        {
+        }
+
+        // Code to execute when the application is activated (brought to foreground)
+        // This code will not execute when the application is first launched
+        private void Application_Activated(object sender, ActivatedEventArgs e)
+        {
+        }
+
+        // Code to execute when the application is deactivated (sent to background)
+        // This code will not execute when the application is closing
+        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
+        {
+        }
+
+        // Code to execute when the application is closing (eg, user hit Back)
+        // This code will not execute when the application is deactivated
+        private void Application_Closing(object sender, ClosingEventArgs e)
+        {
+        }
+
+        // Code to execute if a navigation fails
+        private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
+        {
+            if (System.Diagnostics.Debugger.IsAttached)
+            {
+                // A navigation has failed; break into the debugger
+                System.Diagnostics.Debugger.Break();
+            }
+        }
+
+        // Code to execute on Unhandled Exceptions
+        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
+        {
+            if (System.Diagnostics.Debugger.IsAttached)
+            {
+                // An unhandled exception has occurred; break into the debugger
+                System.Diagnostics.Debugger.Break();
+            }
+        }
+
+        #region Phone application initialization
+
+        // Avoid double-initialization
+        private bool phoneApplicationInitialized = false;
+
+        // Do not add any additional code to this method
+        private void InitializePhoneApplication()
+        {
+            if (phoneApplicationInitialized)
+                return;
+
+            // Create the frame but don't set it as RootVisual yet; this allows the splash
+            // screen to remain active until the application is ready to render.
+            RootFrame = new PhoneApplicationFrame();
+            RootFrame.Navigated += CompleteInitializePhoneApplication;
+
+            // Handle navigation failures
+            RootFrame.NavigationFailed += RootFrame_NavigationFailed;
+
+            // Ensure we don't initialize again
+            phoneApplicationInitialized = true;
+        }
+
+        // Do not add any additional code to this method
+        private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
+        {
+            // Set the root visual to allow the application to render
+            if (RootVisual != RootFrame)
+                RootVisual = RootFrame;
+
+            // Remove this handler since it is no longer needed
+            RootFrame.Navigated -= CompleteInitializePhoneApplication;
+        }
+
+        #endregion
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/ApplicationIcon.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/ApplicationIcon.png b/lib/cordova-wp8/templates/standalone/ApplicationIcon.png
new file mode 100644
index 0000000..6b69046
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/ApplicationIcon.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Background.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Background.png b/lib/cordova-wp8/templates/standalone/Background.png
new file mode 100644
index 0000000..2667df4
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/Background.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/CordovaAppProj.csproj
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/CordovaAppProj.csproj b/lib/cordova-wp8/templates/standalone/CordovaAppProj.csproj
new file mode 100644
index 0000000..23e4eef
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/CordovaAppProj.csproj
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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. 
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>10.0.20506</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}</ProjectGuid>
+    <ProjectTypeGuids>{C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>$safeprojectname$</RootNamespace>
+    <AssemblyName>$safeprojectname$</AssemblyName>
+    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
+    <SilverlightVersion>
+    </SilverlightVersion>
+    <TargetFrameworkProfile>
+    </TargetFrameworkProfile>
+    <TargetFrameworkIdentifier>WindowsPhone</TargetFrameworkIdentifier>
+    <SilverlightApplication>true</SilverlightApplication>
+    <SupportedCultures>en-US</SupportedCultures>
+    <XapOutputs>true</XapOutputs>
+    <GenerateSilverlightManifest>true</GenerateSilverlightManifest>
+    <XapFilename>CordovaAppProj_$(Configuration)_$(Platform).xap</XapFilename>
+    <SilverlightManifestTemplate>Properties\AppManifest.xml</SilverlightManifestTemplate>
+    <SilverlightAppEntry>$safeprojectname$.App</SilverlightAppEntry>
+    <ValidateXaml>true</ValidateXaml>
+    <ThrowErrorsInValidation>true</ThrowErrorsInValidation>
+    <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+    <BackgroundAgentType />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>Bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>Bin\Release</OutputPath>
+    <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>Bin\x86\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>full</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet>
+    <Optimize>false</Optimize>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <OutputPath>Bin\x86\Release</OutputPath>
+    <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <Optimize>true</Optimize>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>Bin\ARM\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>full</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+    <Optimize>false</Optimize>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
+    <OutputPath>Bin\ARM\Release</OutputPath>
+    <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <Optimize>true</Optimize>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="App.xaml.cs">
+      <DependentUpon>App.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="cordovalib\BrowserMouseHelper.cs" />
+    <Compile Include="cordovalib\CommandFactory.cs" />
+    <Compile Include="cordovalib\Commands\BaseCommand.cs" />
+    <Compile Include="cordovalib\ConfigHandler.cs" />
+    <Compile Include="cordovalib\CordovaCommandCall.cs" />
+    <Compile Include="cordovalib\CordovaView.xaml.cs">
+      <DependentUpon>CordovaView.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="cordovalib\DOMStorageHelper.cs" />
+    <Compile Include="cordovalib\JSON\JsonHelper.cs" />
+    <Compile Include="cordovalib\NativeExecution.cs" />
+    <Compile Include="cordovalib\OrientationHelper.cs" />
+    <Compile Include="cordovalib\PluginResult.cs" />
+    <Compile Include="cordovalib\ScriptCallback.cs" />
+   
+    <Compile Include="MainPage.xaml.cs">
+      <DependentUpon>MainPage.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="App.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </ApplicationDefinition>
+    <Page Include="cordovalib\CordovaView.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+
+    <Page Include="Plugins\UI\AudioRecorder.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="Plugins\UI\ImageCapture.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="Plugins\UI\NotificationBox.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="Plugins\UI\VideoRecorder.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="MainPage.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="www\**" />
+    <Content Include="config.xml" />
+    <Content Include="Images\**" />
+
+    <Content Include="resources\notification-beep.wav" />
+    <None Include="VERSION" />
+
+    <None Include="Properties\AppManifest.xml">
+      <SubType>Designer</SubType>
+    </None>
+    <None Include="Properties\WMAppManifest.xml">
+      <SubType>Designer</SubType>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="ApplicationIcon.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Background.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="SplashScreenImage.jpg" />
+  </ItemGroup>
+  <ItemGroup>
+    <WCFMetadata Include="Service References\" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
+  </ItemGroup>
+  <ItemGroup>
+      <Compile Include="Plugins\*" />
+      <Compile Include="Plugins\UI\*.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).$(TargetFrameworkVersion).Overrides.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions />
+  <PropertyGroup>
+  </PropertyGroup>
+  <PropertyGroup>
+    <PostBuildEvent>
+    </PostBuildEvent>
+  </PropertyGroup>
+</Project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/CordovaSolution.sln
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/CordovaSolution.sln b/lib/cordova-wp8/templates/standalone/CordovaSolution.sln
new file mode 100644
index 0000000..58c0e62
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/CordovaSolution.sln
@@ -0,0 +1,30 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2012 for Windows Phone
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CordovaAppProj", "CordovaAppProj.csproj", "{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|ARM = Debug|ARM
+		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|ARM = Release|ARM
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Release|Any CPU.Deploy.0 = Release|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Release|ARM.ActiveCfg = Release|Any CPU
+		{3677C1B7-D68B-4CF9-BF8A-E869D437A6DF}.Release|x86.ActiveCfg = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Images/appbar.back.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Images/appbar.back.rest.png b/lib/cordova-wp8/templates/standalone/Images/appbar.back.rest.png
new file mode 100644
index 0000000..4bc2b92
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/Images/appbar.back.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Images/appbar.close.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Images/appbar.close.rest.png b/lib/cordova-wp8/templates/standalone/Images/appbar.close.rest.png
new file mode 100644
index 0000000..8166a1c
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/Images/appbar.close.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Images/appbar.feature.video.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Images/appbar.feature.video.rest.png b/lib/cordova-wp8/templates/standalone/Images/appbar.feature.video.rest.png
new file mode 100644
index 0000000..baff565
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/Images/appbar.feature.video.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Images/appbar.next.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Images/appbar.next.rest.png b/lib/cordova-wp8/templates/standalone/Images/appbar.next.rest.png
new file mode 100644
index 0000000..ed577d7
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/Images/appbar.next.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Images/appbar.save.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Images/appbar.save.rest.png b/lib/cordova-wp8/templates/standalone/Images/appbar.save.rest.png
new file mode 100644
index 0000000..d49940e
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/Images/appbar.save.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Images/appbar.stop.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Images/appbar.stop.rest.png b/lib/cordova-wp8/templates/standalone/Images/appbar.stop.rest.png
new file mode 100644
index 0000000..4dd724f
Binary files /dev/null and b/lib/cordova-wp8/templates/standalone/Images/appbar.stop.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/MainPage.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/MainPage.xaml b/lib/cordova-wp8/templates/standalone/MainPage.xaml
new file mode 100644
index 0000000..db0d8e6
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/MainPage.xaml
@@ -0,0 +1,53 @@
+<!--
+ 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. 
+-->
+<phone:PhoneApplicationPage 
+    x:Class="$safeprojectname$.MainPage"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d" FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    Background="Black"
+    SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
+    shell:SystemTray.IsVisible="True" d:DesignHeight="768" d:DesignWidth="480" 
+    xmlns:my="clr-namespace:WPCordovaClassLib">
+    <Grid x:Name="LayoutRoot" Background="Transparent" HorizontalAlignment="Stretch">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+        <my:CordovaView HorizontalAlignment="Stretch" 
+                   Margin="0,0,0,0"  
+                   x:Name="CordovaView" 
+                   VerticalAlignment="Stretch" />
+        <Image Source="SplashScreenImage.jpg"
+          x:Name="SplashImage"
+          VerticalAlignment="Center"
+          HorizontalAlignment="Stretch">
+            <Image.Projection>
+                <PlaneProjection x:Name="SplashProjector"  CenterOfRotationX="0"/>
+            </Image.Projection>
+        </Image>
+    </Grid>
+
+</phone:PhoneApplicationPage>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/MainPage.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/MainPage.xaml.cs b/lib/cordova-wp8/templates/standalone/MainPage.xaml.cs
new file mode 100644
index 0000000..b9ec7e1
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/MainPage.xaml.cs
@@ -0,0 +1,72 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+using System.IO;
+using System.Windows.Media.Imaging;
+using System.Windows.Resources;
+
+
+namespace $safeprojectname$
+{
+    public partial class MainPage : PhoneApplicationPage
+    {
+        // Constructor
+        public MainPage()
+        {
+            InitializeComponent();
+            this.CordovaView.Loaded += CordovaView_Loaded;
+        }
+
+        private void CordovaView_Loaded(object sender, RoutedEventArgs e)
+        {
+            this.CordovaView.Loaded -= CordovaView_Loaded;
+            // first time load will have an animation
+            Storyboard _storyBoard = new Storyboard();
+            DoubleAnimation animation = new DoubleAnimation()
+            {
+                From = 0,
+                Duration = TimeSpan.FromSeconds(0.6),
+                To = 90
+            };
+            Storyboard.SetTarget(animation, SplashProjector);
+            Storyboard.SetTargetProperty(animation, new PropertyPath("RotationY"));
+            _storyBoard.Children.Add(animation);
+            _storyBoard.Begin();
+            _storyBoard.Completed += Splash_Completed;
+        }
+
+        void Splash_Completed(object sender, EventArgs e)
+        {
+            (sender as Storyboard).Completed -= Splash_Completed;
+            LayoutRoot.Children.Remove(SplashImage);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/Accelerometer.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/Accelerometer.cs b/lib/cordova-wp8/templates/standalone/Plugins/Accelerometer.cs
new file mode 100644
index 0000000..cba911c
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/Accelerometer.cs
@@ -0,0 +1,196 @@
+/*  
+	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.
+*/
+
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Threading;
+using Microsoft.Devices.Sensors;
+using System.Globalization;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Captures device motion in the x, y, and z direction.
+    /// </summary>
+    public class Accelerometer : BaseCommand
+    {
+        #region AccelerometerOptions class
+        /// <summary>
+        /// Represents Accelerometer options.
+        /// </summary>
+        [DataContract]
+        public class AccelerometerOptions
+        {
+            /// <summary>
+            /// How often to retrieve the Acceleration in milliseconds
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "frequency")]
+            public int Frequency { get; set; }
+
+            /// <summary>
+            /// Watcher id
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "id")]
+            public string Id { get; set; }
+
+            /// <summary>
+            /// Creates options object with default parameters
+            /// </summary>
+            public AccelerometerOptions()
+            {
+                this.SetDefaultValues(new StreamingContext());
+            }
+
+            /// <summary>
+            /// Initializes default values for class fields.
+            /// Implemented in separate method because default constructor is not invoked during deserialization.
+            /// </summary>
+            /// <param name="context"></param>
+            [OnDeserializing()]
+            public void SetDefaultValues(StreamingContext context)
+            {
+                this.Frequency = 10000;
+            }
+        }
+
+        #endregion
+
+        #region Status codes and Constants
+
+        public const int Stopped = 0;
+        public const int Starting = 1;
+        public const int Running = 2;
+        public const int ErrorFailedToStart = 3;
+
+        public const double gConstant = -9.81;
+
+        #endregion
+
+        #region Static members
+
+        /// <summary>
+        /// Status of listener
+        /// </summary>
+        private static int currentStatus;
+
+        /// <summary>
+        /// Accelerometer
+        /// </summary>
+        private static Microsoft.Devices.Sensors.Accelerometer accelerometer = new Microsoft.Devices.Sensors.Accelerometer();
+
+        private static DateTime StartOfEpoch = new DateTime(1970, 1, 1, 0, 0, 0);
+
+        #endregion
+
+        /// <summary>
+        /// Sensor listener event
+        /// </summary>        
+        private void accelerometer_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
+        {
+            this.SetStatus(Running);
+
+            PluginResult result = new PluginResult(PluginResult.Status.OK, GetCurrentAccelerationFormatted());
+            result.KeepCallback = true;
+            DispatchCommandResult(result);
+        }
+
+        /// <summary>
+        /// Starts listening for acceleration sensor
+        /// </summary>
+        /// <returns>status of listener</returns>
+        public void start(string options)
+        {
+            if ((currentStatus == Running) || (currentStatus == Starting))
+            {
+                return;
+            }
+            try
+            {
+                lock (accelerometer)
+                {
+                    accelerometer.CurrentValueChanged += accelerometer_CurrentValueChanged;
+                    accelerometer.Start();
+                    this.SetStatus(Starting);
+                }
+
+                long timeout = 2000;
+                while ((currentStatus == Starting) && (timeout > 0))
+                {
+                    timeout = timeout - 100;
+                    Thread.Sleep(100);
+                }
+
+                if (currentStatus != Running)
+                {
+                    this.SetStatus(ErrorFailedToStart);
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, ErrorFailedToStart));
+                    return;
+                }
+            }
+            catch (Exception)
+            {
+                this.SetStatus(ErrorFailedToStart);
+                DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, ErrorFailedToStart));
+                return;
+            }
+            PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
+            result.KeepCallback = true;
+            DispatchCommandResult(result);
+        }
+
+        public void stop(string options)
+        {
+            if (currentStatus == Running)
+            {
+                lock (accelerometer)
+                {
+                    accelerometer.CurrentValueChanged -= accelerometer_CurrentValueChanged;
+                    accelerometer.Stop();
+                    this.SetStatus(Stopped);
+                }
+            }
+            DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+        }
+
+        /// <summary>
+        /// Formats current coordinates into JSON format
+        /// </summary>
+        /// <returns>Coordinates in JSON format</returns>
+        private string GetCurrentAccelerationFormatted()
+        {
+            // convert to unix timestamp
+            // long timestamp = ((accelerometer.CurrentValue.Timestamp.DateTime - StartOfEpoch).Ticks) / 10000;
+            // Note: Removed timestamp, to let the JS side create it using (new Date().getTime()) -jm
+            // this resolves an issue with inconsistencies between JS dates and Native DateTime 
+            string resultCoordinates = String.Format("\"x\":{0},\"y\":{1},\"z\":{2}",
+                            (accelerometer.CurrentValue.Acceleration.X * gConstant).ToString("0.00000", CultureInfo.InvariantCulture),
+                            (accelerometer.CurrentValue.Acceleration.Y * gConstant).ToString("0.00000", CultureInfo.InvariantCulture),
+                            (accelerometer.CurrentValue.Acceleration.Z * gConstant).ToString("0.00000", CultureInfo.InvariantCulture));
+            return  "{" + resultCoordinates + "}";
+        }
+
+        /// <summary>
+        /// Sets current status
+        /// </summary>
+        /// <param name="status">current status</param>
+        private void SetStatus(int status)
+        {
+            currentStatus = status;
+        }
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/AudioFormatsHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/AudioFormatsHelper.cs b/lib/cordova-wp8/templates/standalone/Plugins/AudioFormatsHelper.cs
new file mode 100644
index 0000000..dca7ee6
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/AudioFormatsHelper.cs
@@ -0,0 +1,89 @@
+/*  
+	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.
+
+ */
+
+using System;
+using System.IO;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Provides extra functionality to support different audio formats.
+    /// </summary>
+    public static class AudioFormatsHelper
+    {
+        #region Wav
+        /// <summary>
+        /// Adds wav file format header to the stream
+        /// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
+        /// </summary>
+        /// <param name="stream">The stream</param>
+        /// <param name="sampleRate">Sample Rate</param>
+        public static void InitializeWavStream(this Stream stream, int sampleRate)
+        {
+            #region args checking
+
+            if (stream == null) 
+            {
+                throw new ArgumentNullException("stream can't be null or empty");
+            }
+
+            #endregion
+
+            int numBits = 16;
+            int numBytes = numBits / 8;
+
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("RIFF"), 0, 4);
+            stream.Write(BitConverter.GetBytes(0), 0, 4);
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("WAVE"), 0, 4);
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("fmt "), 0, 4);
+            stream.Write(BitConverter.GetBytes(16), 0, 4);
+            stream.Write(BitConverter.GetBytes((short)1), 0, 2);
+            stream.Write(BitConverter.GetBytes((short)1), 0, 2);
+            stream.Write(BitConverter.GetBytes(sampleRate), 0, 4);
+            stream.Write(BitConverter.GetBytes(sampleRate * numBytes), 0, 4);
+            stream.Write(BitConverter.GetBytes((short)(numBytes)), 0, 2);
+            stream.Write(BitConverter.GetBytes((short)(numBits)), 0, 2);
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("data"), 0, 4);
+            stream.Write(BitConverter.GetBytes(0), 0, 4);
+        }
+
+        /// <summary>
+        /// Updates wav file format header
+        /// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
+        /// </summary>
+        /// <param name="stream">Wav stream</param>
+        public static void UpdateWavStream(this Stream stream)
+        {
+            #region args checking
+
+            if (stream == null)
+            {
+                throw new ArgumentNullException("stream can't be null or empty");
+            }
+
+            #endregion
+
+            var position = stream.Position;
+
+            stream.Seek(4, SeekOrigin.Begin);
+            stream.Write(BitConverter.GetBytes((int)stream.Length - 8), 0, 4);
+            stream.Seek(40, SeekOrigin.Begin);
+            stream.Write(BitConverter.GetBytes((int)stream.Length - 44), 0, 4);
+            stream.Seek(position, SeekOrigin.Begin);
+        }
+
+        #endregion
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/AudioPlayer.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/AudioPlayer.cs b/lib/cordova-wp8/templates/standalone/Plugins/AudioPlayer.cs
new file mode 100644
index 0000000..a83be5b
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/AudioPlayer.cs
@@ -0,0 +1,620 @@
+/*  
+	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.
+*/
+
+using System;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Threading;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.Media;
+using Microsoft.Phone.Controls;
+using System.Diagnostics;
+using System.Windows.Resources;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Implements audio record and play back functionality.
+    /// </summary>
+    internal class AudioPlayer : IDisposable
+    {
+        #region Constants
+
+        // AudioPlayer states
+        private const int PlayerState_None = 0;
+        private const int PlayerState_Starting = 1;
+        private const int PlayerState_Running = 2;
+        private const int PlayerState_Paused = 3;
+        private const int PlayerState_Stopped = 4;
+
+        // AudioPlayer messages
+        private const int MediaState = 1;
+        private const int MediaDuration = 2;
+        private const int MediaPosition = 3;
+        private const int MediaError = 9;
+
+        // AudioPlayer errors
+        private const int MediaErrorPlayModeSet = 1;
+        private const int MediaErrorAlreadyRecording = 2;
+        private const int MediaErrorStartingRecording = 3;
+        private const int MediaErrorRecordModeSet = 4;
+        private const int MediaErrorStartingPlayback = 5;
+        private const int MediaErrorResumeState = 6;
+        private const int MediaErrorPauseState = 7;
+        private const int MediaErrorStopState = 8;
+
+        //TODO: get rid of this callback, it should be universal
+        //private const string CallbackFunction = "CordovaMediaonStatus";
+
+        #endregion
+
+        /// <summary>
+        /// The AudioHandler object
+        /// </summary>
+        private Media handler;
+
+        /// <summary>
+        /// Temporary buffer to store audio chunk
+        /// </summary>
+        private byte[] buffer;
+
+        /// <summary>
+        /// Xna game loop dispatcher
+        /// </summary>
+        DispatcherTimer dtXna;
+
+        /// <summary>
+        /// Output buffer
+        /// </summary>
+        private MemoryStream memoryStream;
+
+        /// <summary>
+        /// The id of this player (used to identify Media object in JavaScript)
+        /// </summary>
+        private String id;
+
+        /// <summary>
+        /// State of recording or playback
+        /// </summary>
+        private int state = PlayerState_None;
+
+        /// <summary>
+        /// File name to play or record to
+        /// </summary>
+        private String audioFile = null;
+
+        /// <summary>
+        /// Duration of audio
+        /// </summary>
+        private double duration = -1;
+
+        /// <summary>
+        /// Audio player object
+        /// </summary>
+        private MediaElement player = null;
+
+        /// <summary>
+        /// Audio source
+        /// </summary>
+        private Microphone recorder;
+
+        /// <summary>
+        /// Internal flag specified that we should only open audio w/o playing it
+        /// </summary>
+        private bool prepareOnly = false;
+
+        /// <summary>
+        /// Creates AudioPlayer instance
+        /// </summary>
+        /// <param name="handler">Media object</param>
+        /// <param name="id">player id</param>
+        public AudioPlayer(Media handler, String id)
+        {
+            this.handler = handler;
+            this.id = id;
+        }
+
+        /// <summary>
+        /// Destroys player and stop audio playing or recording
+        /// </summary>
+        public void Dispose()
+        {
+            if (this.player != null)
+            {
+                this.stopPlaying();
+                this.player = null;
+            }
+            if (this.recorder != null)
+            {
+                this.stopRecording();
+                this.recorder = null;
+            }
+
+            this.FinalizeXnaGameLoop();
+        }
+
+        private void InvokeCallback(int message, string value, bool removeHandler)
+        {
+            string args = string.Format("('{0}',{1},{2});", this.id, message, value);
+            string callback = @"(function(id,msg,value){
+                try {
+                    if (msg == Media.MEDIA_ERROR) {
+                        value = {'code':value};
+                    }
+                    Media.onStatus(id,msg,value);
+                }
+                catch(e) {
+                    console.log('Error calling Media.onStatus :: ' + e);
+                }
+            })" + args;
+            this.handler.InvokeCustomScript(new ScriptCallback("eval", new string[] { callback }), false);
+        }
+
+        private void InvokeCallback(int message, int value, bool removeHandler)
+        {
+            InvokeCallback(message, value.ToString(), removeHandler);
+        }
+
+        private void InvokeCallback(int message, double value, bool removeHandler)
+        {
+            InvokeCallback(message, value.ToString(), removeHandler);
+        }
+
+        /// <summary>
+        /// Starts recording, data is stored in memory
+        /// </summary>
+        /// <param name="filePath"></param>
+        public void startRecording(string filePath)
+        {
+            if (this.player != null)
+            {
+                InvokeCallback(MediaError, MediaErrorPlayModeSet, false);
+            }
+            else if (this.recorder == null)
+            {
+                try
+                {
+                    this.audioFile = filePath;
+                    this.InitializeXnaGameLoop();
+                    this.recorder = Microphone.Default;
+                    this.recorder.BufferDuration = TimeSpan.FromMilliseconds(500);
+                    this.buffer = new byte[recorder.GetSampleSizeInBytes(this.recorder.BufferDuration)];
+                    this.recorder.BufferReady += new EventHandler<EventArgs>(recorderBufferReady);
+                    this.memoryStream = new MemoryStream();
+                    this.memoryStream.InitializeWavStream(this.recorder.SampleRate);
+                    this.recorder.Start();
+                    FrameworkDispatcher.Update();
+                    this.SetState(PlayerState_Running);
+                }
+                catch (Exception)
+                {
+                    InvokeCallback(MediaError, MediaErrorStartingRecording, false);
+                    //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStartingRecording),false);
+                }
+            }
+            else
+            {
+                InvokeCallback(MediaError, MediaErrorAlreadyRecording, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorAlreadyRecording),false);
+            }
+        }
+
+        /// <summary>
+        /// Stops recording
+        /// </summary>
+        public void stopRecording()
+        {
+            if (this.recorder != null)
+            {
+                if (this.state == PlayerState_Running)
+                {
+                    try
+                    {
+                        this.recorder.Stop();
+                        this.recorder.BufferReady -= recorderBufferReady;
+                        this.recorder = null;
+                        SaveAudioClipToLocalStorage();
+                        this.FinalizeXnaGameLoop();
+                        this.SetState(PlayerState_Stopped);
+                    }
+                    catch (Exception)
+                    {
+                        //TODO 
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Starts or resume playing audio file
+        /// </summary>
+        /// <param name="filePath">The name of the audio file</param>
+        /// <summary>
+        /// Starts or resume playing audio file
+        /// </summary>
+        /// <param name="filePath">The name of the audio file</param>
+        public void startPlaying(string filePath)
+        {
+            if (this.recorder != null)
+            {
+                InvokeCallback(MediaError, MediaErrorRecordModeSet, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorRecordModeSet),false);
+                return;
+            }
+
+
+            if (this.player == null || this.player.Source.AbsolutePath.LastIndexOf(filePath) < 0)
+            {
+                try
+                {
+                    // this.player is a MediaElement, it must be added to the visual tree in order to play
+                    PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                    if (frame != null)
+                    {
+                        PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+                        if (page != null)
+                        {
+                            Grid grid = page.FindName("LayoutRoot") as Grid;
+                            if (grid != null)
+                            {
+
+                                this.player = grid.FindName("playerMediaElement") as MediaElement;
+                                if (this.player == null) // still null ?
+                                {
+                                    this.player = new MediaElement();
+                                    this.player.Name = "playerMediaElement";
+                                    grid.Children.Add(this.player);
+                                    this.player.Visibility = Visibility.Visible;
+                                }
+                                if (this.player.CurrentState == System.Windows.Media.MediaElementState.Playing)
+                                {
+                                    this.player.Stop(); // stop it!
+                                }
+                                
+                                this.player.Source = null; // Garbage collect it.
+                                this.player.MediaOpened += MediaOpened;
+                                this.player.MediaEnded += MediaEnded;
+                                this.player.MediaFailed += MediaFailed;
+                            }
+                        }
+                    }
+
+                    this.audioFile = filePath;
+
+                    Uri uri = new Uri(filePath, UriKind.RelativeOrAbsolute);
+                    if (uri.IsAbsoluteUri)
+                    {
+                        this.player.Source = uri;
+                    }
+                    else
+                    {
+                        using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                        {
+                            if (!isoFile.FileExists(filePath))
+                            {
+                                // try to unpack it from the dll into isolated storage
+                                StreamResourceInfo fileResourceStreamInfo = Application.GetResourceStream(new Uri(filePath, UriKind.Relative));
+                                if (fileResourceStreamInfo != null)
+                                {
+                                    using (BinaryReader br = new BinaryReader(fileResourceStreamInfo.Stream))
+                                    {
+                                        byte[] data = br.ReadBytes((int)fileResourceStreamInfo.Stream.Length);          
+
+                                        string[] dirParts = filePath.Split('/');
+                                        string dirName = "";
+                                        for (int n = 0; n < dirParts.Length - 1; n++)
+                                        {
+                                            dirName += dirParts[n] + "/";
+                                        }
+                                        if (!isoFile.DirectoryExists(dirName))
+                                        {
+                                            isoFile.CreateDirectory(dirName);
+                                        }
+
+                                        using (IsolatedStorageFileStream outFile = isoFile.OpenFile(filePath, FileMode.Create))
+                                        {
+                                            using (BinaryWriter writer = new BinaryWriter(outFile))
+                                            {
+                                                writer.Write(data);
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                            if (isoFile.FileExists(filePath))
+                            {
+                                using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, isoFile))
+                                {
+                                    this.player.SetSource(stream);
+                                }
+                            }
+                            else
+                            {
+                                InvokeCallback(MediaError, MediaErrorPlayModeSet, false);
+                                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, 1), false);
+                                return;
+                            }
+                        }
+                    }
+                    this.SetState(PlayerState_Starting);
+                }
+                catch (Exception e)
+                {
+                    Debug.WriteLine("Error in AudioPlayer::startPlaying : " + e.Message);
+                    InvokeCallback(MediaError, MediaErrorStartingPlayback, false);
+                    //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStartingPlayback),false);
+                }
+            }
+            else
+            {
+                if (this.state != PlayerState_Running)
+                {
+                    this.player.Play();
+                    this.SetState(PlayerState_Running);
+                }
+                else
+                {
+                    InvokeCallback(MediaError, MediaErrorResumeState, false);
+                    //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorResumeState),false);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Callback to be invoked when the media source is ready for playback
+        /// </summary>
+        private void MediaOpened(object sender, RoutedEventArgs arg)
+        {
+            if (this.player != null)
+            {
+                this.duration = this.player.NaturalDuration.TimeSpan.TotalSeconds;
+                InvokeCallback(MediaDuration, this.duration, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaDuration, this.duration),false);
+                if (!this.prepareOnly)
+                {
+                    this.player.Play();
+                    this.SetState(PlayerState_Running);
+                }
+                this.prepareOnly = false;
+            }
+            else
+            {
+                // TODO: occasionally MediaOpened is signalled, but player is null
+            }
+        }
+
+        /// <summary>
+        /// Callback to be invoked when playback of a media source has completed
+        /// </summary>
+        private void MediaEnded(object sender, RoutedEventArgs arg)
+        {
+            this.SetState(PlayerState_Stopped);
+        }
+
+        /// <summary>
+        /// Callback to be invoked when playback of a media source has failed
+        /// </summary>
+        private void MediaFailed(object sender, RoutedEventArgs arg)
+        {
+            player.Stop();
+            InvokeCallback(MediaError, MediaErrorStartingPlayback, false);
+            //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError.ToString(), "Media failed"),false);
+        }
+
+        /// <summary>
+        /// Seek or jump to a new time in the track
+        /// </summary>
+        /// <param name="milliseconds">The new track position</param>
+        public void seekToPlaying(int milliseconds)
+        {
+            if (this.player != null)
+            {
+                TimeSpan tsPos = new TimeSpan(0, 0, 0, 0, milliseconds);
+                this.player.Position = tsPos;
+                InvokeCallback(MediaPosition, milliseconds / 1000.0f, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaPosition, milliseconds / 1000.0f),false);
+            }
+        }
+
+        /// <summary>
+        /// Set the volume of the player
+        /// </summary>
+        /// <param name="vol">volume 0.0-1.0, default value is 0.5</param>
+        public void setVolume(double vol)
+        {
+            if (this.player != null)
+            {
+                this.player.Volume = vol;
+            }
+        }
+
+        /// <summary>
+        /// Pauses playing
+        /// </summary>
+        public void pausePlaying()
+        {
+            if (this.state == PlayerState_Running)
+            {
+                this.player.Pause();
+                this.SetState(PlayerState_Paused);
+            }
+            else
+            {
+                InvokeCallback(MediaError, MediaErrorPauseState, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorPauseState),false);
+            }
+        }
+
+
+        /// <summary>
+        /// Stops playing the audio file
+        /// </summary>
+        public void stopPlaying()
+        {
+            if ((this.state == PlayerState_Running) || (this.state == PlayerState_Paused))
+            {
+                this.player.Stop();
+
+                this.player.Position = new TimeSpan(0L);
+                this.SetState(PlayerState_Stopped);
+            }
+            //else // Why is it an error to call stop on a stopped media?
+            //{
+            //    this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStopState), false);
+            //}
+        }
+
+        /// <summary>
+        /// Gets current position of playback
+        /// </summary>
+        /// <returns>current position</returns>
+        public double getCurrentPosition()
+        {
+            if ((this.state == PlayerState_Running) || (this.state == PlayerState_Paused))
+            {
+                double currentPosition = this.player.Position.TotalSeconds;
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaPosition, currentPosition),false);
+                return currentPosition;
+            }
+            else
+            {
+                return 0;
+            }
+        }
+
+        /// <summary>
+        /// Gets the duration of the audio file
+        /// </summary>
+        /// <param name="filePath">The name of the audio file</param>
+        /// <returns>track duration</returns>
+        public double getDuration(string filePath)
+        {
+            if (this.recorder != null)
+            {
+                return (-2);
+            }
+
+            if (this.player != null)
+            {
+                return this.duration;
+
+            }
+            else
+            {
+                this.prepareOnly = true;
+                this.startPlaying(filePath);
+                return this.duration;
+            }
+        }
+
+        /// <summary>
+        /// Sets the state and send it to JavaScript
+        /// </summary>
+        /// <param name="state">state</param>
+        private void SetState(int state)
+        {
+            if (this.state != state)
+            {
+                InvokeCallback(MediaState, state, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaState, state),false);
+            }
+
+            this.state = state;
+        }
+
+        #region record methods
+
+        /// <summary>
+        /// Copies data from recorder to memory storages and updates recording state
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void recorderBufferReady(object sender, EventArgs e)
+        {
+            this.recorder.GetData(this.buffer);
+            this.memoryStream.Write(this.buffer, 0, this.buffer.Length);
+        }
+
+        /// <summary>
+        /// Writes audio data from memory to isolated storage
+        /// </summary>
+        /// <returns></returns>
+        private void SaveAudioClipToLocalStorage()
+        {
+            if (this.memoryStream == null || this.memoryStream.Length <= 0)
+            {
+                return;
+            }
+
+            this.memoryStream.UpdateWavStream();
+
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    string directory = Path.GetDirectoryName(audioFile);
+
+                    if (!isoFile.DirectoryExists(directory))
+                    {
+                        isoFile.CreateDirectory(directory);
+                    }
+
+                    this.memoryStream.Seek(0, SeekOrigin.Begin);
+
+                    using (IsolatedStorageFileStream fileStream = isoFile.CreateFile(audioFile))
+                    {
+                        this.memoryStream.CopyTo(fileStream);
+                    }
+                }
+            }
+            catch (Exception)
+            {
+                //TODO: log or do something else
+                throw;
+            }
+        }    
+
+        #region Xna loop
+        /// <summary>
+        /// Special initialization required for the microphone: XNA game loop
+        /// </summary>
+        private void InitializeXnaGameLoop()
+        {
+            // Timer to simulate the XNA game loop (Microphone is from XNA)
+            this.dtXna = new DispatcherTimer();
+            this.dtXna.Interval = TimeSpan.FromMilliseconds(33);
+            this.dtXna.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
+            this.dtXna.Start();
+        }
+        /// <summary>
+        /// Finalizes XNA game loop for microphone
+        /// </summary>
+        private void FinalizeXnaGameLoop()
+        {
+            // Timer to simulate the XNA game loop (Microphone is from XNA)
+            if (this.dtXna != null)
+            {
+                this.dtXna.Stop();
+                this.dtXna = null;
+            }
+        }
+
+        #endregion
+
+        #endregion
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/Battery.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/Battery.cs b/lib/cordova-wp8/templates/standalone/Plugins/Battery.cs
new file mode 100644
index 0000000..962959e
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/Battery.cs
@@ -0,0 +1,79 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+
+using Microsoft.Phone.Info;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Listens for changes to the state of the battery on the device.
+    /// Currently only the "isPlugged" parameter available via native APIs.
+    /// </summary>
+    public class Battery : BaseCommand
+    {
+        private bool isPlugged = false;
+        private EventHandler powerChanged;
+
+        public Battery()
+        {
+            powerChanged = new EventHandler(DeviceStatus_PowerSourceChanged);
+            isPlugged = DeviceStatus.PowerSource.ToString().CompareTo("External") == 0;
+        }
+
+        public void start(string options)
+        {
+            // Register power changed event handler
+            DeviceStatus.PowerSourceChanged += powerChanged;
+
+            PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
+            result.KeepCallback = true;
+            DispatchCommandResult(result);
+        }
+        public void stop(string options)
+        {
+            // Unregister power changed event handler
+            DeviceStatus.PowerSourceChanged -= powerChanged;
+        }
+
+        private void DeviceStatus_PowerSourceChanged(object sender, EventArgs e)
+        {
+            isPlugged = DeviceStatus.PowerSource.ToString().CompareTo("External") == 0;
+            PluginResult result = new PluginResult(PluginResult.Status.OK, GetCurrentBatteryStateFormatted());
+            result.KeepCallback = true;
+            DispatchCommandResult(result);
+        }
+
+        private string GetCurrentBatteryStateFormatted()
+        {
+            string batteryState = String.Format("\"level\":{0},\"isPlugged\":{1}",
+                                                    "null",
+                                                    isPlugged ? "true" : "false"
+                            );
+            batteryState = "{" + batteryState + "}";
+            return batteryState;
+        }
+
+    }
+}


[37/50] Add Windows support to Android platform-scripts.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/framework/assets/js/cordova.android.js
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/assets/js/cordova.android.js b/lib/cordova-android/framework/assets/js/cordova.android.js
deleted file mode 100644
index ec1a2de..0000000
--- a/lib/cordova-android/framework/assets/js/cordova.android.js
+++ /dev/null
@@ -1,6836 +0,0 @@
-// Platform: android
-
-// commit cd29cf0f224ccf25e9d422a33fd02ef67d3a78f4
-
-// File generated at :: Thu Apr 25 2013 14:53:10 GMT-0700 (PDT)
-
-/*
- 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.
-*/
-
-;(function() {
-
-// file: lib/scripts/require.js
-
-var require,
-    define;
-
-(function () {
-    var modules = {};
-    // Stack of moduleIds currently being built.
-    var requireStack = [];
-    // Map of module ID -> index into requireStack of modules currently being built.
-    var inProgressModules = {};
-
-    function build(module) {
-        var factory = module.factory;
-        module.exports = {};
-        delete module.factory;
-        factory(require, module.exports, module);
-        return module.exports;
-    }
-
-    require = function (id) {
-        if (!modules[id]) {
-            throw "module " + id + " not found";
-        } else if (id in inProgressModules) {
-            var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;
-            throw "Cycle in require graph: " + cycle;
-        }
-        if (modules[id].factory) {
-            try {
-                inProgressModules[id] = requireStack.length;
-                requireStack.push(id);
-                return build(modules[id]);
-            } finally {
-                delete inProgressModules[id];
-                requireStack.pop();
-            }
-        }
-        return modules[id].exports;
-    };
-
-    define = function (id, factory) {
-        if (modules[id]) {
-            throw "module " + id + " already defined";
-        }
-
-        modules[id] = {
-            id: id,
-            factory: factory
-        };
-    };
-
-    define.remove = function (id) {
-        delete modules[id];
-    };
-
-    define.moduleMap = modules;
-})();
-
-//Export for use in node
-if (typeof module === "object" && typeof require === "function") {
-    module.exports.require = require;
-    module.exports.define = define;
-}
-
-// file: lib/cordova.js
-define("cordova", function(require, exports, module) {
-
-
-var channel = require('cordova/channel');
-
-/**
- * Listen for DOMContentLoaded and notify our channel subscribers.
- */
-document.addEventListener('DOMContentLoaded', function() {
-    channel.onDOMContentLoaded.fire();
-}, false);
-if (document.readyState == 'complete' || document.readyState == 'interactive') {
-    channel.onDOMContentLoaded.fire();
-}
-
-/**
- * Intercept calls to addEventListener + removeEventListener and handle deviceready,
- * resume, and pause events.
- */
-var m_document_addEventListener = document.addEventListener;
-var m_document_removeEventListener = document.removeEventListener;
-var m_window_addEventListener = window.addEventListener;
-var m_window_removeEventListener = window.removeEventListener;
-
-/**
- * Houses custom event handlers to intercept on document + window event listeners.
- */
-var documentEventHandlers = {},
-    windowEventHandlers = {};
-
-document.addEventListener = function(evt, handler, capture) {
-    var e = evt.toLowerCase();
-    if (typeof documentEventHandlers[e] != 'undefined') {
-        documentEventHandlers[e].subscribe(handler);
-    } else {
-        m_document_addEventListener.call(document, evt, handler, capture);
-    }
-};
-
-window.addEventListener = function(evt, handler, capture) {
-    var e = evt.toLowerCase();
-    if (typeof windowEventHandlers[e] != 'undefined') {
-        windowEventHandlers[e].subscribe(handler);
-    } else {
-        m_window_addEventListener.call(window, evt, handler, capture);
-    }
-};
-
-document.removeEventListener = function(evt, handler, capture) {
-    var e = evt.toLowerCase();
-    // If unsubscribing from an event that is handled by a plugin
-    if (typeof documentEventHandlers[e] != "undefined") {
-        documentEventHandlers[e].unsubscribe(handler);
-    } else {
-        m_document_removeEventListener.call(document, evt, handler, capture);
-    }
-};
-
-window.removeEventListener = function(evt, handler, capture) {
-    var e = evt.toLowerCase();
-    // If unsubscribing from an event that is handled by a plugin
-    if (typeof windowEventHandlers[e] != "undefined") {
-        windowEventHandlers[e].unsubscribe(handler);
-    } else {
-        m_window_removeEventListener.call(window, evt, handler, capture);
-    }
-};
-
-function createEvent(type, data) {
-    var event = document.createEvent('Events');
-    event.initEvent(type, false, false);
-    if (data) {
-        for (var i in data) {
-            if (data.hasOwnProperty(i)) {
-                event[i] = data[i];
-            }
-        }
-    }
-    return event;
-}
-
-if(typeof window.console === "undefined") {
-    window.console = {
-        log:function(){}
-    };
-}
-
-var cordova = {
-    define:define,
-    require:require,
-    /**
-     * Methods to add/remove your own addEventListener hijacking on document + window.
-     */
-    addWindowEventHandler:function(event) {
-        return (windowEventHandlers[event] = channel.create(event));
-    },
-    addStickyDocumentEventHandler:function(event) {
-        return (documentEventHandlers[event] = channel.createSticky(event));
-    },
-    addDocumentEventHandler:function(event) {
-        return (documentEventHandlers[event] = channel.create(event));
-    },
-    removeWindowEventHandler:function(event) {
-        delete windowEventHandlers[event];
-    },
-    removeDocumentEventHandler:function(event) {
-        delete documentEventHandlers[event];
-    },
-    /**
-     * Retrieve original event handlers that were replaced by Cordova
-     *
-     * @return object
-     */
-    getOriginalHandlers: function() {
-        return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
-        'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
-    },
-    /**
-     * Method to fire event from native code
-     * bNoDetach is required for events which cause an exception which needs to be caught in native code
-     */
-    fireDocumentEvent: function(type, data, bNoDetach) {
-        var evt = createEvent(type, data);
-        if (typeof documentEventHandlers[type] != 'undefined') {
-            if( bNoDetach ) {
-              documentEventHandlers[type].fire(evt);
-            }
-            else {
-              setTimeout(function() {
-                  // Fire deviceready on listeners that were registered before cordova.js was loaded.
-                  if (type == 'deviceready') {
-                      document.dispatchEvent(evt);
-                  }
-                  documentEventHandlers[type].fire(evt);
-              }, 0);
-            }
-        } else {
-            document.dispatchEvent(evt);
-        }
-    },
-    fireWindowEvent: function(type, data) {
-        var evt = createEvent(type,data);
-        if (typeof windowEventHandlers[type] != 'undefined') {
-            setTimeout(function() {
-                windowEventHandlers[type].fire(evt);
-            }, 0);
-        } else {
-            window.dispatchEvent(evt);
-        }
-    },
-
-    /**
-     * Plugin callback mechanism.
-     */
-    // Randomize the starting callbackId to avoid collisions after refreshing or navigating.
-    // This way, it's very unlikely that any new callback would get the same callbackId as an old callback.
-    callbackId: Math.floor(Math.random() * 2000000000),
-    callbacks:  {},
-    callbackStatus: {
-        NO_RESULT: 0,
-        OK: 1,
-        CLASS_NOT_FOUND_EXCEPTION: 2,
-        ILLEGAL_ACCESS_EXCEPTION: 3,
-        INSTANTIATION_EXCEPTION: 4,
-        MALFORMED_URL_EXCEPTION: 5,
-        IO_EXCEPTION: 6,
-        INVALID_ACTION: 7,
-        JSON_EXCEPTION: 8,
-        ERROR: 9
-    },
-
-    /**
-     * Called by native code when returning successful result from an action.
-     */
-    callbackSuccess: function(callbackId, args) {
-        try {
-            cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
-        } catch (e) {
-            console.log("Error in error callback: " + callbackId + " = "+e);
-        }
-    },
-
-    /**
-     * Called by native code when returning error result from an action.
-     */
-    callbackError: function(callbackId, args) {
-        // TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative.
-        // Derive success from status.
-        try {
-            cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
-        } catch (e) {
-            console.log("Error in error callback: " + callbackId + " = "+e);
-        }
-    },
-
-    /**
-     * Called by native code when returning the result from an action.
-     */
-    callbackFromNative: function(callbackId, success, status, args, keepCallback) {
-        var callback = cordova.callbacks[callbackId];
-        if (callback) {
-            if (success && status == cordova.callbackStatus.OK) {
-                callback.success && callback.success.apply(null, args);
-            } else if (!success) {
-                callback.fail && callback.fail.apply(null, args);
-            }
-
-            // Clear callback if not expecting any more results
-            if (!keepCallback) {
-                delete cordova.callbacks[callbackId];
-            }
-        }
-    },
-    addConstructor: function(func) {
-        channel.onCordovaReady.subscribe(function() {
-            try {
-                func();
-            } catch(e) {
-                console.log("Failed to run constructor: " + e);
-            }
-        });
-    }
-};
-
-// Register pause, resume and deviceready channels as events on document.
-channel.onPause = cordova.addDocumentEventHandler('pause');
-channel.onResume = cordova.addDocumentEventHandler('resume');
-channel.onDeviceReady = cordova.addStickyDocumentEventHandler('deviceready');
-
-module.exports = cordova;
-
-});
-
-// file: lib/common/argscheck.js
-define("cordova/argscheck", function(require, exports, module) {
-
-var exec = require('cordova/exec');
-var utils = require('cordova/utils');
-
-var moduleExports = module.exports;
-
-var typeMap = {
-    'A': 'Array',
-    'D': 'Date',
-    'N': 'Number',
-    'S': 'String',
-    'F': 'Function',
-    'O': 'Object'
-};
-
-function extractParamName(callee, argIndex) {
-  return (/.*?\((.*?)\)/).exec(callee)[1].split(', ')[argIndex];
-}
-
-function checkArgs(spec, functionName, args, opt_callee) {
-    if (!moduleExports.enableChecks) {
-        return;
-    }
-    var errMsg = null;
-    var typeName;
-    for (var i = 0; i < spec.length; ++i) {
-        var c = spec.charAt(i),
-            cUpper = c.toUpperCase(),
-            arg = args[i];
-        // Asterix means allow anything.
-        if (c == '*') {
-            continue;
-        }
-        typeName = utils.typeName(arg);
-        if ((arg === null || arg === undefined) && c == cUpper) {
-            continue;
-        }
-        if (typeName != typeMap[cUpper]) {
-            errMsg = 'Expected ' + typeMap[cUpper];
-            break;
-        }
-    }
-    if (errMsg) {
-        errMsg += ', but got ' + typeName + '.';
-        errMsg = 'Wrong type for parameter "' + extractParamName(opt_callee || args.callee, i) + '" of ' + functionName + ': ' + errMsg;
-        // Don't log when running jake test.
-        if (typeof jasmine == 'undefined') {
-            console.error(errMsg);
-        }
-        throw TypeError(errMsg);
-    }
-}
-
-function getValue(value, defaultValue) {
-    return value === undefined ? defaultValue : value;
-}
-
-moduleExports.checkArgs = checkArgs;
-moduleExports.getValue = getValue;
-moduleExports.enableChecks = true;
-
-
-});
-
-// file: lib/common/builder.js
-define("cordova/builder", function(require, exports, module) {
-
-var utils = require('cordova/utils');
-
-function each(objects, func, context) {
-    for (var prop in objects) {
-        if (objects.hasOwnProperty(prop)) {
-            func.apply(context, [objects[prop], prop]);
-        }
-    }
-}
-
-function clobber(obj, key, value) {
-    exports.replaceHookForTesting(obj, key);
-    obj[key] = value;
-    // Getters can only be overridden by getters.
-    if (obj[key] !== value) {
-        utils.defineGetter(obj, key, function() {
-            return value;
-        });
-    }
-}
-
-function assignOrWrapInDeprecateGetter(obj, key, value, message) {
-    if (message) {
-        utils.defineGetter(obj, key, function() {
-            console.log(message);
-            delete obj[key];
-            clobber(obj, key, value);
-            return value;
-        });
-    } else {
-        clobber(obj, key, value);
-    }
-}
-
-function include(parent, objects, clobber, merge) {
-    each(objects, function (obj, key) {
-        try {
-          var result = obj.path ? require(obj.path) : {};
-
-          if (clobber) {
-              // Clobber if it doesn't exist.
-              if (typeof parent[key] === 'undefined') {
-                  assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
-              } else if (typeof obj.path !== 'undefined') {
-                  // If merging, merge properties onto parent, otherwise, clobber.
-                  if (merge) {
-                      recursiveMerge(parent[key], result);
-                  } else {
-                      assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
-                  }
-              }
-              result = parent[key];
-          } else {
-            // Overwrite if not currently defined.
-            if (typeof parent[key] == 'undefined') {
-              assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
-            } else {
-              // Set result to what already exists, so we can build children into it if they exist.
-              result = parent[key];
-            }
-          }
-
-          if (obj.children) {
-            include(result, obj.children, clobber, merge);
-          }
-        } catch(e) {
-          utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"');
-        }
-    });
-}
-
-/**
- * Merge properties from one object onto another recursively.  Properties from
- * the src object will overwrite existing target property.
- *
- * @param target Object to merge properties into.
- * @param src Object to merge properties from.
- */
-function recursiveMerge(target, src) {
-    for (var prop in src) {
-        if (src.hasOwnProperty(prop)) {
-            if (target.prototype && target.prototype.constructor === target) {
-                // If the target object is a constructor override off prototype.
-                clobber(target.prototype, prop, src[prop]);
-            } else {
-                if (typeof src[prop] === 'object' && typeof target[prop] === 'object') {
-                    recursiveMerge(target[prop], src[prop]);
-                } else {
-                    clobber(target, prop, src[prop]);
-                }
-            }
-        }
-    }
-}
-
-exports.buildIntoButDoNotClobber = function(objects, target) {
-    include(target, objects, false, false);
-};
-exports.buildIntoAndClobber = function(objects, target) {
-    include(target, objects, true, false);
-};
-exports.buildIntoAndMerge = function(objects, target) {
-    include(target, objects, true, true);
-};
-exports.recursiveMerge = recursiveMerge;
-exports.assignOrWrapInDeprecateGetter = assignOrWrapInDeprecateGetter;
-exports.replaceHookForTesting = function() {};
-
-});
-
-// file: lib/common/channel.js
-define("cordova/channel", function(require, exports, module) {
-
-var utils = require('cordova/utils'),
-    nextGuid = 1;
-
-/**
- * Custom pub-sub "channel" that can have functions subscribed to it
- * This object is used to define and control firing of events for
- * cordova initialization, as well as for custom events thereafter.
- *
- * The order of events during page load and Cordova startup is as follows:
- *
- * onDOMContentLoaded*         Internal event that is received when the web page is loaded and parsed.
- * onNativeReady*              Internal event that indicates the Cordova native side is ready.
- * onCordovaReady*             Internal event fired when all Cordova JavaScript objects have been created.
- * onCordovaInfoReady*         Internal event fired when device properties are available.
- * onCordovaConnectionReady*   Internal event fired when the connection property has been set.
- * onDeviceReady*              User event fired to indicate that Cordova is ready
- * onResume                    User event fired to indicate a start/resume lifecycle event
- * onPause                     User event fired to indicate a pause lifecycle event
- * onDestroy*                  Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
- *
- * The events marked with an * are sticky. Once they have fired, they will stay in the fired state.
- * All listeners that subscribe after the event is fired will be executed right away.
- *
- * The only Cordova events that user code should register for are:
- *      deviceready           Cordova native code is initialized and Cordova APIs can be called from JavaScript
- *      pause                 App has moved to background
- *      resume                App has returned to foreground
- *
- * Listeners can be registered as:
- *      document.addEventListener("deviceready", myDeviceReadyListener, false);
- *      document.addEventListener("resume", myResumeListener, false);
- *      document.addEventListener("pause", myPauseListener, false);
- *
- * The DOM lifecycle events should be used for saving and restoring state
- *      window.onload
- *      window.onunload
- *
- */
-
-/**
- * Channel
- * @constructor
- * @param type  String the channel name
- */
-var Channel = function(type, sticky) {
-    this.type = type;
-    // Map of guid -> function.
-    this.handlers = {};
-    // 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
-    this.state = sticky ? 1 : 0;
-    // Used in sticky mode to remember args passed to fire().
-    this.fireArgs = null;
-    // Used by onHasSubscribersChange to know if there are any listeners.
-    this.numHandlers = 0;
-    // Function that is called when the first listener is subscribed, or when
-    // the last listener is unsubscribed.
-    this.onHasSubscribersChange = null;
-},
-    channel = {
-        /**
-         * Calls the provided function only after all of the channels specified
-         * have been fired. All channels must be sticky channels.
-         */
-        join: function(h, c) {
-            var len = c.length,
-                i = len,
-                f = function() {
-                    if (!(--i)) h();
-                };
-            for (var j=0; j<len; j++) {
-                if (c[j].state === 0) {
-                    throw Error('Can only use join with sticky channels.');
-                }
-                c[j].subscribe(f);
-            }
-            if (!len) h();
-        },
-        create: function(type) {
-            return channel[type] = new Channel(type, false);
-        },
-        createSticky: function(type) {
-            return channel[type] = new Channel(type, true);
-        },
-
-        /**
-         * cordova Channels that must fire before "deviceready" is fired.
-         */
-        deviceReadyChannelsArray: [],
-        deviceReadyChannelsMap: {},
-
-        /**
-         * Indicate that a feature needs to be initialized before it is ready to be used.
-         * This holds up Cordova's "deviceready" event until the feature has been initialized
-         * and Cordova.initComplete(feature) is called.
-         *
-         * @param feature {String}     The unique feature name
-         */
-        waitForInitialization: function(feature) {
-            if (feature) {
-                var c = channel[feature] || this.createSticky(feature);
-                this.deviceReadyChannelsMap[feature] = c;
-                this.deviceReadyChannelsArray.push(c);
-            }
-        },
-
-        /**
-         * Indicate that initialization code has completed and the feature is ready to be used.
-         *
-         * @param feature {String}     The unique feature name
-         */
-        initializationComplete: function(feature) {
-            var c = this.deviceReadyChannelsMap[feature];
-            if (c) {
-                c.fire();
-            }
-        }
-    };
-
-function forceFunction(f) {
-    if (typeof f != 'function') throw "Function required as first argument!";
-}
-
-/**
- * Subscribes the given function to the channel. Any time that
- * Channel.fire is called so too will the function.
- * Optionally specify an execution context for the function
- * and a guid that can be used to stop subscribing to the channel.
- * Returns the guid.
- */
-Channel.prototype.subscribe = function(f, c) {
-    // need a function to call
-    forceFunction(f);
-    if (this.state == 2) {
-        f.apply(c || this, this.fireArgs);
-        return;
-    }
-
-    var func = f,
-        guid = f.observer_guid;
-    if (typeof c == "object") { func = utils.close(c, f); }
-
-    if (!guid) {
-        // first time any channel has seen this subscriber
-        guid = '' + nextGuid++;
-    }
-    func.observer_guid = guid;
-    f.observer_guid = guid;
-
-    // Don't add the same handler more than once.
-    if (!this.handlers[guid]) {
-        this.handlers[guid] = func;
-        this.numHandlers++;
-        if (this.numHandlers == 1) {
-            this.onHasSubscribersChange && this.onHasSubscribersChange();
-        }
-    }
-};
-
-/**
- * Unsubscribes the function with the given guid from the channel.
- */
-Channel.prototype.unsubscribe = function(f) {
-    // need a function to unsubscribe
-    forceFunction(f);
-
-    var guid = f.observer_guid,
-        handler = this.handlers[guid];
-    if (handler) {
-        delete this.handlers[guid];
-        this.numHandlers--;
-        if (this.numHandlers === 0) {
-            this.onHasSubscribersChange && this.onHasSubscribersChange();
-        }
-    }
-};
-
-/**
- * Calls all functions subscribed to this channel.
- */
-Channel.prototype.fire = function(e) {
-    var fail = false,
-        fireArgs = Array.prototype.slice.call(arguments);
-    // Apply stickiness.
-    if (this.state == 1) {
-        this.state = 2;
-        this.fireArgs = fireArgs;
-    }
-    if (this.numHandlers) {
-        // Copy the values first so that it is safe to modify it from within
-        // callbacks.
-        var toCall = [];
-        for (var item in this.handlers) {
-            toCall.push(this.handlers[item]);
-        }
-        for (var i = 0; i < toCall.length; ++i) {
-            toCall[i].apply(this, fireArgs);
-        }
-        if (this.state == 2 && this.numHandlers) {
-            this.numHandlers = 0;
-            this.handlers = {};
-            this.onHasSubscribersChange && this.onHasSubscribersChange();
-        }
-    }
-};
-
-
-// defining them here so they are ready super fast!
-// DOM event that is received when the web page is loaded and parsed.
-channel.createSticky('onDOMContentLoaded');
-
-// Event to indicate the Cordova native side is ready.
-channel.createSticky('onNativeReady');
-
-// Event to indicate that all Cordova JavaScript objects have been created
-// and it's time to run plugin constructors.
-channel.createSticky('onCordovaReady');
-
-// Event to indicate that device properties are available
-channel.createSticky('onCordovaInfoReady');
-
-// Event to indicate that the connection property has been set.
-channel.createSticky('onCordovaConnectionReady');
-
-// Event to indicate that all automatically loaded JS plugins are loaded and ready.
-channel.createSticky('onPluginsReady');
-
-// Event to indicate that Cordova is ready
-channel.createSticky('onDeviceReady');
-
-// Event to indicate a resume lifecycle event
-channel.create('onResume');
-
-// Event to indicate a pause lifecycle event
-channel.create('onPause');
-
-// Event to indicate a destroy lifecycle event
-channel.createSticky('onDestroy');
-
-// Channels that must fire before "deviceready" is fired.
-channel.waitForInitialization('onCordovaReady');
-channel.waitForInitialization('onCordovaConnectionReady');
-channel.waitForInitialization('onDOMContentLoaded');
-
-module.exports = channel;
-
-});
-
-// file: lib/common/commandProxy.js
-define("cordova/commandProxy", function(require, exports, module) {
-
-
-// internal map of proxy function
-var CommandProxyMap = {};
-
-module.exports = {
-
-    // example: cordova.commandProxy.add("Accelerometer",{getCurrentAcceleration: function(successCallback, errorCallback, options) {...},...);
-    add:function(id,proxyObj) {
-        console.log("adding proxy for " + id);
-        CommandProxyMap[id] = proxyObj;
-        return proxyObj;
-    },
-
-    // cordova.commandProxy.remove("Accelerometer");
-    remove:function(id) {
-        var proxy = CommandProxyMap[id];
-        delete CommandProxyMap[id];
-        CommandProxyMap[id] = null;
-        return proxy;
-    },
-
-    get:function(service,action) {
-        return ( CommandProxyMap[service] ? CommandProxyMap[service][action] : null );
-    }
-};
-});
-
-// file: lib/android/exec.js
-define("cordova/exec", function(require, exports, module) {
-
-/**
- * Execute a cordova command.  It is up to the native side whether this action
- * is synchronous or asynchronous.  The native side can return:
- *      Synchronous: PluginResult object as a JSON string
- *      Asynchronous: Empty string ""
- * If async, the native side will cordova.callbackSuccess or cordova.callbackError,
- * depending upon the result of the action.
- *
- * @param {Function} success    The success callback
- * @param {Function} fail       The fail callback
- * @param {String} service      The name of the service to use
- * @param {String} action       Action to be run in cordova
- * @param {String[]} [args]     Zero or more arguments to pass to the method
- */
-var cordova = require('cordova'),
-    nativeApiProvider = require('cordova/plugin/android/nativeapiprovider'),
-    utils = require('cordova/utils'),
-    jsToNativeModes = {
-        PROMPT: 0,
-        JS_OBJECT: 1,
-        // This mode is currently for benchmarking purposes only. It must be enabled
-        // on the native side through the ENABLE_LOCATION_CHANGE_EXEC_MODE
-        // constant within CordovaWebViewClient.java before it will work.
-        LOCATION_CHANGE: 2
-    },
-    nativeToJsModes = {
-        // Polls for messages using the JS->Native bridge.
-        POLLING: 0,
-        // For LOAD_URL to be viable, it would need to have a work-around for
-        // the bug where the soft-keyboard gets dismissed when a message is sent.
-        LOAD_URL: 1,
-        // For the ONLINE_EVENT to be viable, it would need to intercept all event
-        // listeners (both through addEventListener and window.ononline) as well
-        // as set the navigator property itself.
-        ONLINE_EVENT: 2,
-        // Uses reflection to access private APIs of the WebView that can send JS
-        // to be executed.
-        // Requires Android 3.2.4 or above.
-        PRIVATE_API: 3
-    },
-    jsToNativeBridgeMode,  // Set lazily.
-    nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT,
-    pollEnabled = false,
-    messagesFromNative = [];
-
-function androidExec(success, fail, service, action, args) {
-    // Set default bridge modes if they have not already been set.
-    // By default, we use the failsafe, since addJavascriptInterface breaks too often
-    if (jsToNativeBridgeMode === undefined) {
-        androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
-    }
-
-    // Process any ArrayBuffers in the args into a string.
-    for (var i = 0; i < args.length; i++) {
-        if (utils.typeName(args[i]) == 'ArrayBuffer') {
-            args[i] = window.btoa(String.fromCharCode.apply(null, new Uint8Array(args[i])));
-        }
-    }
-
-    var callbackId = service + cordova.callbackId++,
-        argsJson = JSON.stringify(args);
-
-    if (success || fail) {
-        cordova.callbacks[callbackId] = {success:success, fail:fail};
-    }
-
-    if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) {
-        window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;
-    } else {
-        var messages = nativeApiProvider.get().exec(service, action, callbackId, argsJson);
-        // If argsJson was received by Java as null, try again with the PROMPT bridge mode.
-        // This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2.  See CB-2666.
-        if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && messages === "@Null arguments.") {
-            androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
-            androidExec(success, fail, service, action, args);
-            androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
-            return;
-        } else {
-            androidExec.processMessages(messages);
-        }
-    }
-}
-
-function pollOnce() {
-    var msg = nativeApiProvider.get().retrieveJsMessages();
-    androidExec.processMessages(msg);
-}
-
-function pollingTimerFunc() {
-    if (pollEnabled) {
-        pollOnce();
-        setTimeout(pollingTimerFunc, 50);
-    }
-}
-
-function hookOnlineApis() {
-    function proxyEvent(e) {
-        cordova.fireWindowEvent(e.type);
-    }
-    // The network module takes care of firing online and offline events.
-    // It currently fires them only on document though, so we bridge them
-    // to window here (while first listening for exec()-releated online/offline
-    // events).
-    window.addEventListener('online', pollOnce, false);
-    window.addEventListener('offline', pollOnce, false);
-    cordova.addWindowEventHandler('online');
-    cordova.addWindowEventHandler('offline');
-    document.addEventListener('online', proxyEvent, false);
-    document.addEventListener('offline', proxyEvent, false);
-}
-
-hookOnlineApis();
-
-androidExec.jsToNativeModes = jsToNativeModes;
-androidExec.nativeToJsModes = nativeToJsModes;
-
-androidExec.setJsToNativeBridgeMode = function(mode) {
-    if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaNative) {
-        console.log('Falling back on PROMPT mode since _cordovaNative is missing. Expected for Android 3.2 and lower only.');
-        mode = jsToNativeModes.PROMPT;
-    }
-    nativeApiProvider.setPreferPrompt(mode == jsToNativeModes.PROMPT);
-    jsToNativeBridgeMode = mode;
-};
-
-androidExec.setNativeToJsBridgeMode = function(mode) {
-    if (mode == nativeToJsBridgeMode) {
-        return;
-    }
-    if (nativeToJsBridgeMode == nativeToJsModes.POLLING) {
-        pollEnabled = false;
-    }
-
-    nativeToJsBridgeMode = mode;
-    // Tell the native side to switch modes.
-    nativeApiProvider.get().setNativeToJsBridgeMode(mode);
-
-    if (mode == nativeToJsModes.POLLING) {
-        pollEnabled = true;
-        setTimeout(pollingTimerFunc, 1);
-    }
-};
-
-// Processes a single message, as encoded by NativeToJsMessageQueue.java.
-function processMessage(message) {
-    try {
-        var firstChar = message.charAt(0);
-        if (firstChar == 'J') {
-            eval(message.slice(1));
-        } else if (firstChar == 'S' || firstChar == 'F') {
-            var success = firstChar == 'S';
-            var keepCallback = message.charAt(1) == '1';
-            var spaceIdx = message.indexOf(' ', 2);
-            var status = +message.slice(2, spaceIdx);
-            var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1);
-            var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx);
-            var payloadKind = message.charAt(nextSpaceIdx + 1);
-            var payload;
-            if (payloadKind == 's') {
-                payload = message.slice(nextSpaceIdx + 2);
-            } else if (payloadKind == 't') {
-                payload = true;
-            } else if (payloadKind == 'f') {
-                payload = false;
-            } else if (payloadKind == 'N') {
-                payload = null;
-            } else if (payloadKind == 'n') {
-                payload = +message.slice(nextSpaceIdx + 2);
-            } else if (payloadKind == 'A') {
-                var data = message.slice(nextSpaceIdx + 2);
-                var bytes = window.atob(data);
-                var arraybuffer = new Uint8Array(bytes.length);
-                for (var i = 0; i < bytes.length; i++) {
-                    arraybuffer[i] = bytes.charCodeAt(i);
-                }
-                payload = arraybuffer.buffer;
-            } else if (payloadKind == 'S') {
-                payload = window.atob(message.slice(nextSpaceIdx + 2));
-            } else {
-                payload = JSON.parse(message.slice(nextSpaceIdx + 1));
-            }
-            cordova.callbackFromNative(callbackId, success, status, [payload], keepCallback);
-        } else {
-            console.log("processMessage failed: invalid message:" + message);
-        }
-    } catch (e) {
-        console.log("processMessage failed: Message: " + message);
-        console.log("processMessage failed: Error: " + e);
-        console.log("processMessage failed: Stack: " + e.stack);
-    }
-}
-
-// This is called from the NativeToJsMessageQueue.java.
-androidExec.processMessages = function(messages) {
-    if (messages) {
-        messagesFromNative.push(messages);
-        // Check for the reentrant case, and enqueue the message if that's the case.
-        if (messagesFromNative.length > 1) {
-            return;
-        }
-        while (messagesFromNative.length) {
-            // Don't unshift until the end so that reentrancy can be detected.
-            messages = messagesFromNative[0];
-            // The Java side can send a * message to indicate that it
-            // still has messages waiting to be retrieved.
-            if (messages == '*') {
-                messagesFromNative.shift();
-                window.setTimeout(pollOnce, 0);
-                return;
-            }
-
-            var spaceIdx = messages.indexOf(' ');
-            var msgLen = +messages.slice(0, spaceIdx);
-            var message = messages.substr(spaceIdx + 1, msgLen);
-            messages = messages.slice(spaceIdx + msgLen + 1);
-            processMessage(message);
-            if (messages) {
-                messagesFromNative[0] = messages;
-            } else {
-                messagesFromNative.shift();
-            }
-        }
-    }
-};
-
-module.exports = androidExec;
-
-});
-
-// file: lib/common/modulemapper.js
-define("cordova/modulemapper", function(require, exports, module) {
-
-var builder = require('cordova/builder'),
-    moduleMap = define.moduleMap,
-    symbolList,
-    deprecationMap;
-
-exports.reset = function() {
-    symbolList = [];
-    deprecationMap = {};
-};
-
-function addEntry(strategy, moduleName, symbolPath, opt_deprecationMessage) {
-    if (!(moduleName in moduleMap)) {
-        throw new Error('Module ' + moduleName + ' does not exist.');
-    }
-    symbolList.push(strategy, moduleName, symbolPath);
-    if (opt_deprecationMessage) {
-        deprecationMap[symbolPath] = opt_deprecationMessage;
-    }
-}
-
-// Note: Android 2.3 does have Function.bind().
-exports.clobbers = function(moduleName, symbolPath, opt_deprecationMessage) {
-    addEntry('c', moduleName, symbolPath, opt_deprecationMessage);
-};
-
-exports.merges = function(moduleName, symbolPath, opt_deprecationMessage) {
-    addEntry('m', moduleName, symbolPath, opt_deprecationMessage);
-};
-
-exports.defaults = function(moduleName, symbolPath, opt_deprecationMessage) {
-    addEntry('d', moduleName, symbolPath, opt_deprecationMessage);
-};
-
-function prepareNamespace(symbolPath, context) {
-    if (!symbolPath) {
-        return context;
-    }
-    var parts = symbolPath.split('.');
-    var cur = context;
-    for (var i = 0, part; part = parts[i]; ++i) {
-        cur = cur[part] = cur[part] || {};
-    }
-    return cur;
-}
-
-exports.mapModules = function(context) {
-    var origSymbols = {};
-    context.CDV_origSymbols = origSymbols;
-    for (var i = 0, len = symbolList.length; i < len; i += 3) {
-        var strategy = symbolList[i];
-        var moduleName = symbolList[i + 1];
-        var symbolPath = symbolList[i + 2];
-        var lastDot = symbolPath.lastIndexOf('.');
-        var namespace = symbolPath.substr(0, lastDot);
-        var lastName = symbolPath.substr(lastDot + 1);
-
-        var module = require(moduleName);
-        var deprecationMsg = symbolPath in deprecationMap ? 'Access made to deprecated symbol: ' + symbolPath + '. ' + deprecationMsg : null;
-        var parentObj = prepareNamespace(namespace, context);
-        var target = parentObj[lastName];
-
-        if (strategy == 'm' && target) {
-            builder.recursiveMerge(target, module);
-        } else if ((strategy == 'd' && !target) || (strategy != 'd')) {
-            if (!(symbolPath in origSymbols)) {
-                origSymbols[symbolPath] = target;
-            }
-            builder.assignOrWrapInDeprecateGetter(parentObj, lastName, module, deprecationMsg);
-        }
-    }
-};
-
-exports.getOriginalSymbol = function(context, symbolPath) {
-    var origSymbols = context.CDV_origSymbols;
-    if (origSymbols && (symbolPath in origSymbols)) {
-        return origSymbols[symbolPath];
-    }
-    var parts = symbolPath.split('.');
-    var obj = context;
-    for (var i = 0; i < parts.length; ++i) {
-        obj = obj && obj[parts[i]];
-    }
-    return obj;
-};
-
-exports.loadMatchingModules = function(matchingRegExp) {
-    for (var k in moduleMap) {
-        if (matchingRegExp.exec(k)) {
-            require(k);
-        }
-    }
-};
-
-exports.reset();
-
-
-});
-
-// file: lib/android/platform.js
-define("cordova/platform", function(require, exports, module) {
-
-module.exports = {
-    id: "android",
-    initialize:function() {
-        var channel = require("cordova/channel"),
-            cordova = require('cordova'),
-            exec = require('cordova/exec'),
-            modulemapper = require('cordova/modulemapper');
-
-        modulemapper.loadMatchingModules(/cordova.*\/symbols$/);
-        modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app');
-
-        modulemapper.mapModules(window);
-
-        // Inject a listener for the backbutton on the document.
-        var backButtonChannel = cordova.addDocumentEventHandler('backbutton');
-        backButtonChannel.onHasSubscribersChange = function() {
-            // If we just attached the first handler or detached the last handler,
-            // let native know we need to override the back button.
-            exec(null, null, "App", "overrideBackbutton", [this.numHandlers == 1]);
-        };
-
-        // Add hardware MENU and SEARCH button handlers
-        cordova.addDocumentEventHandler('menubutton');
-        cordova.addDocumentEventHandler('searchbutton');
-
-        // Let native code know we are all done on the JS side.
-        // Native code will then un-hide the WebView.
-        channel.join(function() {
-            exec(null, null, "App", "show", []);
-        }, [channel.onCordovaReady]);
-    }
-};
-
-});
-
-// file: lib/common/plugin/Acceleration.js
-define("cordova/plugin/Acceleration", function(require, exports, module) {
-
-var Acceleration = function(x, y, z, timestamp) {
-    this.x = x;
-    this.y = y;
-    this.z = z;
-    this.timestamp = timestamp || (new Date()).getTime();
-};
-
-module.exports = Acceleration;
-
-});
-
-// file: lib/common/plugin/Camera.js
-define("cordova/plugin/Camera", function(require, exports, module) {
-
-var argscheck = require('cordova/argscheck'),
-    exec = require('cordova/exec'),
-    Camera = require('cordova/plugin/CameraConstants'),
-    CameraPopoverHandle = require('cordova/plugin/CameraPopoverHandle');
-
-var cameraExport = {};
-
-// Tack on the Camera Constants to the base camera plugin.
-for (var key in Camera) {
-    cameraExport[key] = Camera[key];
-}
-
-/**
- * Gets a picture from source defined by "options.sourceType", and returns the
- * image as defined by the "options.destinationType" option.
-
- * The defaults are sourceType=CAMERA and destinationType=FILE_URI.
- *
- * @param {Function} successCallback
- * @param {Function} errorCallback
- * @param {Object} options
- */
-cameraExport.getPicture = function(successCallback, errorCallback, options) {
-    argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
-    options = options || {};
-    var getValue = argscheck.getValue;
-
-    var quality = getValue(options.quality, 50);
-    var destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
-    var sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
-    var targetWidth = getValue(options.targetWidth, -1);
-    var targetHeight = getValue(options.targetHeight, -1);
-    var encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG);
-    var mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE);
-    var allowEdit = !!options.allowEdit;
-    var correctOrientation = !!options.correctOrientation;
-    var saveToPhotoAlbum = !!options.saveToPhotoAlbum;
-    var popoverOptions = getValue(options.popoverOptions, null);
-    var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
-
-    var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
-                mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
-
-    exec(successCallback, errorCallback, "Camera", "takePicture", args);
-    return new CameraPopoverHandle();
-};
-
-cameraExport.cleanup = function(successCallback, errorCallback) {
-    exec(successCallback, errorCallback, "Camera", "cleanup", []);
-};
-
-module.exports = cameraExport;
-
-});
-
-// file: lib/common/plugin/CameraConstants.js
-define("cordova/plugin/CameraConstants", function(require, exports, module) {
-
-module.exports = {
-  DestinationType:{
-    DATA_URL: 0,         // Return base64 encoded string
-    FILE_URI: 1,         // Return file uri (content://media/external/images/media/2 for Android)
-    NATIVE_URI: 2        // Return native uri (eg. asset-library://... for iOS)
-  },
-  EncodingType:{
-    JPEG: 0,             // Return JPEG encoded image
-    PNG: 1               // Return PNG encoded image
-  },
-  MediaType:{
-    PICTURE: 0,          // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
-    VIDEO: 1,            // allow selection of video only, ONLY RETURNS URL
-    ALLMEDIA : 2         // allow selection from all media types
-  },
-  PictureSourceType:{
-    PHOTOLIBRARY : 0,    // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
-    CAMERA : 1,          // Take picture from camera
-    SAVEDPHOTOALBUM : 2  // Choose image from picture library (same as PHOTOLIBRARY for Android)
-  },
-  PopoverArrowDirection:{
-      ARROW_UP : 1,        // matches iOS UIPopoverArrowDirection constants to specify arrow location on popover
-      ARROW_DOWN : 2,
-      ARROW_LEFT : 4,
-      ARROW_RIGHT : 8,
-      ARROW_ANY : 15
-  },
-  Direction:{
-      BACK: 0,
-      FRONT: 1
-  }
-};
-
-});
-
-// file: lib/common/plugin/CameraPopoverHandle.js
-define("cordova/plugin/CameraPopoverHandle", function(require, exports, module) {
-
-var exec = require('cordova/exec');
-
-/**
- * A handle to an image picker popover.
- */
-var CameraPopoverHandle = function() {
-    this.setPosition = function(popoverOptions) {
-        console.log('CameraPopoverHandle.setPosition is only supported on iOS.');
-    };
-};
-
-module.exports = CameraPopoverHandle;
-
-});
-
-// file: lib/common/plugin/CameraPopoverOptions.js
-define("cordova/plugin/CameraPopoverOptions", function(require, exports, module) {
-
-var Camera = require('cordova/plugin/CameraConstants');
-
-/**
- * Encapsulates options for iOS Popover image picker
- */
-var CameraPopoverOptions = function(x,y,width,height,arrowDir){
-    // information of rectangle that popover should be anchored to
-    this.x = x || 0;
-    this.y = y || 32;
-    this.width = width || 320;
-    this.height = height || 480;
-    // The direction of the popover arrow
-    this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY;
-};
-
-module.exports = CameraPopoverOptions;
-
-});
-
-// file: lib/common/plugin/CaptureAudioOptions.js
-define("cordova/plugin/CaptureAudioOptions", function(require, exports, module) {
-
-/**
- * Encapsulates all audio capture operation configuration options.
- */
-var CaptureAudioOptions = function(){
-    // Upper limit of sound clips user can record. Value must be equal or greater than 1.
-    this.limit = 1;
-    // Maximum duration of a single sound clip in seconds.
-    this.duration = 0;
-    // The selected audio mode. Must match with one of the elements in supportedAudioModes array.
-    this.mode = null;
-};
-
-module.exports = CaptureAudioOptions;
-
-});
-
-// file: lib/common/plugin/CaptureError.js
-define("cordova/plugin/CaptureError", function(require, exports, module) {
-
-/**
- * The CaptureError interface encapsulates all errors in the Capture API.
- */
-var CaptureError = function(c) {
-   this.code = c || null;
-};
-
-// Camera or microphone failed to capture image or sound.
-CaptureError.CAPTURE_INTERNAL_ERR = 0;
-// Camera application or audio capture application is currently serving other capture request.
-CaptureError.CAPTURE_APPLICATION_BUSY = 1;
-// Invalid use of the API (e.g. limit parameter has value less than one).
-CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
-// User exited camera application or audio capture application before capturing anything.
-CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
-// The requested capture operation is not supported.
-CaptureError.CAPTURE_NOT_SUPPORTED = 20;
-
-module.exports = CaptureError;
-
-});
-
-// file: lib/common/plugin/CaptureImageOptions.js
-define("cordova/plugin/CaptureImageOptions", function(require, exports, module) {
-
-/**
- * Encapsulates all image capture operation configuration options.
- */
-var CaptureImageOptions = function(){
-    // Upper limit of images user can take. Value must be equal or greater than 1.
-    this.limit = 1;
-    // The selected image mode. Must match with one of the elements in supportedImageModes array.
-    this.mode = null;
-};
-
-module.exports = CaptureImageOptions;
-
-});
-
-// file: lib/common/plugin/CaptureVideoOptions.js
-define("cordova/plugin/CaptureVideoOptions", function(require, exports, module) {
-
-/**
- * Encapsulates all video capture operation configuration options.
- */
-var CaptureVideoOptions = function(){
-    // Upper limit of videos user can record. Value must be equal or greater than 1.
-    this.limit = 1;
-    // Maximum duration of a single video clip in seconds.
-    this.duration = 0;
-    // The selected video mode. Must match with one of the elements in supportedVideoModes array.
-    this.mode = null;
-};
-
-module.exports = CaptureVideoOptions;
-
-});
-
-// file: lib/common/plugin/CompassError.js
-define("cordova/plugin/CompassError", function(require, exports, module) {
-
-/**
- *  CompassError.
- *  An error code assigned by an implementation when an error has occurred
- * @constructor
- */
-var CompassError = function(err) {
-    this.code = (err !== undefined ? err : null);
-};
-
-CompassError.COMPASS_INTERNAL_ERR = 0;
-CompassError.COMPASS_NOT_SUPPORTED = 20;
-
-module.exports = CompassError;
-
-});
-
-// file: lib/common/plugin/CompassHeading.js
-define("cordova/plugin/CompassHeading", function(require, exports, module) {
-
-var CompassHeading = function(magneticHeading, trueHeading, headingAccuracy, timestamp) {
-  this.magneticHeading = magneticHeading;
-  this.trueHeading = trueHeading;
-  this.headingAccuracy = headingAccuracy;
-  this.timestamp = timestamp || new Date().getTime();
-};
-
-module.exports = CompassHeading;
-
-});
-
-// file: lib/common/plugin/ConfigurationData.js
-define("cordova/plugin/ConfigurationData", function(require, exports, module) {
-
-/**
- * Encapsulates a set of parameters that the capture device supports.
- */
-function ConfigurationData() {
-    // The ASCII-encoded string in lower case representing the media type.
-    this.type = null;
-    // The height attribute represents height of the image or video in pixels.
-    // In the case of a sound clip this attribute has value 0.
-    this.height = 0;
-    // The width attribute represents width of the image or video in pixels.
-    // In the case of a sound clip this attribute has value 0
-    this.width = 0;
-}
-
-module.exports = ConfigurationData;
-
-});
-
-// file: lib/common/plugin/Connection.js
-define("cordova/plugin/Connection", function(require, exports, module) {
-
-/**
- * Network status
- */
-module.exports = {
-        UNKNOWN: "unknown",
-        ETHERNET: "ethernet",
-        WIFI: "wifi",
-        CELL_2G: "2g",
-        CELL_3G: "3g",
-        CELL_4G: "4g",
-        CELL:"cellular",
-        NONE: "none"
-};
-
-});
-
-// file: lib/common/plugin/Contact.js
-define("cordova/plugin/Contact", function(require, exports, module) {
-
-var argscheck = require('cordova/argscheck'),
-    exec = require('cordova/exec'),
-    ContactError = require('cordova/plugin/ContactError'),
-    utils = require('cordova/utils');
-
-/**
-* Converts primitives into Complex Object
-* Currently only used for Date fields
-*/
-function convertIn(contact) {
-    var value = contact.birthday;
-    try {
-      contact.birthday = new Date(parseFloat(value));
-    } catch (exception){
-      console.log("Cordova Contact convertIn error: exception creating date.");
-    }
-    return contact;
-}
-
-/**
-* Converts Complex objects into primitives
-* Only conversion at present is for Dates.
-**/
-
-function convertOut(contact) {
-    var value = contact.birthday;
-    if (value !== null) {
-        // try to make it a Date object if it is not already
-        if (!utils.isDate(value)){
-            try {
-                value = new Date(value);
-            } catch(exception){
-                value = null;
-            }
-        }
-        if (utils.isDate(value)){
-            value = value.valueOf(); // convert to milliseconds
-        }
-        contact.birthday = value;
-    }
-    return contact;
-}
-
-/**
-* Contains information about a single contact.
-* @constructor
-* @param {DOMString} id unique identifier
-* @param {DOMString} displayName
-* @param {ContactName} name
-* @param {DOMString} nickname
-* @param {Array.<ContactField>} phoneNumbers array of phone numbers
-* @param {Array.<ContactField>} emails array of email addresses
-* @param {Array.<ContactAddress>} addresses array of addresses
-* @param {Array.<ContactField>} ims instant messaging user ids
-* @param {Array.<ContactOrganization>} organizations
-* @param {DOMString} birthday contact's birthday
-* @param {DOMString} note user notes about contact
-* @param {Array.<ContactField>} photos
-* @param {Array.<ContactField>} categories
-* @param {Array.<ContactField>} urls contact's web sites
-*/
-var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
-    ims, organizations, birthday, note, photos, categories, urls) {
-    this.id = id || null;
-    this.rawId = null;
-    this.displayName = displayName || null;
-    this.name = name || null; // ContactName
-    this.nickname = nickname || null;
-    this.phoneNumbers = phoneNumbers || null; // ContactField[]
-    this.emails = emails || null; // ContactField[]
-    this.addresses = addresses || null; // ContactAddress[]
-    this.ims = ims || null; // ContactField[]
-    this.organizations = organizations || null; // ContactOrganization[]
-    this.birthday = birthday || null;
-    this.note = note || null;
-    this.photos = photos || null; // ContactField[]
-    this.categories = categories || null; // ContactField[]
-    this.urls = urls || null; // ContactField[]
-};
-
-/**
-* Removes contact from device storage.
-* @param successCB success callback
-* @param errorCB error callback
-*/
-Contact.prototype.remove = function(successCB, errorCB) {
-    argscheck.checkArgs('FF', 'Contact.remove', arguments);
-    var fail = errorCB && function(code) {
-        errorCB(new ContactError(code));
-    };
-    if (this.id === null) {
-        fail(ContactError.UNKNOWN_ERROR);
-    }
-    else {
-        exec(successCB, fail, "Contacts", "remove", [this.id]);
-    }
-};
-
-/**
-* Creates a deep copy of this Contact.
-* With the contact ID set to null.
-* @return copy of this Contact
-*/
-Contact.prototype.clone = function() {
-    var clonedContact = utils.clone(this);
-    clonedContact.id = null;
-    clonedContact.rawId = null;
-
-    function nullIds(arr) {
-        if (arr) {
-            for (var i = 0; i < arr.length; ++i) {
-                arr[i].id = null;
-            }
-        }
-    }
-
-    // Loop through and clear out any id's in phones, emails, etc.
-    nullIds(clonedContact.phoneNumbers);
-    nullIds(clonedContact.emails);
-    nullIds(clonedContact.addresses);
-    nullIds(clonedContact.ims);
-    nullIds(clonedContact.organizations);
-    nullIds(clonedContact.categories);
-    nullIds(clonedContact.photos);
-    nullIds(clonedContact.urls);
-    return clonedContact;
-};
-
-/**
-* Persists contact to device storage.
-* @param successCB success callback
-* @param errorCB error callback
-*/
-Contact.prototype.save = function(successCB, errorCB) {
-    argscheck.checkArgs('FFO', 'Contact.save', arguments);
-    var fail = errorCB && function(code) {
-        errorCB(new ContactError(code));
-    };
-    var success = function(result) {
-        if (result) {
-            if (successCB) {
-                var fullContact = require('cordova/plugin/contacts').create(result);
-                successCB(convertIn(fullContact));
-            }
-        }
-        else {
-            // no Entry object returned
-            fail(ContactError.UNKNOWN_ERROR);
-        }
-    };
-    var dupContact = convertOut(utils.clone(this));
-    exec(success, fail, "Contacts", "save", [dupContact]);
-};
-
-
-module.exports = Contact;
-
-});
-
-// file: lib/common/plugin/ContactAddress.js
-define("cordova/plugin/ContactAddress", function(require, exports, module) {
-
-/**
-* Contact address.
-* @constructor
-* @param {DOMString} id unique identifier, should only be set by native code
-* @param formatted // NOTE: not a W3C standard
-* @param streetAddress
-* @param locality
-* @param region
-* @param postalCode
-* @param country
-*/
-
-var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
-    this.id = null;
-    this.pref = (typeof pref != 'undefined' ? pref : false);
-    this.type = type || null;
-    this.formatted = formatted || null;
-    this.streetAddress = streetAddress || null;
-    this.locality = locality || null;
-    this.region = region || null;
-    this.postalCode = postalCode || null;
-    this.country = country || null;
-};
-
-module.exports = ContactAddress;
-
-});
-
-// file: lib/common/plugin/ContactError.js
-define("cordova/plugin/ContactError", function(require, exports, module) {
-
-/**
- *  ContactError.
- *  An error code assigned by an implementation when an error has occurred
- * @constructor
- */
-var ContactError = function(err) {
-    this.code = (typeof err != 'undefined' ? err : null);
-};
-
-/**
- * Error codes
- */
-ContactError.UNKNOWN_ERROR = 0;
-ContactError.INVALID_ARGUMENT_ERROR = 1;
-ContactError.TIMEOUT_ERROR = 2;
-ContactError.PENDING_OPERATION_ERROR = 3;
-ContactError.IO_ERROR = 4;
-ContactError.NOT_SUPPORTED_ERROR = 5;
-ContactError.PERMISSION_DENIED_ERROR = 20;
-
-module.exports = ContactError;
-
-});
-
-// file: lib/common/plugin/ContactField.js
-define("cordova/plugin/ContactField", function(require, exports, module) {
-
-/**
-* Generic contact field.
-* @constructor
-* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
-* @param type
-* @param value
-* @param pref
-*/
-var ContactField = function(type, value, pref) {
-    this.id = null;
-    this.type = (type && type.toString()) || null;
-    this.value = (value && value.toString()) || null;
-    this.pref = (typeof pref != 'undefined' ? pref : false);
-};
-
-module.exports = ContactField;
-
-});
-
-// file: lib/common/plugin/ContactFindOptions.js
-define("cordova/plugin/ContactFindOptions", function(require, exports, module) {
-
-/**
- * ContactFindOptions.
- * @constructor
- * @param filter used to match contacts against
- * @param multiple boolean used to determine if more than one contact should be returned
- */
-
-var ContactFindOptions = function(filter, multiple) {
-    this.filter = filter || '';
-    this.multiple = (typeof multiple != 'undefined' ? multiple : false);
-};
-
-module.exports = ContactFindOptions;
-
-});
-
-// file: lib/common/plugin/ContactName.js
-define("cordova/plugin/ContactName", function(require, exports, module) {
-
-/**
-* Contact name.
-* @constructor
-* @param formatted // NOTE: not part of W3C standard
-* @param familyName
-* @param givenName
-* @param middle
-* @param prefix
-* @param suffix
-*/
-var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
-    this.formatted = formatted || null;
-    this.familyName = familyName || null;
-    this.givenName = givenName || null;
-    this.middleName = middle || null;
-    this.honorificPrefix = prefix || null;
-    this.honorificSuffix = suffix || null;
-};
-
-module.exports = ContactName;
-
-});
-
-// file: lib/common/plugin/ContactOrganization.js
-define("cordova/plugin/ContactOrganization", function(require, exports, module) {
-
-/**
-* Contact organization.
-* @constructor
-* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
-* @param name
-* @param dept
-* @param title
-* @param startDate
-* @param endDate
-* @param location
-* @param desc
-*/
-
-var ContactOrganization = function(pref, type, name, dept, title) {
-    this.id = null;
-    this.pref = (typeof pref != 'undefined' ? pref : false);
-    this.type = type || null;
-    this.name = name || null;
-    this.department = dept || null;
-    this.title = title || null;
-};
-
-module.exports = ContactOrganization;
-
-});
-
-// file: lib/common/plugin/Coordinates.js
-define("cordova/plugin/Coordinates", function(require, exports, module) {
-
-/**
- * This class contains position information.
- * @param {Object} lat
- * @param {Object} lng
- * @param {Object} alt
- * @param {Object} acc
- * @param {Object} head
- * @param {Object} vel
- * @param {Object} altacc
- * @constructor
- */
-var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
-    /**
-     * The latitude of the position.
-     */
-    this.latitude = lat;
-    /**
-     * The longitude of the position,
-     */
-    this.longitude = lng;
-    /**
-     * The accuracy of the position.
-     */
-    this.accuracy = acc;
-    /**
-     * The altitude of the position.
-     */
-    this.altitude = (alt !== undefined ? alt : null);
-    /**
-     * The direction the device is moving at the position.
-     */
-    this.heading = (head !== undefined ? head : null);
-    /**
-     * The velocity with which the device is moving at the position.
-     */
-    this.speed = (vel !== undefined ? vel : null);
-
-    if (this.speed === 0 || this.speed === null) {
-        this.heading = NaN;
-    }
-
-    /**
-     * The altitude accuracy of the position.
-     */
-    this.altitudeAccuracy = (altacc !== undefined) ? altacc : null;
-};
-
-module.exports = Coordinates;
-
-});
-
-// file: lib/common/plugin/DirectoryEntry.js
-define("cordova/plugin/DirectoryEntry", function(require, exports, module) {
-
-var argscheck = require('cordova/argscheck'),
-    utils = require('cordova/utils'),
-    exec = require('cordova/exec'),
-    Entry = require('cordova/plugin/Entry'),
-    FileError = require('cordova/plugin/FileError'),
-    DirectoryReader = require('cordova/plugin/DirectoryReader');
-
-/**
- * An interface representing a directory on the file system.
- *
- * {boolean} isFile always false (readonly)
- * {boolean} isDirectory always true (readonly)
- * {DOMString} name of the directory, excluding the path leading to it (readonly)
- * {DOMString} fullPath the absolute full path to the directory (readonly)
- * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly)
- */
-var DirectoryEntry = function(name, fullPath) {
-     DirectoryEntry.__super__.constructor.call(this, false, true, name, fullPath);
-};
-
-utils.extend(DirectoryEntry, Entry);
-
-/**
- * Creates a new DirectoryReader to read entries from this directory
- */
-DirectoryEntry.prototype.createReader = function() {
-    return new DirectoryReader(this.fullPath);
-};
-
-/**
- * Creates or looks up a directory
- *
- * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
- * @param {Flags} options to create or exclusively create the directory
- * @param {Function} successCallback is called with the new entry
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
-    argscheck.checkArgs('sOFF', 'DirectoryEntry.getDirectory', arguments);
-    var win = successCallback && function(result) {
-        var entry = new DirectoryEntry(result.name, result.fullPath);
-        successCallback(entry);
-    };
-    var fail = errorCallback && function(code) {
-        errorCallback(new FileError(code));
-    };
-    exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]);
-};
-
-/**
- * Deletes a directory and all of it's contents
- *
- * @param {Function} successCallback is called with no parameters
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
-    argscheck.checkArgs('FF', 'DirectoryEntry.removeRecursively', arguments);
-    var fail = errorCallback && function(code) {
-        errorCallback(new FileError(code));
-    };
-    exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]);
-};
-
-/**
- * Creates or looks up a file
- *
- * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
- * @param {Flags} options to create or exclusively create the file
- * @param {Function} successCallback is called with the new entry
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
-    argscheck.checkArgs('sOFF', 'DirectoryEntry.getFile', arguments);
-    var win = successCallback && function(result) {
-        var FileEntry = require('cordova/plugin/FileEntry');
-        var entry = new FileEntry(result.name, result.fullPath);
-        successCallback(entry);
-    };
-    var fail = errorCallback && function(code) {
-        errorCallback(new FileError(code));
-    };
-    exec(win, fail, "File", "getFile", [this.fullPath, path, options]);
-};
-
-module.exports = DirectoryEntry;
-
-});
-
-// file: lib/common/plugin/DirectoryReader.js
-define("cordova/plugin/DirectoryReader", function(require, exports, module) {
-
-var exec = require('cordova/exec'),
-    FileError = require('cordova/plugin/FileError') ;
-
-/**
- * An interface that lists the files and directories in a directory.
- */
-function DirectoryReader(path) {
-    this.path = path || null;
-}
-
-/**
- * Returns a list of entries from a directory.
- *
- * @param {Function} successCallback is called with a list of entries
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
-    var win = typeof successCallback !== 'function' ? null : function(result) {
-        var retVal = [];
-        for (var i=0; i<result.length; i++) {
-            var entry = null;
-            if (result[i].isDirectory) {
-                entry = new (require('cordova/plugin/DirectoryEntry'))();
-            }
-            else if (result[i].isFile) {
-                entry = new (require('cordova/plugin/FileEntry'))();
-            }
-            entry.isDirectory = result[i].isDirectory;
-            entry.isFile = result[i].isFile;
-            entry.name = result[i].name;
-            entry.fullPath = result[i].fullPath;
-            retVal.push(entry);
-        }
-        successCallback(retVal);
-    };
-    var fail = typeof errorCallback !== 'function' ? null : function(code) {
-        errorCallback(new FileError(code));
-    };
-    exec(win, fail, "File", "readEntries", [this.path]);
-};
-
-module.exports = DirectoryReader;
-
-});
-
-// file: lib/common/plugin/Entry.js
-define("cordova/plugin/Entry", function(require, exports, module) {
-
-var argscheck = require('cordova/argscheck'),
-    exec = require('cordova/exec'),
-    FileError = require('cordova/plugin/FileError'),
-    Metadata = require('cordova/plugin/Metadata');
-
-/**
- * Represents a file or directory on the local file system.
- *
- * @param isFile
- *            {boolean} true if Entry is a file (readonly)
- * @param isDirectory
- *            {boolean} true if Entry is a directory (readonly)
- * @param name
- *            {DOMString} name of the file or directory, excluding the path
- *            leading to it (readonly)
- * @param fullPath
- *            {DOMString} the absolute full path to the file or directory
- *            (readonly)
- */
-function Entry(isFile, isDirectory, name, fullPath, fileSystem) {
-    this.isFile = !!isFile;
-    this.isDirectory = !!isDirectory;
-    this.name = name || '';
-    this.fullPath = fullPath || '';
-    this.filesystem = fileSystem || null;
-}
-
-/**
- * Look up the metadata of the entry.
- *
- * @param successCallback
- *            {Function} is called with a Metadata object
- * @param errorCallback
- *            {Function} is called with a FileError
- */
-Entry.prototype.getMetadata = function(successCallback, errorCallback) {
-    argscheck.checkArgs('FF', 'Entry.getMetadata', arguments);
-    var success = successCallback && function(lastModified) {
-        var metadata = new Metadata(lastModified);
-        successCallback(metadata);
-    };
-    var fail = errorCallback && function(code) {
-        errorCallback(new FileError(code));
-    };
-
-    exec(success, fail, "File", "getMetadata", [this.fullPath]);
-};
-
-/**
- * Set the metadata of the entry.
- *
- * @param successCallback
- *            {Function} is called with a Metadata object
- * @param errorCallback
- *            {Function} is called with a FileError
- * @param metadataObject
- *            {Object} keys and values to set
- */
-Entry.prototype.setMetadata = function(successCallback, errorCallback, metadataObject) {
-    argscheck.checkArgs('FFO', 'Entry.setMetadata', arguments);
-    exec(successCallback, errorCallback, "File", "setMetadata", [this.fullPath, metadataObject]);
-};
-
-/**
- * Move a file or directory to a new location.
- *
- * @param parent
- *            {DirectoryEntry} the directory to which to move this entry
- * @param newName
- *            {DOMString} new name of the entry, defaults to the current name
- * @param successCallback
- *            {Function} called with the new DirectoryEntry object
- * @param errorCallback
- *            {Function} called with a FileError
- */
-Entry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
-    argscheck.checkArgs('oSFF', 'Entry.moveTo', arguments);
-    var fail = errorCallback && function(code) {
-        errorCallback(new FileError(code));
-    };
-    // source path
-    var srcPath = this.fullPath,
-        // entry name
-        name = newName || this.name,
-        success = function(entry) {
-            if (entry) {
-                if (successCallback) {
-                    // create appropriate Entry object
-                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
-                    successCallback(result);
-                }
-            }
-            else {
-                // no Entry object returned
-                fail && fail(FileError.NOT_FOUND_ERR);
-            }
-        };
-
-    // copy
-    exec(success, fail, "File", "moveTo", [srcPath, parent.fullPath, name]);
-};
-
-/**
- * Copy a directory to a different location.
- *
- * @param parent
- *            {DirectoryEntry} the directory to which to copy the entry
- * @param newName
- *            {DOMString} new name of the entry, defaults to the current name
- * @param successCallback
- *            {Function} called with the new Entry object
- * @param errorCallback
- *            {Function} called with a FileError
- */
-Entry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
-    argscheck.checkArgs('oSFF', 'Entry.copyTo', arguments);
-    var fail = errorCallback && function(code) {
-        errorCallback(new FileError(code));
-    };
-
-        // source path
-    var srcPath = this.fullPath,
-        // entry name
-        name = newName || this.name,
-        // success callback
-        success = function(entry) {
-            if (entry) {
-                if (successCallback) {
-                    // create appropriate Entry object
-                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
-                    successCallback(result);
-                }
-            }
-            else {
-                // no Entry object returned
-                fail && fail(FileError.NOT_FOUND_ERR);
-            }
-        };
-
-    // copy
-    exec(success, fail, "File", "copyTo", [srcPath, parent.fullPath, name]);
-};
-
-/**
- * Return a URL that can be used to identify this entry.
- */
-Entry.prototype.toURL = function() {
-    // fullPath attribute contains the full URL
-    return this.fullPath;
-};
-
-/**
- * Returns a URI that can be used to identify this entry.
- *
- * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
- * @return uri
- */
-Entry.prototype.toURI = function(mimeType) {
-    console.log("DEPRECATED: Update your code to use 'toURL'");
-    // fullPath attribute contains the full URI
-    return this.toURL();
-};
-
-/**
- * Remove a file or directory. It is an error to attempt to delete a
- * directory that is not empty. It is an error to attempt to delete a
- * root directory of a file system.
- *
- * @param successCallback {Function} called with no parameters
- * @param errorCallback {Function} called with a FileError
- */
-Entry.prototype.remove = function(successCallback, errorCallback) {
-    argscheck.checkArgs('FF', 'Entry.remove', arguments);
-    var fail = errorCallback && function(code) {
-        errorCallback(new FileError(code));
-    };
-    exec(successCallback, fail, "File", "remove", [this.fullPath]);
-};
-
-/**
- * Look up the parent DirectoryEntry of this entry.
- *
- * @param successCallback {Function} called with the parent DirectoryEntry object
- * @param errorCallback {Function} called with a FileError
- */
-Entry.prototype.getParent = function(successCallback, errorCallback) {
-    argscheck.checkArgs('FF', 'Entry.getParent', arguments);
-    var win = successCallback && function(result) {
-        var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
-        var entry = new DirectoryEntry(result.name, result.fullPath);
-        successCallback(entry);
-    };
-    var fail = errorCallback && function(code) {
-        errorCallback(new FileError(code));
-    };
-    exec(win, fail, "File", "getParent", [this.fullPath]);
-};
-
-module.exports = Entry;
-
-});
-
-// file: lib/common/plugin/File.js
-define("cordova/plugin/File", function(require, exports, module) {
-
-/**
- * Constructor.
- * name {DOMString} name of the file, without path information
- * fullPath {DOMString} the full path of the file, including the name
- * type {DOMString} mime type
- * lastModifiedDate {Date} last modified date
- * size {Number} size of the file in bytes
- */
-
-var File = function(name, fullPath, type, lastModifiedDate, size){
-    this.name = name || '';
-    this.fullPath = fullPath || null;
-    this.type = type || null;
-    this.lastModifiedDate = lastModifiedDate || null;
-    this.size = size || 0;
-
-    // These store the absolute start and end for slicing the file.
-    this.start = 0;
-    this.end = this.size;
-};
-
-/**
- * Returns a "slice" of the file. Since Cordova Files don't contain the actual
- * content, this really returns a File with adjusted start and end.
- * Slices of slices are supported.
- * start {Number} The index at which to start the slice (inclusive).
- * end {Number} The index at which to end the slice (exclusive).
- */
-File.prototype.slice = function(start, end) {
-    var size = this.end - this.start;
-    var newStart = 0;
-    var newEnd = size;
-    if (arguments.length) {
-        if (start < 0) {
-            newStart = Math.max(size + start, 0);
-        } else {
-            newStart = Math.min(size, start);
-        }
-    }
-
-    if (arguments.length >= 2) {
-        if (end < 0) {
-            newEnd = Math.max(size + end, 0);
-        } else {
-            newEnd = Math.min(end, size);
-        }
-    }
-
-    var newFile = new File(this.name, this.fullPath, this.type, this.lastModifiedData, this.size);
-    newFile.start = this.start + newStart;
-    newFile.end = this.start + newEnd;
-    return newFile;
-};
-
-
-module.exports = File;
-
-});
-
-// file: lib/common/plugin/FileEntry.js
-define("cordova/plugin/FileEntry", function(require, exports, module) {
-
-var utils = require('cordova/utils'),
-    exec = require('cordova/exec'),
-    Entry = require('cordova/plugin/Entry'),
-    FileWriter = require('cordova/plugin/FileWriter'),
-    File = require('cordova/plugin/File'),
-    FileError = require('cordova/plugin/FileError');
-
-/**
- * An interface representing a file on the file system.
- *
- * {boolean} isFile always true (readonly)
- * {boolean} isDirectory always false (readonly)
- * {DOMString} name of the file, excluding the path leading to it (readonly)
- * {DOMString} fullPath the absolute full path to the file (readonly)
- * {FileSystem} filesystem on which the file resides (readonly)
- */
-var FileEntry = function(name, fullPath) {
-     FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath]);
-};
-
-utils.extend(FileEntry, Entry);
-
-/**
- * Creates a new FileWriter associated with the file that this FileEntry represents.
- *
- * @param {Function} successCallback is called with the new FileWriter
- * @param {Function} errorCallback is called with a FileError
- */
-FileEntry.prototype.createWriter = function(successCallback, errorCallback) {
-    this.file(function(filePointer) {
-        var writer = new FileWriter(filePointer);
-
-        if (writer.fileName === null || writer.fileName === "") {
-            errorCallback && errorCallback(new FileError(FileError.INVALID_STATE_ERR));
-        } else {
-            successCallback && successCallback(writer);
-        }
-    }, errorCallback);
-};
-
-/**
- * Returns a File that represents the current state of the file that this FileEntry represents.
- *
- * @param {Function} successCallback is called with the new File object
- * @param {Function} errorCallback is called with a FileError
- */
-FileEntry.prototype.file = function(successCallback, errorCallback) {
-    var win = successCallback && function(f) {
-        var file = new File(f.name, f.fullPath, f.type, f.lastModifiedDate, f.size);
-        successCallback(file);
-    };
-    var fail = errorCallback && function(code) {
-        errorCallback(new FileError(code));
-    };
-    exec(win, fail, "File", "getFileMetadata", [this.fullPath]);
-};
-
-
-module.exports = FileEntry;
-
-});
-
-// file: lib/common/plugin/FileError.js
-define("cordova/plugin/FileError", function(require, exports, module) {
-
-/**
- * FileError
- */
-function FileError(error) {
-  this.code = error || null;
-}
-
-// File error codes
-// Found in DOMException
-FileError.NOT_FOUND_ERR = 1;
-FileError.SECURITY_ERR = 2;
-FileError.ABORT_ERR = 3;
-
-// Added by File API specification
-FileError.NOT_READABLE_ERR = 4;
-FileError.ENCODING_ERR = 5;
-FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
-FileError.INVALID_STATE_ERR = 7;
-FileError.SYNTAX_ERR = 8;
-FileError.INVALID_MODIFICATION_ERR = 9;
-FileError.QUOTA_EXCEEDED_ERR = 10;
-FileError.TYPE_MISMATCH_ERR = 11;
-FileError.PATH_EXISTS_ERR = 12;
-
-module.exports = FileError;
-
-});
-
-// file: lib/common/plugin/FileReader.js
-define("cordova/plugin/FileReader", function(require, exports, module) {
-
-var exec = require('cordova/exec'),
-    modulemapper = require('cordova/modulemapper'),
-    utils = require('cordova/utils'),
-    File = require('cordova/plugin/File'),
-    FileError = require('cordova/plugin/FileError'),
-    ProgressEvent = require('cordova/plugin/ProgressEvent'),
-    origFileReader = modulemapper.getOriginalSymbol(this, 'FileReader');
-
-/**
- * This class reads the mobile device file system.
- *
- * For Android:
- *      The root directory is the root of the file system.
- *      To read from the SD card, the file name is "sdcard/my_file.txt"
- * @constructor
- */
-var FileReader = function() {
-    this._readyState = 0;
-    this._error = null;
-    this._result = null;
-    this._fileName = '';
-    this._realReader = origFileReader ? new origFileReader() : {};
-};
-
-// States
-FileReader.EMPTY = 0;
-FileReader.LOADING = 1;
-FileReader.DONE = 2;
-
-utils.defineGetter(FileReader.prototype, 'readyState', function() {
-    return this._fileName ? this._readyState : this._realReader.readyState;
-});
-
-utils.defineGetter(FileReader.prototype, 'error', function() {
-    return this._fileName ? this._error: this._realReader.error;
-});
-
-utils.defineGetter(FileReader.prototype, 'result', function() {
-    return this._fileName ? this._result: this._realReader.result;
-});
-
-function defineEvent(eventName) {
-    utils.defineGetterSetter(FileReader.prototype, eventName, function() {
-        return this._realReader[eventName] || null;
-    }, function(value) {
-        this._realReader[eventName] = value;
-    });
-}
-defineEvent('onloadstart');    // When the read starts.
-defineEvent('onprogress');     // While reading (and decoding) file or fileBlob data, and reporting partial file data (progress.loaded/progress.total)
-defineEvent('onload');         // When the read has successfully completed.
-defineEvent('onerror');        // When the read has failed (see errors).
-defineEvent('onloadend');      // When the request has completed (either in success or failure).
-defineEvent('onabort');        // When the read has been aborted. For instance, by invoking the abort() method.
-
-function initRead(reader, file) {
-    // Already loading something
-    if (reader.readyState == FileReader.LOADING) {
-      throw new FileError(FileError.INVALID_STATE_ERR);
-    }
-
-    reader._result = null;
-    reader._error = null;
-    reader._readyState = FileReader.LOADING;
-
-    if (typeof file == 'string') {
-        // Deprecated in Cordova 2.4.
-        console.warn('Using a string argument with FileReader.readAs functions is deprecated.');
-        reader._fileName = file;
-    } else if (typeof file.fullPath == 'string') {
-        reader._fileName = file.fullPath;
-    } else {
-        reader._fileName = '';
-        return true;
-    }
-
-    reader.onloadstart && reader.onloadstart(new ProgressEvent("loadstart", {target:reader}));
-}
-
-/**
- * Abort reading file.
- */
-FileReader.prototype.abort = function() {
-    if (origFileReader && !this._fileName) {
-        return this._realReader.abort();
-    }
-    this._result = null;
-
-    if (this._readyState == FileReader.DONE || this._readyState == FileReader.EMPTY) {
-      return;
-    }
-
-    this._readyState = FileReader.DONE;
-
-    // If abort callback
-    if (typeof this.onabort === 'function') {
-        this.onabort(new ProgressEvent('abort', {target:this}));
-    }
-    // If load end callback
-    if (typeof this.onloadend === 'function') {
-        this.onloadend(new ProgressEvent('loadend', {target:this}));
-    }
-};
-
-/**
- * Read text file.
- *
- * @param file          {File} File object containing file properties
- * @param encoding      [Optional] (see http://www.iana.org/assignments/character-sets)
- */
-FileReader.prototype.readAsText = function(file, encoding) {
-    if (initRead(this, file)) {
-        return this._realReader.readAsText(file, encoding);
-    }
-
-    // Default encoding is UTF-8
-    var enc = encoding ? encoding : "UTF-8";
-    var me = this;
-    var execArgs = [this._fileName, enc, file.start, file.end];
-
-    // Read file
-    exec(
-        // Success callback
-        function(r) {
-            // If DONE (cancelled), then don't do anything
-            if (me._readyState === FileReader.DONE) {
-                return;
-            }
-
-            // Save result
-            me._result = r;
-
-            // If onload callback
-            if (typeof me.onload === "function") {
-                me.onload(new ProgressEvent("load", {target:me}));
-            }
-
-            // DONE state
-            me._readyState = FileReader.DONE;
-
-            // If onloadend callback
-            if (typeof me.onloadend === "function") {
-                me.onloadend(new ProgressEvent("loadend", {target:me}));
-            }
-        },
-        // Error callback
-        function(e) {
-            // If DONE (cancelled), then don't do anything
-            if (me._readyState === FileReader.DONE) {
-                return;
-            }
-
-            // DONE state
-            me._readyState = FileReader.DONE;
-
-            // null result
-            me._result = null;
-
-            // Save error
-            me._error = new FileError(e);
-
-            // If onerror callback
-            if (typeof me.onerror === "function") {
-                me.onerror(new ProgressEvent("error", {target:me}));
-            }
-
-            // If onloadend callback
-            if (typeof me.onloadend === "function") {
-                me.onloadend(new ProgressEvent("loadend", {target:me}));
-            }
-        }, "File", "readAsText", execArgs);
-};
-
-
-/**
- * Read file and return data as a base64 encoded data url.
- * A data url is of the form:
- *      data:[<mediatype>][;base64],<data>
- *
- * @param file          {File} File object containing file properties
- */
-FileReader.prototype.readAsDataURL = function(file) {
-    if (initRead(this, file)) {
-        return this._realReader.readAsDataURL(file);
-    }
-
-    var me = this;
-    var execArgs = [this._fileName, file.start, file.end];
-
-    // Read file
-    exec(
-        // Success callback
-        function(r) {
-            // If DONE (cancelled), then don't do anything
-            if (me._readyState === FileReader.DONE) {
-                return;
-            }
-
-            // DONE state
-            me._readyState = FileReader.DONE;
-
-            // Save result
-            me._result = r;
-
-            // If onload callback
-            if (typeof me.onload === "function") {
-                me.onload(new ProgressEvent("load", {target:me}));
-            }
-
-            // If onloadend callback
-            if (typeof me.onloadend === "function") {
-                me.onloadend(new ProgressEvent("loadend", {target:me}));
-            }
-        },
-        // Error callback
-        function(e) {
-            // If DONE (cancelled), then don't do anything
-            if (me._readyState === FileReader.DONE) {
-                return;
-            }
-
-            // DONE state
-            me._readyState = FileReader.DONE;
-
-            me._result = null;
-
-            // Save error
-            me._error = new FileError(e);
-
-            // If onerror callback
-            if (typeof me.onerror === "function") {
-                me.onerror(new ProgressEvent("error", {target:me}));
-            }
-
-            // If onloadend callback
-            if (typeof me.onloadend === "function") {
-                me.onloadend(new ProgressEvent("loadend", {target:me}));
-            }
-        }, "File", "readAsDataURL", execArgs);
-};
-
-/**
- * Read file and return data as a binary data.
- *
- * @param file          {File} File object containing file properties
- */
-FileReader.prototype.readAsBinaryString = function(file) {
-    if (initRead(this, file)) {
-        return this._realReader.readAsBinaryString(file);
-    }
-
-    var me = this;
-    var execArgs = [this._fileName, file.start, file.end];
-
-    // Read file
-    exec(
-        // Success callback
-        function(r) {
-            // If DONE (cancelled), then don't do anything
-            if (me._readyState === FileReader.DONE) {
-                return;
-            }
-
-            // DONE state
-            me._readyState = FileReader.DONE;
-
-            me._result = r;
-
-            // If onload callback
-            if (typeof me.onload === "function") {
-                me.onload(new ProgressEvent("load", {target:me}));
-            }
-
-            // If onloadend callback
-            if (typeof me.onloadend === "function") {
-                me.onloadend(new ProgressEvent("loadend", {target:me}));
-            }
-        },
-        // Error callback
-        function(e) {
-            // If DONE (cancelled), then don't do anything
-            if (me._readyState === FileReader.DONE) {
-                return;
-            }
-
-            // DONE state
-            me._readyState = FileReader.DONE;
-
-            me._result = null;
-
-            // Save error
-            me._error = new FileError(e);
-
-            // If onerror callback
-            if (typeof me.onerror === "function") {
-                me.onerror(new ProgressEvent("error", {target:me}));
-            }
-
-            // If onloadend callback
-            if (typeof me.onloadend === "function") {
-                me.onloadend(new ProgressEvent("loadend", {target:me}));
-            }
-        }, "File", "readAsBinaryString", execArgs);
-};
-
-/**
- * Read file and return data as a binary data.
- *
- * @param file          {File} File object containing file properties
- */
-FileReader.prototype.readAsArrayBuffer = function(file) {
-    if (initRead(this, file)) {
-        return this._realReader.readAsArrayBuffer(file);
-    }
-
-    var me = this;
-    var execArgs = [this._fileName, file.start, file.end];
-
-    // Read file
-    exec(
-        // Success callback
-        function(r) {
-            // If DONE (cancelled), then don't do anything
-            if (me._readyState === FileReader.DONE) {
-                return;
-            }
-
-            // DONE state
-            me._readyState = FileReader.DONE;
-
-            me._result = r;
-
-            // If onload callback
-            if (typeof me.onload === "function") {
-                me.onload(new ProgressEvent("load", {target:me}));
-            }
-
-            // If onloadend callback
-            if (typeof me.onloadend === "function") {
-     

<TRUNCATED>

[30/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/Globalization.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/Globalization.cs b/lib/cordova-wp7/templates/standalone/Plugins/Globalization.cs
new file mode 100644
index 0000000..1528807
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/Globalization.cs
@@ -0,0 +1,1178 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Provides information about system locale, culture settings, number formats, ect.
+    /// </summary>
+    public class Globalization : BaseCommand
+    {
+
+        #region Globalization errors
+
+        /// <summary>
+        /// Globalization error codes.
+        /// </summary>
+        public enum ErrorCode : int
+        {
+            UnknownError = 0,
+            FormattingError = 1,
+            ParsingError = 2,
+            PatternError = 3
+        }
+
+        /// <summary>
+        /// Represents globalization error object.
+        /// </summary>
+        [DataContract]
+        public class GlobalizationError
+        {
+            #region Error messages
+            /// <summary>
+            /// Error messages
+            /// </summary>        
+            public const string UnknownError = "UNKNOWN_ERROR";
+            public const string FormattingError = "FORMATTIN_ERROR";
+            public const string ParsingError = "PARSING_ERROR";
+            public const string PatternError = "PATTERN_ERROR";
+
+            #endregion
+
+            /// <summary>
+            /// Error code
+            /// </summary>
+            [DataMember(Name = "code", IsRequired = false)]
+            public ErrorCode Code { get; set; }
+
+            /// <summary>
+            /// Error message
+            /// </summary>
+            [DataMember(Name = "message", IsRequired = false)]
+            public string Message { get; set; }
+
+            /// <summary>
+            /// Default constructor
+            /// </summary>
+            public GlobalizationError()
+            {
+                this.Code = ErrorCode.UnknownError;
+                this.Message = UnknownError;
+            }
+
+            /// <summary>
+            /// Constructor setting error code
+            /// </summary>
+            public GlobalizationError(ErrorCode error)
+            {
+                this.Code = error;
+
+                switch (error)
+                {
+                    case ErrorCode.ParsingError:
+                        {
+                            this.Message = ParsingError;
+                            break;
+                        }
+                    case ErrorCode.FormattingError:
+                        {
+                            this.Message = FormattingError;
+                            break;
+                        }
+                    case ErrorCode.PatternError:
+                        {
+                            this.Message = PatternError;
+                            break;
+                        }
+                    default:
+                        {
+                            this.Message = UnknownError;
+                            break;
+                        }
+                }
+            }
+        }
+
+        #endregion
+
+        #region Globalization options
+
+        /// <summary>
+        /// Represents globalization options.
+        /// </summary>
+        [DataContract]
+        public class GlobalizationOptions
+        {
+            #region available option values
+            /// <summary>
+            /// Number pattern types.
+            /// </summary>        
+            public const string Percent = "percent";
+            public const string Currency = "currency";
+            public const string Decimal = "decimal";
+
+            /// <summary>
+            /// Format length types
+            /// </summary>        
+            public const string Short = "short";
+            public const string Medium = "medium";
+            public const string Long = "long";
+            public const string Full = "full";
+
+            /// <summary>
+            /// Selector types
+            /// </summary>        
+            public const string TimeSelector = "time";
+            public const string DateSelector = "date";
+            public const string DateAndTimeSelector = "date and time";
+
+            /// <summary>
+            /// Date name types
+            /// </summary>        
+            public const string Narrow = "narrow";
+            public const string Wide = "wide";
+
+            /// <summary>
+            /// Date name items
+            /// </summary>        
+            public const string Months = "months";
+            public const string Days = "days";
+
+            #endregion
+
+            /// <summary>
+            /// Additional options
+            /// </summary>
+            [DataMember(Name = "options", IsRequired = false)]
+            public Options AdditionalOptions { get; set; }
+
+            /// <summary>
+            /// Date to convert
+            /// </summary>
+            [DataMember(Name = "date", IsRequired = false)]
+            public long Date { get; set; }
+
+            /// <summary>
+            /// Date as stirng
+            /// </summary>
+            [DataMember(Name = "dateString", IsRequired = false)]
+            public string DateString { get; set; }
+
+            /// <summary>
+            /// Currency code
+            /// </summary>
+            [DataMember(Name = "currencyCode", IsRequired = false)]
+            public string CurrencyCode { get; set; }
+
+            /// <summary>
+            /// Number as string
+            /// </summary>
+            [DataMember(Name = "numberString", IsRequired = false)]
+            public string NumberString { get; set; }
+
+            /// <summary>
+            /// Number to convert
+            /// </summary>
+            [DataMember(Name = "number", IsRequired = false)]
+            public double Number { get; set; }
+        }
+
+        /// <summary>
+        /// Represents additional options
+        /// </summary>
+        [DataContract]
+        public class Options
+        {
+            /// <summary>
+            /// Pattern type
+            /// </summary>
+            [DataMember(Name = "type", IsRequired = false)]
+            public string Type { get; set; }
+
+            /// <summary>
+            /// Format length
+            /// </summary>
+            [DataMember(Name = "formatLength", IsRequired = false)]
+            public string FormatLength { get; set; }
+
+            /// <summary>
+            /// Selector
+            /// </summary>
+            [DataMember(Name = "selector", IsRequired = false)]
+            public string Selector { get; set; }
+
+            /// <summary>
+            /// Date name item
+            /// </summary>
+            [DataMember(Name = "item", IsRequired = false)]
+            public string Item { get; set; }
+        }
+
+        #endregion
+
+        #region returned objects
+
+        #region Number pattern object
+
+        /// <summary>
+        /// Represents number pattern
+        /// </summary>
+        [DataContract]
+        public class NumberPattern
+        {
+            /// <summary>
+            /// Pattern
+            /// </summary>
+            [DataMember(Name = "pattern", IsRequired = false)]
+            public string Pattern { get; set; }
+
+            /// <summary>
+            /// Symbol
+            /// </summary>
+            [DataMember(Name = "symbol", IsRequired = false)]
+            public string Symbol { get; set; }
+
+            /// <summary>
+            /// Fraction
+            /// </summary>
+            [DataMember(Name = "fraction", IsRequired = false)]
+            public int Fraction { get; set; }
+
+            /// <summary>
+            /// Positive
+            /// </summary>
+            [DataMember(Name = "positive", IsRequired = false)]
+            public string Positive { get; set; }
+
+            /// <summary>
+            /// Negative
+            /// </summary>
+            [DataMember(Name = "negative", IsRequired = false)]
+            public string Negative { get; set; }
+
+            /// <summary>
+            /// Rounding
+            /// </summary>
+            [DataMember(Name = "rounding", IsRequired = false)]
+            public int Rounding { get; set; }
+
+            /// <summary>
+            /// Decimal
+            /// </summary>
+            [DataMember(Name = "decimal", IsRequired = false)]
+            public string Decimal { get; set; }
+
+            /// <summary>
+            /// Grouping
+            /// </summary>
+            [DataMember(Name = "grouping", IsRequired = false)]
+            public string Grouping { get; set; }
+
+            /// <summary>
+            /// Constructor of the class
+            /// </summary>
+            /// <param name="pattern"></param>
+            /// <param name="symbol"></param>
+            /// <param name="fraction"></param>
+            /// <param name="positive"></param>
+            /// <param name="negative"></param>
+            /// <param name="rounding"></param>
+            /// <param name="dec"></param>
+            /// <param name="grouping"></param>
+            public NumberPattern(string pattern, string symbol, int fraction, string positive, string negative, int rounding, string dec, string grouping)
+            {
+                this.Pattern = pattern;
+                this.Symbol = symbol;
+                this.Fraction = fraction;
+                this.Positive = positive;
+                this.Negative = negative;
+                this.Rounding = rounding;
+                this.Decimal = dec;
+                this.Grouping = grouping;
+            }
+        }
+        #endregion
+
+        #region Date format object
+
+        /// <summary>
+        /// Represents date format
+        /// </summary>
+        [DataContract]
+        public class DateFormat
+        {
+            /// <summary>
+            /// Year
+            /// </summary>
+            [DataMember(Name = "year", IsRequired = false)]
+            public int Year { get; set; }
+
+            /// <summary>
+            /// Month
+            /// </summary>
+            [DataMember(Name = "month", IsRequired = false)]
+            public int Month { get; set; }
+
+            /// <summary>
+            /// Day
+            /// </summary>
+            [DataMember(Name = "day", IsRequired = false)]
+            public int Day { get; set; }
+
+            /// <summary>
+            /// Hour
+            /// </summary>
+            [DataMember(Name = "hour", IsRequired = false)]
+            public int Hour { get; set; }
+
+            /// <summary>
+            /// Minute
+            /// </summary>
+            [DataMember(Name = "minute", IsRequired = false)]
+            public int Minute { get; set; }
+
+            /// <summary>
+            /// Second
+            /// </summary>
+            [DataMember(Name = "second", IsRequired = false)]
+            public int Second { get; set; }
+
+            /// <summary>
+            /// Millisecond
+            /// </summary>
+            [DataMember(Name = "millisecond", IsRequired = false)]
+            public int Millisecond { get; set; }
+
+            public DateFormat(int year, int month, int day, int hour, int minute, int second, int millisecond)
+            {
+                this.Year = year;
+                this.Month = month;
+                this.Day = day;
+                this.Hour = hour;
+                this.Minute = minute;
+                this.Millisecond = millisecond;
+            }
+
+        }
+        #endregion
+
+        #region Date pattern object
+
+        /// <summary>
+        /// Represents date pattern object
+        /// </summary>
+        [DataContract]
+        public class DatePattern
+        {
+
+            /// <summary>
+            /// Date pattern
+            /// </summary>
+            [DataMember(Name = "pattern", IsRequired = false)]
+            public string Pattern { get; set; }
+
+            /// <summary>
+            /// TimeZone
+            /// </summary>
+            [DataMember(Name = "timezone", IsRequired = false)]
+            public string TimeZone { get; set; }
+
+            /// <summary>
+            /// UTC offset
+            /// </summary>
+            [DataMember(Name = "utc_offset", IsRequired = false)]
+            public double UtcOffset { get; set; }
+
+            /// <summary>
+            /// Dst offset
+            /// </summary>
+            [DataMember(Name = "dst_offset", IsRequired = false)]
+            public double DstOffset { get; set; }
+
+            /// <summary>
+            /// Constructor of the class
+            /// </summary>
+            /// <param name="pattern"></param>
+            /// <param name="timezone"></param>
+            /// <param name="utcOffset"></param>
+            /// <param name="dstOffset"></param>
+            public DatePattern(string pattern, string timezone, double utcOffset, double dstOffset)
+            {
+                this.Pattern = pattern;
+                this.TimeZone = timezone;
+                this.UtcOffset = utcOffset;
+                this.DstOffset = dstOffset;
+            }
+
+        }
+
+        #endregion
+
+        #endregion
+
+        #region Locale info
+
+        /// <summary>
+        /// Gets the string identifier for the client's current locale setting.
+        /// </summary>
+        /// <param name="options"></param>               
+        public void getLocaleName(string options)
+        {
+            try
+            {
+                var locale = RegionInfo.CurrentRegion.TwoLetterISORegionName;
+                PluginResult result = new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(locale));
+                this.DispatchCommandResult(result);
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+            }
+        }
+
+        /// <summary>
+        /// Gets the string identifier for the client's current language.
+        /// </summary>
+        /// <param name="options"></param>               
+        public void getPreferredLanguage(string options)
+        {
+            try
+            {
+                var language = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
+                PluginResult result = new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(language));
+                this.DispatchCommandResult(result);
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+            }
+        }
+
+        #endregion
+
+        #region Date and time info
+
+        /// <summary>
+        /// Gets whether daylight savings time is in effect for a given date using the client's 
+        /// time zone and calendar.        
+        /// </summary>
+        /// <param name="opitons">Date to daylight savings check.</param>
+        public void isDayLightSavingsTime(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                DateTime start = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+                DateTime date = start.AddMilliseconds(globalOptions.Date).ToLocalTime();
+                TimeZoneInfo localZone = TimeZoneInfo.Local;
+                bool isDaylightSavingTime = localZone.IsDaylightSavingTime(date);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(isDaylightSavingTime, "dst")));
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+            }
+        }
+
+        /// <summary>
+        /// Gets the first day of the week according to the client's user preferences and calendar.
+        /// The days of the week are numbered starting from 1 where 1 is considered to be Sunday.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getFirstDayOfWeek(string options)
+        {
+            try
+            {
+                // DateTimeFormat returns days of the week numbered from zero, so we have to increase returned value by one.
+                var firstDayOfWeek = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek + 1;
+                PluginResult result = new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(firstDayOfWeek));
+                this.DispatchCommandResult(result);
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+            }
+        }
+
+        #endregion
+
+        #region Formatting
+
+        /// <summary>
+        /// Gets a date formatted as a string according to the client's user preferences and calendar using the time zone of the client. 
+        /// </summary>
+        /// <param name="options"></param>
+        public void dateToString(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                DateTime start = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+                DateTime date = start.AddMilliseconds(globalOptions.Date).ToLocalTime();
+
+                string format = "{0:M/dd/yy H:m:s}"; //short datetime by default
+                int formatLength = 0; //default format
+                int selector = 0; //default selector 
+
+                if (globalOptions.AdditionalOptions != null)
+                {
+                    if (globalOptions.AdditionalOptions.FormatLength != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.FormatLength;
+
+                        if (t.Equals(GlobalizationOptions.Full))
+                        {
+                            formatLength++;
+                        }
+                    }
+
+                    if (globalOptions.AdditionalOptions.Selector != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.Selector;
+
+                        if (t.Equals(GlobalizationOptions.DateSelector))
+                        {
+                            selector += 10;
+                        }
+                        else if (t.Equals(GlobalizationOptions.TimeSelector))
+                        {
+                            selector += 20;
+                        }
+                    }
+
+                    //determine return value
+                    int method = formatLength + selector;
+
+                    switch (method)
+                    {
+                        case 1: // full datetime
+                            {
+                                format = "{0:MMMM/dddd/yyyy HH:mm:ss tt}";
+                                break;
+                            }
+                        case 10: // short date
+                            {
+                                format = "{0:d}";
+                                break;
+                            }
+                        case 11: // full date
+                            {
+                                format = "{0:D}";
+                                break;
+                            }
+                        case 20: // short time
+                            {
+                                format = "{0:t}";
+                                break;
+                            }
+                        case 21: // full time
+                            {
+                                format = "{0:T}";
+                                break;
+                            }
+                        default: // short datetime
+                            {
+                                format = "{0:M/dd/yy H:m:s}";
+                                break;
+                            }
+                    }
+                }
+
+                string formattedValue = string.Format(CultureInfo.CurrentCulture, format, date);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(formattedValue)));
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.FormattingError)));
+            }
+        }
+
+        /// <summary>
+        /// Parses a date formatted as a string according to the client's user preferences and calendar using the time zone of the client and returns the corresponding date object
+        /// </summary>
+        /// <param name="options"></param>
+        public void stringToDate(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                if (string.IsNullOrEmpty(globalOptions.DateString))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                string format = "M/dd/yy H:m:s"; // short datetime by default
+                int formatLength = 0; //default format
+                int selector = 0; //default selector 
+
+                if (globalOptions.AdditionalOptions != null)
+                {
+                    if (globalOptions.AdditionalOptions.FormatLength != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.FormatLength;
+
+                        if (t.Equals(GlobalizationOptions.Full))
+                        {
+                            formatLength++;
+                        }
+                    }
+
+                    if (globalOptions.AdditionalOptions.Selector != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.Selector;
+
+                        if (t.Equals(GlobalizationOptions.DateSelector))
+                        {
+                            selector += 10;
+                        }
+                        else if (t.Equals(GlobalizationOptions.TimeSelector))
+                        {
+                            selector += 20;
+                        }
+                    }
+
+                    //determine return value
+                    int method = formatLength + selector;
+
+                    switch (method)
+                    {
+                        case 1: // full datetime
+                            {
+                                format = "MMMM/dddd/yyyy HH:mm:ss tt";
+                                break;
+                            }
+                        case 10: // short date
+                            {
+                                format = "d";
+                                break;
+                            }
+                        case 11: // full date
+                            {
+                                format = "D";
+                                break;
+                            }
+                        case 20: // short time
+                            {
+                                format = "t";
+                                break;
+                            }
+                        case 21: // full time
+                            {
+                                format = "T";
+                                break;
+                            }
+                        default: // short datetime
+                            {
+                                format = "M/dd/yy H:m:s";
+                                break;
+                            }
+                    }
+                }
+
+                DateTime date = DateTime.ParseExact(globalOptions.DateString, format, CultureInfo.CurrentCulture);
+                DateFormat dateFormat = new DateFormat(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Millisecond);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, dateFormat));
+
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.ParsingError)));
+            }
+        }
+
+        /// <summary>
+        /// Gets a pattern string for formatting and parsing dates according to the client's user preferences.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getDatePattern(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                DateTimeFormatInfo dateFormatInfo = DateTimeFormatInfo.CurrentInfo;
+                string pattern = dateFormatInfo.FullDateTimePattern; // full datetime by default
+                int formatLength = 0; //default format
+                int selector = 0; //default selector 
+
+                if (globalOptions.AdditionalOptions != null)
+                {
+                    if (globalOptions.AdditionalOptions.FormatLength != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.FormatLength;
+
+                        if (t.Equals(GlobalizationOptions.Full))
+                        {
+                            formatLength++;
+                        }
+                    }
+
+                    if (globalOptions.AdditionalOptions.Selector != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.Selector;
+
+                        if (t.Equals(GlobalizationOptions.DateSelector))
+                        {
+                            selector += 10;
+                        }
+                        else if (t.Equals(GlobalizationOptions.TimeSelector))
+                        {
+                            selector += 20;
+                        }
+                    }
+
+                    //determine return value
+                    int method = formatLength + selector;
+
+                    switch (method)
+                    {
+                        case 1: // full datetime
+                            {
+                                pattern = dateFormatInfo.FullDateTimePattern;
+                                break;
+                            }
+                        case 10: // short date
+                            {
+                                pattern = dateFormatInfo.ShortDatePattern;
+                                break;
+                            }
+                        case 11: // full date
+                            {
+                                pattern = dateFormatInfo.LongDatePattern;
+                                break;
+                            }
+                        case 20: // short time
+                            {
+                                pattern = dateFormatInfo.ShortTimePattern;
+                                break;
+                            }
+                        case 21: // full time
+                            {
+                                pattern = dateFormatInfo.LongTimePattern;
+                                break;
+                            }
+                        default: // short datetime
+                            {
+                                // Seems like C# doesn't support short datetime pattern so we use full format
+                                // http://msdn.microsoft.com/en-us/library/1at0z4ew%28v=vs.71%29.aspx
+                                pattern = dateFormatInfo.FullDateTimePattern;
+                                break;
+                            }
+                    }
+                }
+
+                TimeZoneInfo localZone = TimeZoneInfo.Local;
+                DatePattern datePattern = new DatePattern(pattern, localZone.DisplayName, localZone.BaseUtcOffset.TotalSeconds, 0);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, datePattern));
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.PatternError)));
+            }
+        }
+
+        /// <summary>
+        /// Gets an array of either the names of the months or days of the week according to the client's user preferences and calendar.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getDateNames(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                int type = 0; //default wide
+                int item = 0; //default months 
+
+                if (globalOptions.AdditionalOptions != null)
+                {
+                    if (globalOptions.AdditionalOptions.Type != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.Type;
+
+                        if (t.Equals(GlobalizationOptions.Narrow))
+                        {
+                            type++;
+                        }
+                    }
+
+                    if (globalOptions.AdditionalOptions.Item != null)
+                    {
+                        string t = globalOptions.AdditionalOptions.Item;
+
+                        if (t.Equals(GlobalizationOptions.Days))
+                        {
+                            item += 10;
+                        }
+                    }
+                }
+
+                //determine return value
+                int method = item + type;
+                string[] namesArray;
+                CultureInfo currentCulture = CultureInfo.CurrentCulture;
+
+                if (method == 1) //months and narrow
+                {
+                    namesArray = currentCulture.DateTimeFormat.AbbreviatedMonthNames;
+                }
+                else if (method == 10) //days and wide
+                {
+                    namesArray = currentCulture.DateTimeFormat.DayNames;
+                }
+                else if (method == 11) //days and narrow
+                {
+                    namesArray = currentCulture.DateTimeFormat.AbbreviatedDayNames;
+                }
+                else //default: months and wide
+                {
+                    namesArray = currentCulture.DateTimeFormat.MonthNames;
+                }
+
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(namesArray)));
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+            }
+        }
+
+        /// <summary>
+        /// Gets a number formatted as a string according to the client's user preferences. 
+        /// </summary>
+        /// <param name="options"></param>
+        public void numberToString(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                string format = string.Empty;
+                string numberFormatType = (globalOptions.AdditionalOptions == null || string.IsNullOrEmpty(globalOptions.AdditionalOptions.Type)) ?
+                    GlobalizationOptions.Decimal : globalOptions.AdditionalOptions.Type;
+
+                switch (numberFormatType)
+                {
+                    case GlobalizationOptions.Percent:
+                        {
+                            format = "{0:p}";
+                            break;
+                        }
+
+                    case GlobalizationOptions.Currency:
+                        {
+                            format = "{0:c}";
+                            break;
+                        }
+
+                    default:
+                        {
+                            format = "{0:f}";
+                            break;
+                        }
+                }
+
+                string formattedValue = string.Format(CultureInfo.CurrentCulture, format, globalOptions.Number);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(formattedValue)));
+
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.FormattingError)));
+            }
+        }
+
+        /// <summary>
+        /// Gets a number formatted as a string according to the client's user preferences and returns the corresponding number.
+        /// </summary>
+        /// <param name="options"></param>
+        public void stringToNumber(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                if (string.IsNullOrEmpty(globalOptions.NumberString))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                string numberString = globalOptions.NumberString;
+                string numberFormatType = (globalOptions.AdditionalOptions == null || string.IsNullOrEmpty(globalOptions.AdditionalOptions.Type)) ?
+                    GlobalizationOptions.Decimal : globalOptions.AdditionalOptions.Type;
+
+                NumberStyles numberStyle;
+
+                switch (numberFormatType)
+                {
+                    case GlobalizationOptions.Percent:
+                        {
+                            numberStyle = NumberStyles.Any;
+                            numberString = numberString.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, "");
+                            break;
+                        }
+
+                    case GlobalizationOptions.Currency:
+                        {
+                            numberStyle = NumberStyles.Currency;
+                            break;
+                        }
+
+                    default:
+                        {
+                            numberStyle = NumberStyles.Number;
+                            break;
+                        }
+                }
+
+                double value = double.Parse(numberString, numberStyle, CultureInfo.CurrentCulture);
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(value)));
+
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.ParsingError)));
+            }
+        }
+
+
+        /// <summary>
+        /// Gets a pattern string for formatting and parsing numbers according to the client's user preferences.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getNumberPattern(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                CultureInfo cultureInfo = CultureInfo.CurrentCulture;
+                NumberFormatInfo formatInfo = cultureInfo.NumberFormat;
+                string numberFormatType = (globalOptions.AdditionalOptions == null || string.IsNullOrEmpty(globalOptions.AdditionalOptions.Type)) ?
+                    GlobalizationOptions.Decimal : globalOptions.AdditionalOptions.Type;
+                NumberPattern pattern = null;
+                string symbol;
+
+                // TODO find out how to get format pattern and the number of fraction digits
+                switch (numberFormatType)
+                {
+                    case GlobalizationOptions.Percent:
+                        {
+                            symbol = formatInfo.PercentSymbol;
+                            pattern = new NumberPattern("", symbol, 0, formatInfo.PercentPositivePattern.ToString(), formatInfo.PercentNegativePattern.ToString(), 0, formatInfo.PercentDecimalSeparator, formatInfo.PercentGroupSeparator);
+                            break;
+                        }
+                    case GlobalizationOptions.Currency:
+                        {
+                            symbol = formatInfo.CurrencySymbol;
+                            pattern = new NumberPattern("", symbol, 0, formatInfo.CurrencyPositivePattern.ToString(), formatInfo.CurrencyNegativePattern.ToString(), 0, formatInfo.CurrencyDecimalSeparator, formatInfo.CurrencyGroupSeparator);
+                            break;
+                        }
+                    default:
+                        {
+                            symbol = formatInfo.NumberDecimalSeparator;
+                            pattern = new NumberPattern("", symbol, 0, "", formatInfo.NumberNegativePattern.ToString(), 0, formatInfo.NumberDecimalSeparator, formatInfo.NumberGroupSeparator);
+                            break;
+                        }
+                }
+
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, pattern));
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.PatternError)));
+            }
+        }
+
+        /// <summary>
+        /// Gets a pattern string for formatting and parsing currency values according to the client's user preferences and ISO 4217 currency code.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getCurrencyPattern(string options)
+        {
+            GlobalizationOptions globalOptions;
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                if (string.IsNullOrEmpty(globalOptions.CurrencyCode))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                string currencyCode = globalOptions.CurrencyCode;
+
+                // temporary not supported via lack of api required
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.INVALID_ACTION, "Not supported"));
+                return;
+
+                // TODO find the way to get currency info from currency code
+                // http://stackoverflow.com/questions/12373800/3-digit-currency-code-to-currency-symbol
+                // http://stackoverflow.com/questions/6924067/how-to-get-specific-culture-currency-pattern
+                // CultureInfo cultureInfo = new CultureInfo(currencyCode);
+                // NumberFormatInfo numberFormat = cultureInfo.NumberFormat;
+            }
+            catch (Exception e)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.FormattingError)));
+            }
+        }
+
+        #endregion
+
+
+        #region private methods
+
+        /// <summary>
+        /// Wraps data into JSON format
+        /// </summary>
+        /// <param name="data">data</param>
+        /// <returns>data formatted as JSON object</returns>
+        private string WrapIntoJSON<T>(T data, string keyName = "value")
+        {
+            string param = "{0}";
+            string stringifiedData = data.ToString();
+
+            if (data.GetType() == typeof(string))
+            {
+                param = "\"" + param + "\"";
+            }
+
+            if (data.GetType() == typeof(bool))
+            {
+                stringifiedData = stringifiedData.ToLower();
+            }
+
+            if (data.GetType() == typeof(string[]))
+            {
+                stringifiedData = JSON.JsonHelper.Serialize(data);
+            }
+
+            var formattedData = string.Format("\"" + keyName + "\":" + param, stringifiedData);
+            formattedData = "{" + formattedData + "}";
+
+            return formattedData;
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/ImageExifHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/ImageExifHelper.cs b/lib/cordova-wp7/templates/standalone/Plugins/ImageExifHelper.cs
new file mode 100644
index 0000000..62b6462
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/ImageExifHelper.cs
@@ -0,0 +1,209 @@
+/*  
+	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.
+ 
+*/
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Windows.Media.Imaging;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public class ImageExifOrientation
+    {
+        public const int Portrait = 1;
+        public const int PortraitUpsideDown = 3;
+        public const int LandscapeLeft = 6;
+        public const int LandscapeRight = 8;
+    }
+
+    public class ImageExifHelper
+    {
+
+        public static Stream RotateStream(Stream stream, int angle)
+        {
+            stream.Position = 0;
+            if (angle % 90 != 0 || angle < 0)
+            {
+                throw new ArgumentException();
+            }
+            if (angle % 360 == 0)
+            {
+                return stream;
+            }
+
+            angle = angle % 360;
+
+            BitmapImage bitmap = new BitmapImage();
+            bitmap.SetSource(stream);
+            WriteableBitmap wbSource = new WriteableBitmap(bitmap);
+
+            WriteableBitmap wbTarget = null;
+
+            int srcPixelWidth = wbSource.PixelWidth;
+            int srcPixelHeight = wbSource.PixelHeight;
+
+            if (angle % 180 == 0)
+            {
+                wbTarget = new WriteableBitmap(srcPixelWidth, srcPixelHeight);
+            }
+            else
+            {
+                wbTarget = new WriteableBitmap(srcPixelHeight, srcPixelWidth);
+            }
+
+            int destPixelWidth = wbTarget.PixelWidth;
+            int[] srcPxls = wbSource.Pixels;
+            int[] destPxls = wbTarget.Pixels;
+
+            // this ugly if/else is to avoid a conditional check for every pixel
+            if (angle == 90)
+            {
+                for (int x = 0; x < srcPixelWidth; x++)
+                {
+                    for (int y = 0; y < srcPixelHeight; y++)
+                    {
+                        destPxls[(srcPixelHeight - y - 1) + (x * destPixelWidth)] = srcPxls[x + y * srcPixelWidth];
+                    }
+                }
+            }
+            else if (angle == 180)
+            {
+                for (int x = 0; x < srcPixelWidth; x++)
+                {
+                    for (int y = 0; y < srcPixelHeight; y++)
+                    {
+                        destPxls[(srcPixelWidth - x - 1) + (srcPixelHeight - y - 1) * srcPixelWidth] = srcPxls[x + y * srcPixelWidth];
+                    }
+                }
+            }
+            else if (angle == 270)
+            {
+                for (int x = 0; x < srcPixelWidth; x++)
+                {
+                    for (int y = 0; y < srcPixelHeight; y++)
+                    {
+                        destPxls[y + (srcPixelWidth - x - 1) * destPixelWidth] = srcPxls[x + y * srcPixelWidth];
+                    }
+                }
+            }
+
+            MemoryStream targetStream = new MemoryStream();
+            wbTarget.SaveJpeg(targetStream, destPixelWidth, wbTarget.PixelHeight, 0, 100);
+            return targetStream;
+        }
+
+        public static int getImageOrientationFromStream(Stream imgStream)
+        {
+
+            // 0xFFD8 : jpgHeader
+            // 0xFFE1 :
+            // 0x???? : length of exif data
+            // 0x????, 0x???? : Chars 'E','x','i','f'
+            // 0x0000 : 2 empty bytes
+            // <== mark beginning of tags SIZE:ID:VALUE
+            // 0x???? : 'II' or 'MM' for Intel or Motorola ( always getting II on my WP7 devices ), determines littleEndian-ness
+            // 0x002A : marker value
+            // 0x???? : offset to the Image File Data
+
+            // XXXX possible space before actual tag data ... we skip to mark + offset
+
+            // 0x???? number of exif tags present
+
+            // make sure we are at the beginning
+            imgStream.Seek(0, SeekOrigin.Begin);
+            BinaryReader reader = new BinaryReader(imgStream);
+
+            byte[] jpgHdr = reader.ReadBytes(2); // always (0xFFD8)
+
+            byte start = reader.ReadByte(); // 0xFF
+            byte index = reader.ReadByte(); // 0xE1
+
+            while (start == 0xFF && index != 0xE1) // This never seems to happen, todo: optimize
+            {
+                // Get the data length
+                ushort dLen = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+                // skip along
+                reader.ReadBytes(dLen - 2);
+                start = reader.ReadByte();
+                index = reader.ReadByte();
+            }
+
+            // It's only success if we found the 0xFFE1 marker
+            if (start != 0xFF || index != 0xE1)
+            {
+                //   throw new Exception("Could not find Exif data block");
+                Debug.WriteLine("Did not find EXIF data");
+                return 0;
+            }
+
+            // read 2 byte length of EXIF data
+            ushort exifLen = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+            String exif = ""; // build the string
+            for (var n = 0; n < 4; n++)
+            {
+                exif += reader.ReadChar();
+            }
+            if (exif != "Exif")
+            {
+                // did not find exif data ...
+                Debug.WriteLine("Did not find EXIF data");
+                return 0;
+            }
+
+            // read 2 empty bytes
+            //ushort emptyBytes = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+            reader.ReadBytes(2);
+
+            long headerMark = reader.BaseStream.Position; // where are we now <==
+
+            //bool isLEndian = (reader.ReadChar() + "" + reader.ReadChar()) == "II";
+            reader.ReadBytes(2); // 'II' or 'MM', but we don't care
+
+            if (0x002A != BitConverter.ToUInt16(reader.ReadBytes(2), 0))
+            {
+                Debug.WriteLine("Error in data != 0x002A");
+                return 0;
+            }
+
+            // Get the offset to the IFD (image file directory)
+            ushort imgOffset = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+
+            imgStream.Position = headerMark + imgOffset;
+            ushort tagCount = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+            for (ushort x = 0; x < tagCount; x++)
+            {
+                // Orientation = 0x112, aka 274
+                if (0x112 == BitConverter.ToUInt16(reader.ReadBytes(2), 0))
+                {
+                    ushort dType = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+                    // don't care ..
+                    uint comps = reader.ReadUInt32();
+                    byte[] tagData = reader.ReadBytes(4);
+                    int orientation = (int)tagData[0];
+                    Debug.WriteLine("orientation = " + orientation.ToString());
+                    return orientation;
+                    // 6 means rotate clockwise 90 deg
+                    // 8 means rotate counter-clockwise 90 deg
+                    // 1 means all is good
+                    // 3 means flip vertical
+                }
+                // skip to the next item, 12 bytes each
+                reader.BaseStream.Seek(10, SeekOrigin.Current);
+            }
+            return 0;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/InAppBrowser.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/InAppBrowser.cs b/lib/cordova-wp7/templates/standalone/Plugins/InAppBrowser.cs
new file mode 100644
index 0000000..425f5ae
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/InAppBrowser.cs
@@ -0,0 +1,268 @@
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+using System.Diagnostics;
+using System.Runtime.Serialization;
+using WPCordovaClassLib.Cordova;
+using WPCordovaClassLib.Cordova.Commands;
+using WPCordovaClassLib.Cordova.JSON;
+using Microsoft.Phone.Shell;
+using Microsoft.Phone.Tasks;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    [DataContract]
+    public class BrowserOptions
+    {
+        [DataMember]
+        public string url;
+
+        [DataMember]
+        public bool isGeolocationEnabled;
+    }
+
+    public class InAppBrowser : BaseCommand
+    {
+
+        private static WebBrowser browser;
+        private static ApplicationBarIconButton backButton;
+        private static ApplicationBarIconButton fwdButton;
+
+        public void open(string options)
+        {
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+            //BrowserOptions opts = JSON.JsonHelper.Deserialize<BrowserOptions>(options);
+            string urlLoc = args[0];
+            string target = args[1];
+            /*
+                _self - opens in the Cordova WebView if url is in the white-list, else it opens in the InAppBrowser 
+                _blank - always open in the InAppBrowser 
+                _system - always open in the system web browser 
+            */
+            switch (target)
+            {
+                case "_blank":
+                    ShowInAppBrowser(urlLoc);
+                    break;
+                case "_self":
+                    ShowCordovaBrowser(urlLoc);
+                    break;
+                case "_system":
+                    ShowSystemBrowser(urlLoc);
+                    break;
+            }
+
+
+        }
+
+        private void ShowCordovaBrowser(string url)
+        {
+            Uri loc = new Uri(url, UriKind.RelativeOrAbsolute);
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                if (frame != null)
+                {
+                    PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+                    if (page != null)
+                    {
+                        CordovaView cView = page.FindName("CordovaView") as CordovaView;
+                        if (cView != null)
+                        {
+                            WebBrowser br = cView.Browser;
+                            br.Navigate(loc);
+                        }
+                    }
+
+                }
+            });
+        }
+
+        private void ShowSystemBrowser(string url)
+        {
+            WebBrowserTask webBrowserTask = new WebBrowserTask();
+            webBrowserTask.Uri = new Uri(url, UriKind.Absolute);
+            webBrowserTask.Show();
+        }
+
+
+        // Display an inderminate progress indicator
+        private void ShowInAppBrowser(string url)
+        {
+            Uri loc = new Uri(url);
+
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                if (browser != null)
+                {
+                    //browser.IsGeolocationEnabled = opts.isGeolocationEnabled;
+                    browser.Navigate(loc);
+                }
+                else
+                {
+                    PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                    if (frame != null)
+                    {
+                        PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+
+                        if (page != null)
+                        {
+                            Grid grid = page.FindName("LayoutRoot") as Grid;
+                            if (grid != null)
+                            {
+                                browser = new WebBrowser();
+                                browser.IsScriptEnabled = true;
+                                browser.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(browser_LoadCompleted);
+                                browser.Navigating += new EventHandler<NavigatingEventArgs>(browser_Navigating);
+                                browser.NavigationFailed += new System.Windows.Navigation.NavigationFailedEventHandler(browser_NavigationFailed);
+                                browser.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(browser_Navigated);
+                                
+                                browser.Navigate(loc);
+                                //browser.IsGeolocationEnabled = opts.isGeolocationEnabled;
+                                grid.Children.Add(browser);
+                            }
+
+                            ApplicationBar bar = new ApplicationBar();
+                            bar.BackgroundColor = Colors.Gray;
+                            bar.IsMenuEnabled = false;
+
+                            backButton = new ApplicationBarIconButton();
+                            backButton.Text = "Back";
+                            backButton.IconUri = new Uri("/Images/appbar.back.rest.png", UriKind.Relative);
+                            backButton.Click += new EventHandler(backButton_Click);
+                            //backButton.IsEnabled = false;
+                            bar.Buttons.Add(backButton);
+
+
+                            fwdButton = new ApplicationBarIconButton();
+                            fwdButton.Text = "Forward";
+                            fwdButton.IconUri = new Uri("/Images/appbar.next.rest.png", UriKind.Relative);
+                            fwdButton.Click += new EventHandler(fwdButton_Click);
+                            //fwdButton.IsEnabled = false;
+                            bar.Buttons.Add(fwdButton);
+
+                            ApplicationBarIconButton closeBtn = new ApplicationBarIconButton();
+                            closeBtn.Text = "Close";
+                            closeBtn.IconUri = new Uri("/Images/appbar.close.rest.png", UriKind.Relative);
+                            closeBtn.Click += new EventHandler(closeBtn_Click);
+                            bar.Buttons.Add(closeBtn);
+
+                            page.ApplicationBar = bar;
+                        }
+
+                    }
+                }
+            });
+        }
+
+        void browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+
+        }
+
+        void fwdButton_Click(object sender, EventArgs e)
+        {
+            if (browser != null)
+            {
+                try
+                {
+                    //browser.GoForward();
+                    browser.InvokeScript("execScript", "history.forward();");
+                }
+                catch (Exception)
+                {
+
+                }
+            }
+        }
+
+        void backButton_Click(object sender, EventArgs e)
+        {
+            if (browser != null)
+            {
+                try
+                {
+                    //browser.GoBack();
+                    browser.InvokeScript("execScript", "history.back();");
+                }
+                catch (Exception)
+                {
+
+                }
+            }
+        }
+
+        void closeBtn_Click(object sender, EventArgs e)
+        {
+            this.close();
+        }
+
+
+        public void close(string options = "")
+        {
+            if (browser != null)
+            {
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                    if (frame != null)
+                    {
+                        PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+                        if (page != null)
+                        {
+                            Grid grid = page.FindName("LayoutRoot") as Grid;
+                            if (grid != null)
+                            {
+                                grid.Children.Remove(browser);
+                            }
+                            page.ApplicationBar = null;
+                        }
+                    }
+                    browser = null;
+                    string message = "{\"type\":\"exit\"}";
+                    PluginResult result = new PluginResult(PluginResult.Status.OK, message);
+                    result.KeepCallback = false;
+                    this.DispatchCommandResult(result);
+                });
+            }
+        }
+
+        void browser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            //if (browser != null)
+            //{
+            //    backButton.IsEnabled = browser.CanGoBack;
+            //    fwdButton.IsEnabled = browser.CanGoForward;
+            //}
+            string message = "{\"type\":\"loadstop\", \"url\":\"" + e.Uri.AbsoluteUri + "\"}";
+            PluginResult result = new PluginResult(PluginResult.Status.OK, message);
+            result.KeepCallback = true;
+            this.DispatchCommandResult(result);
+        }
+
+        void browser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
+        {
+            string message = "{\"type\":\"error\",\"url\":\"" + e.Uri.AbsoluteUri + "\"}";
+            PluginResult result = new PluginResult(PluginResult.Status.ERROR, message);
+            result.KeepCallback = true;
+            this.DispatchCommandResult(result);
+        }
+
+        void browser_Navigating(object sender, NavigatingEventArgs e)
+        {
+            string message = "{\"type\":\"loadstart\",\"url\":\"" + e.Uri.AbsoluteUri + "\"}";
+            PluginResult result = new PluginResult(PluginResult.Status.OK, message);
+            result.KeepCallback = true;
+            this.DispatchCommandResult(result);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/Media.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/Media.cs b/lib/cordova-wp7/templates/standalone/Plugins/Media.cs
new file mode 100644
index 0000000..90c54f1
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/Media.cs
@@ -0,0 +1,532 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Windows;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Provides the ability to record and play back audio files on a device. 
+    /// </summary>
+    public class Media : BaseCommand
+    {
+        /// <summary>
+        /// Audio player objects
+        /// </summary>
+        private static Dictionary<string, AudioPlayer> players = new Dictionary<string, AudioPlayer>();
+
+        /// <summary>
+        /// Represents Media action options.
+        /// </summary>
+        [DataContract]
+        public class MediaOptions
+        {
+            /// <summary>
+            /// Audio id
+            /// </summary>
+            [DataMember(Name = "id", IsRequired = true)]
+            public string Id { get; set; }
+
+            /// <summary>
+            /// Path to audio file
+            /// </summary>
+            [DataMember(Name = "src")]
+            public string Src { get; set; }
+
+            /// <summary>
+            /// New track position
+            /// </summary>
+            [DataMember(Name = "milliseconds")]
+            public int Milliseconds { get; set; }
+        }
+
+        /// <summary>
+        /// Releases the audio player instance to save memory.
+        /// </summary>  
+        public void release(string options)
+        {
+            try
+            {
+                MediaOptions mediaOptions;
+
+                try
+                {
+                    string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaOptions = new MediaOptions();
+                    mediaOptions.Id = optionsString[0];
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                if (!Media.players.ContainsKey(mediaOptions.Id))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, false));
+                    return;
+                }
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        AudioPlayer audio = Media.players[mediaOptions.Id];
+                        Media.players.Remove(mediaOptions.Id);
+                        audio.Dispose();
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, true));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Starts recording and save the specified file 
+        /// </summary>
+        public void startRecordingAudio(string options)
+        {
+            try
+            {
+                MediaOptions mediaOptions;
+
+                try
+                {
+                    string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaOptions = new MediaOptions();
+                    mediaOptions.Id = optionsString[0];
+                    mediaOptions.Src = optionsString[1];
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                if (mediaOptions != null)
+                {
+
+                    Deployment.Current.Dispatcher.BeginInvoke(() =>
+                    {
+                        try
+                        {
+                            if (!Media.players.ContainsKey(mediaOptions.Id))
+                            {
+                                AudioPlayer audio = new AudioPlayer(this, mediaOptions.Id);
+                                Media.players.Add(mediaOptions.Id, audio);
+                                audio.startRecording(mediaOptions.Src);
+                            }
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                        }
+                        catch (Exception e)
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                        }
+
+                    });
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                }
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Stops recording and save to the file specified when recording started 
+        /// </summary>
+        public void stopRecordingAudio(string options)
+        {
+            try
+            {
+                string mediaId = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        if (Media.players.ContainsKey(mediaId))
+                        {
+                            AudioPlayer audio = Media.players[mediaId];
+                            audio.stopRecording();
+                            Media.players.Remove(mediaId);
+                        }
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+            }
+        }
+
+        public void setVolume(string options) // id,volume
+        {
+            try
+            {
+                string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                string id = optionsString[0];
+                double volume = double.Parse(optionsString[1]);
+
+                if (Media.players.ContainsKey(id))
+                {
+                    Deployment.Current.Dispatcher.BeginInvoke(() =>
+                    {
+                        try
+                        {
+                            AudioPlayer player = Media.players[id];
+                            player.setVolume(volume);
+                        }
+                        catch (Exception e)
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                        }
+                    });
+                }
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Error parsing options into setVolume method"));
+                return;
+            }
+        }
+
+        // Some Audio Notes:
+        // In the Windows Phone Emulator, playback of video or audio content using the MediaElement control is not supported.
+        // While playing, a MediaElement stops all other media playback on the phone.
+        // Multiple MediaElement controls are NOT supported
+
+        // Called when you create a new Media('blah') object in JS.
+        public void create(string options)
+        {
+            // Debug.WriteLine("Creating Audio :: " + options);
+            try
+            {
+                MediaOptions mediaOptions;
+                try
+                {
+                    string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaOptions = new MediaOptions();
+                    mediaOptions.Id = optionsString[0];
+                    mediaOptions.Src = optionsString[1];
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Error parsing options into create method"));
+                    return;
+                }
+
+                AudioPlayer audio = new AudioPlayer(this, mediaOptions.Id);
+                Media.players.Add(mediaOptions.Id, audio);
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Starts or resume playing audio file 
+        /// </summary>
+        public void startPlayingAudio(string options)
+        {
+            try
+            {
+                MediaOptions mediaOptions;
+                try
+                {
+                    string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaOptions = new MediaOptions();
+                    mediaOptions.Id = optionsString[0];
+                    mediaOptions.Src = optionsString[1];
+                    if (optionsString.Length > 2 && optionsString[2] != null)
+                    {
+                        mediaOptions.Milliseconds = int.Parse(optionsString[2]);
+                    }
+
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                AudioPlayer audio;
+
+                if (!Media.players.ContainsKey(mediaOptions.Id))
+                {
+                    audio = new AudioPlayer(this, mediaOptions.Id);
+                    Media.players[mediaOptions.Id] = audio;
+                }
+                else
+                {
+                    Debug.WriteLine("INFO: startPlayingAudio FOUND mediaPlayer for " + mediaOptions.Id);
+                    audio = Media.players[mediaOptions.Id];
+                }
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        audio.startPlaying(mediaOptions.Src);
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+
+        /// <summary>
+        /// Seeks to a location
+        /// </summary>
+        public void seekToAudio(string options)
+        {
+            try
+            {
+                MediaOptions mediaOptions;
+
+                try
+                {
+                    string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaOptions = new MediaOptions();
+                    mediaOptions.Id = optionsString[0];
+                    if (optionsString.Length > 1 && optionsString[1] != null)
+                    {
+                        mediaOptions.Milliseconds = int.Parse(optionsString[1]);
+                    }
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        if (Media.players.ContainsKey(mediaOptions.Id))
+                        {
+                            AudioPlayer audio = Media.players[mediaOptions.Id];
+                            audio.seekToPlaying(mediaOptions.Milliseconds);
+                        }
+                        else
+                        {
+                            Debug.WriteLine("ERROR: seekToAudio could not find mediaPlayer for " + mediaOptions.Id);
+                        }
+
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Pauses playing 
+        /// </summary>
+        public void pausePlayingAudio(string options)
+        {
+
+            try
+            {
+                string mediaId = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        if (Media.players.ContainsKey(mediaId))
+                        {
+                            AudioPlayer audio = Media.players[mediaId];
+                            audio.pausePlaying();
+                        }
+                        else
+                        {
+                            Debug.WriteLine("ERROR: pausePlayingAudio could not find mediaPlayer for " + mediaId);
+                        }
+
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+
+
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+            }
+
+
+        }
+
+
+        /// <summary>
+        /// Stops playing the audio file
+        /// </summary>
+        public void stopPlayingAudio(String options)
+        {
+            try
+            {
+                string mediaId = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        if (Media.players.ContainsKey(mediaId))
+                        {
+                            AudioPlayer audio = Media.players[mediaId];
+                            audio.stopPlaying();
+                        }
+                        else
+                        {
+                            Debug.WriteLine("stopPlaying could not find mediaPlayer for " + mediaId);
+                        }
+
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+            }
+        }
+
+        /// <summary>
+        /// Gets current position of playback
+        /// </summary>
+        public void getCurrentPositionAudio(string options)
+        {
+            try
+            {
+                string mediaId = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        if (Media.players.ContainsKey(mediaId))
+                        {
+                            AudioPlayer audio = Media.players[mediaId];
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, audio.getCurrentPosition()));
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, -1));
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+        }
+
+
+        /// <summary>
+        /// Gets the duration of the audio file
+        /// </summary>
+        
+        [Obsolete("This method will be removed shortly")]
+        public void getDurationAudio(string options)
+        {
+            try
+            {
+                MediaOptions mediaOptions;
+
+                try
+                {
+                    mediaOptions = JSON.JsonHelper.Deserialize<MediaOptions>(options);
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                AudioPlayer audio;
+                if (Media.players.ContainsKey(mediaOptions.Id))
+                {
+                    audio = Media.players[mediaOptions.Id];
+                }
+                else
+                {
+                    Debug.WriteLine("ERROR: getDurationAudio could not find mediaPlayer for " + mediaOptions.Id);
+                    audio = new AudioPlayer(this, mediaOptions.Id);
+                    Media.players.Add(mediaOptions.Id, audio);
+                }
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, audio.getDuration(mediaOptions.Src)));
+                });
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+    }
+}


[05/50] git commit: Version 2.7.4

Posted by br...@apache.org.
Version 2.7.4


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

Branch: refs/heads/master2
Commit: c7253d6fc1a04109c3aad1e5fba1493a3ccc09dc
Parents: 2597ee2
Author: Michael Brooks <mi...@michaelbrooks.ca>
Authored: Wed May 22 15:38:44 2013 -0700
Committer: Ian Clelland <ic...@chromium.org>
Committed: Thu May 23 16:04:02 2013 -0400

----------------------------------------------------------------------
 package.json |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/c7253d6f/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index e62415e..dfffcbf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "cordova",
-  "version": "2.7.3",
+  "version": "2.7.4",
   "preferGlobal": "true",
   "description": "Cordova command line interface tool",
   "main": "cordova",


[49/50] git commit: Fix bootstrap, app/ dir is fully reverted now.

Posted by br...@apache.org.
Fix bootstrap, app/ dir is fully reverted now.


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

Branch: refs/heads/master2
Commit: 8dd2c2e3615127c9dc9503b8c1a8e14028ef35c9
Parents: 01811ca
Author: Braden Shepherdson <br...@gmail.com>
Authored: Thu May 23 17:07:42 2013 -0400
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Thu May 23 17:07:42 2013 -0400

----------------------------------------------------------------------
 bootstrap.js                                       |    2 +-
 src/create.js                                      |    5 +-
 templates/app/config.xml                           |   49 -
 templates/app/www/css/index.css                    |  100 -
 templates/app/www/img/cordova.png                  |  Bin 19932 -> 0 bytes
 templates/app/www/index.html                       |   24 -
 templates/app/www/js/index.js                      |   20 -
 templates/app/www/res/icon/cordova_128.png         |  Bin 11401 -> 0 bytes
 templates/app/www/res/icon/cordova_16.png          |  Bin 1699 -> 0 bytes
 templates/app/www/res/icon/cordova_24.png          |  Bin 2215 -> 0 bytes
 templates/app/www/res/icon/cordova_256.png         |  Bin 27408 -> 0 bytes
 templates/app/www/res/icon/cordova_32.png          |  Bin 2843 -> 0 bytes
 templates/app/www/res/icon/cordova_48.png          |  Bin 4111 -> 0 bytes
 templates/app/www/res/icon/cordova_512.png         |  Bin 39830 -> 0 bytes
 templates/app/www/res/icon/cordova_64.png          |  Bin 5463 -> 0 bytes
 templates/app/www/res/icon/cordova_android_36.png  |  Bin 3096 -> 0 bytes
 templates/app/www/res/icon/cordova_android_48.png  |  Bin 4090 -> 0 bytes
 templates/app/www/res/icon/cordova_android_72.png  |  Bin 6080 -> 0 bytes
 templates/app/www/res/icon/cordova_android_96.png  |  Bin 7685 -> 0 bytes
 templates/app/www/res/icon/cordova_bb_80.png       |  Bin 7287 -> 0 bytes
 templates/app/www/res/icon/cordova_ios_114.png     |  Bin 7869 -> 0 bytes
 templates/app/www/res/icon/cordova_ios_144.png     |  Bin 11706 -> 0 bytes
 templates/app/www/res/icon/cordova_ios_57.png      |  Bin 3908 -> 0 bytes
 templates/app/www/res/icon/cordova_ios_72.png      |  Bin 4944 -> 0 bytes
 .../app/www/res/screen/android_hdpi_landscape.png  |  Bin 218302 -> 0 bytes
 .../app/www/res/screen/android_hdpi_portrait.png   |  Bin 222148 -> 0 bytes
 .../app/www/res/screen/android_ldpi_landscape.png  |  Bin 42616 -> 0 bytes
 .../app/www/res/screen/android_ldpi_portrait.png   |  Bin 42034 -> 0 bytes
 .../app/www/res/screen/android_mdpi_landscape.png  |  Bin 92347 -> 0 bytes
 .../app/www/res/screen/android_mdpi_portrait.png   |  Bin 90555 -> 0 bytes
 .../app/www/res/screen/android_xhdpi_landscape.png |  Bin 489604 -> 0 bytes
 .../app/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/app/www/res/screen/ipad_landscape.png    |  Bin 407370 -> 0 bytes
 templates/app/www/res/screen/ipad_portrait.png     |  Bin 422441 -> 0 bytes
 .../app/www/res/screen/ipad_retina_landscape.png   |  Bin 1534088 -> 0 bytes
 .../app/www/res/screen/ipad_retina_portrait.png    |  Bin 1610434 -> 0 bytes
 templates/app/www/res/screen/iphone_landscape.png  |  Bin 92301 -> 0 bytes
 templates/app/www/res/screen/iphone_portrait.png   |  Bin 93897 -> 0 bytes
 .../app/www/res/screen/iphone_retina_landscape.png |  Bin 339639 -> 0 bytes
 .../app/www/res/screen/iphone_retina_portrait.png  |  Bin 350593 -> 0 bytes
 .../app/www/res/screen/windows_phone_portrait.jpg  |  Bin 11483 -> 0 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 0 -> 19932 bytes
 templates/www/index.html                           |   24 +
 templates/www/js/index.js                          |   20 +
 templates/www/res/icon/cordova_128.png             |  Bin 0 -> 11401 bytes
 templates/www/res/icon/cordova_16.png              |  Bin 0 -> 1699 bytes
 templates/www/res/icon/cordova_24.png              |  Bin 0 -> 2215 bytes
 templates/www/res/icon/cordova_256.png             |  Bin 0 -> 27408 bytes
 templates/www/res/icon/cordova_32.png              |  Bin 0 -> 2843 bytes
 templates/www/res/icon/cordova_48.png              |  Bin 0 -> 4111 bytes
 templates/www/res/icon/cordova_512.png             |  Bin 0 -> 39830 bytes
 templates/www/res/icon/cordova_64.png              |  Bin 0 -> 5463 bytes
 templates/www/res/icon/cordova_android_36.png      |  Bin 0 -> 3096 bytes
 templates/www/res/icon/cordova_android_48.png      |  Bin 0 -> 4090 bytes
 templates/www/res/icon/cordova_android_72.png      |  Bin 0 -> 6080 bytes
 templates/www/res/icon/cordova_android_96.png      |  Bin 0 -> 7685 bytes
 templates/www/res/icon/cordova_bb_80.png           |  Bin 0 -> 7287 bytes
 templates/www/res/icon/cordova_ios_114.png         |  Bin 0 -> 7869 bytes
 templates/www/res/icon/cordova_ios_144.png         |  Bin 0 -> 11706 bytes
 templates/www/res/icon/cordova_ios_57.png          |  Bin 0 -> 3908 bytes
 templates/www/res/icon/cordova_ios_72.png          |  Bin 0 -> 4944 bytes
 .../www/res/screen/android_hdpi_landscape.png      |  Bin 0 -> 218302 bytes
 templates/www/res/screen/android_hdpi_portrait.png |  Bin 0 -> 222148 bytes
 .../www/res/screen/android_ldpi_landscape.png      |  Bin 0 -> 42616 bytes
 templates/www/res/screen/android_ldpi_portrait.png |  Bin 0 -> 42034 bytes
 .../www/res/screen/android_mdpi_landscape.png      |  Bin 0 -> 92347 bytes
 templates/www/res/screen/android_mdpi_portrait.png |  Bin 0 -> 90555 bytes
 .../www/res/screen/android_xhdpi_landscape.png     |  Bin 0 -> 489604 bytes
 .../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/www/res/screen/ipad_landscape.png        |  Bin 0 -> 407370 bytes
 templates/www/res/screen/ipad_portrait.png         |  Bin 0 -> 422441 bytes
 templates/www/res/screen/ipad_retina_landscape.png |  Bin 0 -> 1534088 bytes
 templates/www/res/screen/ipad_retina_portrait.png  |  Bin 0 -> 1610434 bytes
 templates/www/res/screen/iphone_landscape.png      |  Bin 0 -> 92301 bytes
 templates/www/res/screen/iphone_portrait.png       |  Bin 0 -> 93897 bytes
 .../www/res/screen/iphone_retina_landscape.png     |  Bin 0 -> 339639 bytes
 .../www/res/screen/iphone_retina_portrait.png      |  Bin 0 -> 350593 bytes
 .../www/res/screen/windows_phone_portrait.jpg      |  Bin 0 -> 11483 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 +++++++++++++++
 98 files changed, 3552 insertions(+), 3553 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/bootstrap.js
----------------------------------------------------------------------
diff --git a/bootstrap.js b/bootstrap.js
index e8055f5..4750571 100644
--- a/bootstrap.js
+++ b/bootstrap.js
@@ -54,7 +54,7 @@ shell.rm('-rf', cordovaDir);
 create(cordovaDir);
 var platformsDir = path.join(cordovaDir, 'platforms');
 // kill the stupid spec shit!
-shell.rm('-rf', path.join(cordovaDir, 'app', 'www', 'spec'));
+shell.rm('-rf', path.join(cordovaDir, 'www', 'spec'));
 
 var end = n(platforms.length, function() {
     // Check that we are installing globally into a root-only directory.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/src/create.js
----------------------------------------------------------------------
diff --git a/src/create.js b/src/create.js
index 7ba2c50..84e8b13 100644
--- a/src/create.js
+++ b/src/create.js
@@ -59,9 +59,8 @@ module.exports = function create (dir, id, name) {
     // Create basic project structure.
     shell.mkdir('-p', dotCordova);
     shell.mkdir('-p', path.join(dir, 'platforms'));
+    shell.mkdir('-p', path.join(dir, 'merges'));
     shell.mkdir('-p', path.join(dir, 'plugins'));
-    shell.mkdir('-p', path.join(dir, 'app'));
-    shell.mkdir('-p', path.join(dir, 'app', 'merges'));
     var hooks = path.join(dotCordova, 'hooks');
     shell.mkdir('-p', hooks);
 
@@ -96,7 +95,7 @@ module.exports = function create (dir, id, name) {
     }));
 
     // Copy in base template
-    shell.cp('-r', path.join(__dirname, '..', 'templates', 'app'), dir);
+    shell.cp('-r', path.join(__dirname, '..', 'templates', 'www'), dir);
 
     // Write out id and name to config.xml
     var configPath = util.projectConfig(dir);

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/config.xml
----------------------------------------------------------------------
diff --git a/templates/app/config.xml b/templates/app/config.xml
deleted file mode 100644
index aca4d5e..0000000
--- a/templates/app/config.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<widget xmlns     = "http://www.w3.org/ns/widgets"
-        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"  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" />
-
-    <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>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/css/index.css
----------------------------------------------------------------------
diff --git a/templates/app/www/css/index.css b/templates/app/www/css/index.css
deleted file mode 100644
index c869f87..0000000
--- a/templates/app/www/css/index.css
+++ /dev/null
@@ -1,100 +0,0 @@
-html,
-body {
-    height:100%;
-    font-size:12px;
-    width:100%;
-}
-
-html {
-    display:table;
-}
-
-body {
-    background-color:#A7A7A7;
-    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
-    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
-    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
-    background-image:-webkit-gradient(
-        linear,
-        left top,
-        left bottom,
-        color-stop(0, #A7A7A7),
-        color-stop(0.51, #E4E4E4)
-    );
-    display:table-cell;
-    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
-    text-transform:uppercase;
-    vertical-align:middle;
-}
-
-.app {
-    background-image:url(../img/cordova.png);
-    background-repeat:no-repeat;
-    margin:0px auto;
-    width:275px;
-}
-
-h1 {
-    font-size:2em;
-    font-weight:300;
-    margin:0px;
-    overflow:visible;
-    padding:0px;
-    text-align:center;
-}
-
-.status {
-    background-color:#333333;
-    border-radius:4px;
-    -webkit-border-radius:4px;
-    color:#FFFFFF;
-    font-size:1em;
-    margin:0px auto;
-    padding:2px 10px;
-    text-align:center;
-    width:100%;
-    max-width:175px;
-}
-
-.status.complete {
-    background-color:#4B946A;
-}
-
-.hide {
-    display:none;
-}
-
-@keyframes fade {
-    from { opacity: 1.0; }
-    50% { opacity: 0.4; }
-    to { opacity: 1.0; }
-}
- 
-@-webkit-keyframes fade {
-    from { opacity: 1.0; }
-    50% { opacity: 0.4; }
-    to { opacity: 1.0; }
-}
- 
-.blink {
-    animation:fade 3000ms infinite;
-    -webkit-animation:fade 3000ms infinite;
-}
-
-/* portrait */
-/* @media screen and (max-aspect-ratio: 1/1) */
-.app {
-    background-position:center top;
-    height:100px;              /* adds enough room for text */
-    padding:180px 0px 0px 0px; /* background height - shadow offset */
-}
-
-/* lanscape (when wide enough) */
-@media screen and (min-aspect-ratio: 1/1) and (min-width:445px) {
-    .app {
-        background-position:left center;
-        height:140px;       /* height + padding = background image size */
-        padding-left:170px; /* background width */
-        padding-top:60px;   /* center the text */
-    }
-}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/img/cordova.png
----------------------------------------------------------------------
diff --git a/templates/app/www/img/cordova.png b/templates/app/www/img/cordova.png
deleted file mode 100644
index e8169cf..0000000
Binary files a/templates/app/www/img/cordova.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/index.html
----------------------------------------------------------------------
diff --git a/templates/app/www/index.html b/templates/app/www/index.html
deleted file mode 100644
index 202af51..0000000
--- a/templates/app/www/index.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<html>
-    <head>
-        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-        <meta name = "format-detection" content = "telephone=no"/>
-        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width;" />
-        <link rel="stylesheet" type="text/css" href="css/index.css" />
-        <title>Hello Cordova</title>
-    </head>
-    <body>
-        <div class="app">
-            <h1>Apache Cordova</h1>
-            <div id="deviceready">
-                <p class="status pending blink">Connecting to Device</p>
-                <p class="status complete blink hide">Device is Ready</p>
-            </div>
-        </div>
-        <script type="text/javascript" src="cordova.js"></script>
-        <script type="text/javascript" src="js/index.js"></script>
-        <script type="text/javascript">
-            app.initialize();
-        </script>
-    </body>
-</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/js/index.js
----------------------------------------------------------------------
diff --git a/templates/app/www/js/index.js b/templates/app/www/js/index.js
deleted file mode 100644
index 6140331..0000000
--- a/templates/app/www/js/index.js
+++ /dev/null
@@ -1,20 +0,0 @@
-var app = {
-    initialize: function() {
-        this.bind();
-    },
-    bind: function() {
-        document.addEventListener('deviceready', this.deviceready, false);
-    },
-    deviceready: function() {
-        // note that this is an event handler so the scope is that of the event
-        // so we need to call app.report(), and not this.report()
-        app.report('deviceready');
-    },
-    report: function(id) { 
-        console.log("report:" + id);
-        // hide the .pending <p> and show the .complete <p>
-        document.querySelector('#' + id + ' .pending').className += ' hide';
-        var completeElem = document.querySelector('#' + id + ' .complete');
-        completeElem.className = completeElem.className.split('hide').join('');
-    }
-};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_128.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_128.png b/templates/app/www/res/icon/cordova_128.png
deleted file mode 100644
index 3516df3..0000000
Binary files a/templates/app/www/res/icon/cordova_128.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_16.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_16.png b/templates/app/www/res/icon/cordova_16.png
deleted file mode 100644
index 54e19c5..0000000
Binary files a/templates/app/www/res/icon/cordova_16.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_24.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_24.png b/templates/app/www/res/icon/cordova_24.png
deleted file mode 100644
index c7d43ad..0000000
Binary files a/templates/app/www/res/icon/cordova_24.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_256.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_256.png b/templates/app/www/res/icon/cordova_256.png
deleted file mode 100644
index e1cd0e6..0000000
Binary files a/templates/app/www/res/icon/cordova_256.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_32.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_32.png b/templates/app/www/res/icon/cordova_32.png
deleted file mode 100644
index 734fffc..0000000
Binary files a/templates/app/www/res/icon/cordova_32.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_48.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_48.png b/templates/app/www/res/icon/cordova_48.png
deleted file mode 100644
index 8ad8bac..0000000
Binary files a/templates/app/www/res/icon/cordova_48.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_512.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_512.png b/templates/app/www/res/icon/cordova_512.png
deleted file mode 100644
index c9465f3..0000000
Binary files a/templates/app/www/res/icon/cordova_512.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_64.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_64.png b/templates/app/www/res/icon/cordova_64.png
deleted file mode 100644
index 03b3849..0000000
Binary files a/templates/app/www/res/icon/cordova_64.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_android_36.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_android_36.png b/templates/app/www/res/icon/cordova_android_36.png
deleted file mode 100644
index cd5032a..0000000
Binary files a/templates/app/www/res/icon/cordova_android_36.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_android_48.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_android_48.png b/templates/app/www/res/icon/cordova_android_48.png
deleted file mode 100644
index e79c606..0000000
Binary files a/templates/app/www/res/icon/cordova_android_48.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_android_72.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_android_72.png b/templates/app/www/res/icon/cordova_android_72.png
deleted file mode 100644
index 4d27634..0000000
Binary files a/templates/app/www/res/icon/cordova_android_72.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_android_96.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_android_96.png b/templates/app/www/res/icon/cordova_android_96.png
deleted file mode 100644
index ec7ffbf..0000000
Binary files a/templates/app/www/res/icon/cordova_android_96.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_bb_80.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_bb_80.png b/templates/app/www/res/icon/cordova_bb_80.png
deleted file mode 100644
index f86a27a..0000000
Binary files a/templates/app/www/res/icon/cordova_bb_80.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_ios_114.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_ios_114.png b/templates/app/www/res/icon/cordova_ios_114.png
deleted file mode 100644
index efd9c37..0000000
Binary files a/templates/app/www/res/icon/cordova_ios_114.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_ios_144.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_ios_144.png b/templates/app/www/res/icon/cordova_ios_144.png
deleted file mode 100644
index dd819da..0000000
Binary files a/templates/app/www/res/icon/cordova_ios_144.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_ios_57.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_ios_57.png b/templates/app/www/res/icon/cordova_ios_57.png
deleted file mode 100644
index c795fc4..0000000
Binary files a/templates/app/www/res/icon/cordova_ios_57.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/icon/cordova_ios_72.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/icon/cordova_ios_72.png b/templates/app/www/res/icon/cordova_ios_72.png
deleted file mode 100644
index b1cfde7..0000000
Binary files a/templates/app/www/res/icon/cordova_ios_72.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/android_hdpi_landscape.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/android_hdpi_landscape.png b/templates/app/www/res/screen/android_hdpi_landscape.png
deleted file mode 100644
index a61e2b1..0000000
Binary files a/templates/app/www/res/screen/android_hdpi_landscape.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/android_hdpi_portrait.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/android_hdpi_portrait.png b/templates/app/www/res/screen/android_hdpi_portrait.png
deleted file mode 100644
index 5d6a28a..0000000
Binary files a/templates/app/www/res/screen/android_hdpi_portrait.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/android_ldpi_landscape.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/android_ldpi_landscape.png b/templates/app/www/res/screen/android_ldpi_landscape.png
deleted file mode 100644
index f3934cd..0000000
Binary files a/templates/app/www/res/screen/android_ldpi_landscape.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/android_ldpi_portrait.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/android_ldpi_portrait.png b/templates/app/www/res/screen/android_ldpi_portrait.png
deleted file mode 100644
index 65ad163..0000000
Binary files a/templates/app/www/res/screen/android_ldpi_portrait.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/android_mdpi_landscape.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/android_mdpi_landscape.png b/templates/app/www/res/screen/android_mdpi_landscape.png
deleted file mode 100644
index a1b697c..0000000
Binary files a/templates/app/www/res/screen/android_mdpi_landscape.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/android_mdpi_portrait.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/android_mdpi_portrait.png b/templates/app/www/res/screen/android_mdpi_portrait.png
deleted file mode 100644
index ea15693..0000000
Binary files a/templates/app/www/res/screen/android_mdpi_portrait.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/android_xhdpi_landscape.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/android_xhdpi_landscape.png b/templates/app/www/res/screen/android_xhdpi_landscape.png
deleted file mode 100644
index 79f2f09..0000000
Binary files a/templates/app/www/res/screen/android_xhdpi_landscape.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/android_xhdpi_portrait.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/android_xhdpi_portrait.png b/templates/app/www/res/screen/android_xhdpi_portrait.png
deleted file mode 100644
index c2e8042..0000000
Binary files a/templates/app/www/res/screen/android_xhdpi_portrait.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/blackberry_transparent_300.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/blackberry_transparent_300.png b/templates/app/www/res/screen/blackberry_transparent_300.png
deleted file mode 100644
index b548bdc..0000000
Binary files a/templates/app/www/res/screen/blackberry_transparent_300.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/blackberry_transparent_400.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/blackberry_transparent_400.png b/templates/app/www/res/screen/blackberry_transparent_400.png
deleted file mode 100644
index 3facdf9..0000000
Binary files a/templates/app/www/res/screen/blackberry_transparent_400.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/ipad_landscape.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/ipad_landscape.png b/templates/app/www/res/screen/ipad_landscape.png
deleted file mode 100644
index 04be5ac..0000000
Binary files a/templates/app/www/res/screen/ipad_landscape.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/ipad_portrait.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/ipad_portrait.png b/templates/app/www/res/screen/ipad_portrait.png
deleted file mode 100644
index 41e839d..0000000
Binary files a/templates/app/www/res/screen/ipad_portrait.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/ipad_retina_landscape.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/ipad_retina_landscape.png b/templates/app/www/res/screen/ipad_retina_landscape.png
deleted file mode 100644
index 95c542d..0000000
Binary files a/templates/app/www/res/screen/ipad_retina_landscape.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/ipad_retina_portrait.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/ipad_retina_portrait.png b/templates/app/www/res/screen/ipad_retina_portrait.png
deleted file mode 100644
index aae1862..0000000
Binary files a/templates/app/www/res/screen/ipad_retina_portrait.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/iphone_landscape.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/iphone_landscape.png b/templates/app/www/res/screen/iphone_landscape.png
deleted file mode 100644
index d154883..0000000
Binary files a/templates/app/www/res/screen/iphone_landscape.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/iphone_portrait.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/iphone_portrait.png b/templates/app/www/res/screen/iphone_portrait.png
deleted file mode 100644
index 6fcba56..0000000
Binary files a/templates/app/www/res/screen/iphone_portrait.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/iphone_retina_landscape.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/iphone_retina_landscape.png b/templates/app/www/res/screen/iphone_retina_landscape.png
deleted file mode 100644
index 0165669..0000000
Binary files a/templates/app/www/res/screen/iphone_retina_landscape.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/iphone_retina_portrait.png
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/iphone_retina_portrait.png b/templates/app/www/res/screen/iphone_retina_portrait.png
deleted file mode 100644
index bd24886..0000000
Binary files a/templates/app/www/res/screen/iphone_retina_portrait.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/res/screen/windows_phone_portrait.jpg
----------------------------------------------------------------------
diff --git a/templates/app/www/res/screen/windows_phone_portrait.jpg b/templates/app/www/res/screen/windows_phone_portrait.jpg
deleted file mode 100644
index 9f95387..0000000
Binary files a/templates/app/www/res/screen/windows_phone_portrait.jpg and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/spec.html
----------------------------------------------------------------------
diff --git a/templates/app/www/spec.html b/templates/app/www/spec.html
deleted file mode 100644
index 83d7d2e..0000000
--- a/templates/app/www/spec.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<html>
-    <head>
-        <title>Jasmine Spec Runner</title>
-
-        <!-- jasmine source -->
-        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
-        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
-        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
-        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
-
-        <!-- include source files here... -->
-        <script type="text/javascript" src="js/index.js"></script>
-
-        <!-- include spec files here... -->
-        <script type="text/javascript" src="spec/helper.js"></script>
-        <script type="text/javascript" src="spec/index.js"></script>
-
-        <script type="text/javascript">
-            (function() {
-                var jasmineEnv = jasmine.getEnv();
-                jasmineEnv.updateInterval = 1000;
-
-                var htmlReporter = new jasmine.HtmlReporter();
-
-                jasmineEnv.addReporter(htmlReporter);
-
-                jasmineEnv.specFilter = function(spec) {
-                    return htmlReporter.specFilter(spec);
-                };
-
-                var currentWindowOnload = window.onload;
-
-                window.onload = function() {
-                    if (currentWindowOnload) {
-                        currentWindowOnload();
-                    }
-                    execJasmine();
-                };
-
-                function execJasmine() {
-                    jasmineEnv.execute();
-                }
-            })();
-        </script>
-    </head>
-    <body>
-        <div id="stage" style="display:none;"></div>
-    </body>
-</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/spec/helper.js
----------------------------------------------------------------------
diff --git a/templates/app/www/spec/helper.js b/templates/app/www/spec/helper.js
deleted file mode 100644
index 9f99445..0000000
--- a/templates/app/www/spec/helper.js
+++ /dev/null
@@ -1,11 +0,0 @@
-afterEach(function() {
-    document.getElementById('stage').innerHTML = '';
-});
-
-var helper = {
-    trigger: function(obj, name) {
-        var e = document.createEvent('Event');
-        e.initEvent(name, true, true);
-        obj.dispatchEvent(e);
-    }
-};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/spec/index.js
----------------------------------------------------------------------
diff --git a/templates/app/www/spec/index.js b/templates/app/www/spec/index.js
deleted file mode 100644
index 121cf63..0000000
--- a/templates/app/www/spec/index.js
+++ /dev/null
@@ -1,49 +0,0 @@
-describe('app', function() {
-    describe('initialize', function() {
-        it('should bind deviceready', function() {
-            runs(function() {
-                spyOn(app, 'deviceready');
-                app.initialize();
-                helper.trigger(window.document, 'deviceready');
-            });
-
-            waitsFor(function() {
-                return (app.deviceready.calls.length > 0);
-            }, 'deviceready should be called once', 500);
-
-            runs(function() {
-                expect(app.deviceready).toHaveBeenCalled();
-            });
-        });
-    });
-
-    describe('deviceready', function() {
-        it('should report that it fired', function() {
-            spyOn(app, 'report');
-            app.deviceready();
-            expect(app.report).toHaveBeenCalledWith('deviceready');
-        });
-    });
-
-    describe('report', function() {
-        beforeEach(function() {
-            var el = document.getElementById('stage');
-            el.innerHTML = ['<div id="deviceready">',
-                            '    <p class="status pending">Pending</p>',
-                            '    <p class="status complete hide">Complete</p>',
-                            '</div>'].join('\n');
-        });
-
-        it('should show the completion state', function() {
-            app.report('deviceready');
-            var el = document.querySelector('#deviceready .complete:not(.hide)');
-            expect(el).toBeTruthy();
-        });
-
-        it('should hide the pending state', function() {
-            app.report('deviceready');
-            var el = document.querySelector('#deviceready .pending.hide');
-            expect(el).toBeTruthy();
-        });
-    });
-});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/spec/lib/jasmine-1.2.0/MIT.LICENSE
----------------------------------------------------------------------
diff --git a/templates/app/www/spec/lib/jasmine-1.2.0/MIT.LICENSE b/templates/app/www/spec/lib/jasmine-1.2.0/MIT.LICENSE
deleted file mode 100644
index 7c435ba..0000000
--- a/templates/app/www/spec/lib/jasmine-1.2.0/MIT.LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2008-2011 Pivotal Labs
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/spec/lib/jasmine-1.2.0/jasmine-html.js
----------------------------------------------------------------------
diff --git a/templates/app/www/spec/lib/jasmine-1.2.0/jasmine-html.js b/templates/app/www/spec/lib/jasmine-1.2.0/jasmine-html.js
deleted file mode 100644
index a0b0639..0000000
--- a/templates/app/www/spec/lib/jasmine-1.2.0/jasmine-html.js
+++ /dev/null
@@ -1,616 +0,0 @@
-jasmine.HtmlReporterHelpers = {};
-
-jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
-  var el = document.createElement(type);
-
-  for (var i = 2; i < arguments.length; i++) {
-    var child = arguments[i];
-
-    if (typeof child === 'string') {
-      el.appendChild(document.createTextNode(child));
-    } else {
-      if (child) {
-        el.appendChild(child);
-      }
-    }
-  }
-
-  for (var attr in attrs) {
-    if (attr == "className") {
-      el[attr] = attrs[attr];
-    } else {
-      el.setAttribute(attr, attrs[attr]);
-    }
-  }
-
-  return el;
-};
-
-jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
-  var results = child.results();
-  var status = results.passed() ? 'passed' : 'failed';
-  if (results.skipped) {
-    status = 'skipped';
-  }
-
-  return status;
-};
-
-jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
-  var parentDiv = this.dom.summary;
-  var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
-  var parent = child[parentSuite];
-
-  if (parent) {
-    if (typeof this.views.suites[parent.id] == 'undefined') {
-      this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
-    }
-    parentDiv = this.views.suites[parent.id].element;
-  }
-
-  parentDiv.appendChild(childElement);
-};
-
-
-jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
-  for(var fn in jasmine.HtmlReporterHelpers) {
-    ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
-  }
-};
-
-jasmine.HtmlReporter = function(_doc) {
-  var self = this;
-  var doc = _doc || window.document;
-
-  var reporterView;
-
-  var dom = {};
-
-  // Jasmine Reporter Public Interface
-  self.logRunningSpecs = false;
-
-  self.reportRunnerStarting = function(runner) {
-    var specs = runner.specs() || [];
-
-    if (specs.length == 0) {
-      return;
-    }
-
-    createReporterDom(runner.env.versionString());
-    doc.body.appendChild(dom.reporter);
-
-    reporterView = new jasmine.HtmlReporter.ReporterView(dom);
-    reporterView.addSpecs(specs, self.specFilter);
-  };
-
-  self.reportRunnerResults = function(runner) {
-    reporterView && reporterView.complete();
-  };
-
-  self.reportSuiteResults = function(suite) {
-    reporterView.suiteComplete(suite);
-  };
-
-  self.reportSpecStarting = function(spec) {
-    if (self.logRunningSpecs) {
-      self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
-    }
-  };
-
-  self.reportSpecResults = function(spec) {
-    reporterView.specComplete(spec);
-  };
-
-  self.log = function() {
-    var console = jasmine.getGlobal().console;
-    if (console && console.log) {
-      if (console.log.apply) {
-        console.log.apply(console, arguments);
-      } else {
-        console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
-      }
-    }
-  };
-
-  self.specFilter = function(spec) {
-    if (!focusedSpecName()) {
-      return true;
-    }
-
-    return spec.getFullName().indexOf(focusedSpecName()) === 0;
-  };
-
-  return self;
-
-  function focusedSpecName() {
-    var specName;
-
-    (function memoizeFocusedSpec() {
-      if (specName) {
-        return;
-      }
-
-      var paramMap = [];
-      var params = doc.location.search.substring(1).split('&');
-
-      for (var i = 0; i < params.length; i++) {
-        var p = params[i].split('=');
-        paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
-      }
-
-      specName = paramMap.spec;
-    })();
-
-    return specName;
-  }
-
-  function createReporterDom(version) {
-    dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
-      dom.banner = self.createDom('div', { className: 'banner' },
-        self.createDom('span', { className: 'title' }, "Jasmine "),
-        self.createDom('span', { className: 'version' }, version)),
-
-      dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
-      dom.alert = self.createDom('div', {className: 'alert'}),
-      dom.results = self.createDom('div', {className: 'results'},
-        dom.summary = self.createDom('div', { className: 'summary' }),
-        dom.details = self.createDom('div', { id: 'details' }))
-    );
-  }
-};
-jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) {
-  this.startedAt = new Date();
-  this.runningSpecCount = 0;
-  this.completeSpecCount = 0;
-  this.passedCount = 0;
-  this.failedCount = 0;
-  this.skippedCount = 0;
-
-  this.createResultsMenu = function() {
-    this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
-      this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
-      ' | ',
-      this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
-
-    this.summaryMenuItem.onclick = function() {
-      dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
-    };
-
-    this.detailsMenuItem.onclick = function() {
-      showDetails();
-    };
-  };
-
-  this.addSpecs = function(specs, specFilter) {
-    this.totalSpecCount = specs.length;
-
-    this.views = {
-      specs: {},
-      suites: {}
-    };
-
-    for (var i = 0; i < specs.length; i++) {
-      var spec = specs[i];
-      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
-      if (specFilter(spec)) {
-        this.runningSpecCount++;
-      }
-    }
-  };
-
-  this.specComplete = function(spec) {
-    this.completeSpecCount++;
-
-    if (isUndefined(this.views.specs[spec.id])) {
-      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
-    }
-
-    var specView = this.views.specs[spec.id];
-
-    switch (specView.status()) {
-      case 'passed':
-        this.passedCount++;
-        break;
-
-      case 'failed':
-        this.failedCount++;
-        break;
-
-      case 'skipped':
-        this.skippedCount++;
-        break;
-    }
-
-    specView.refresh();
-    this.refresh();
-  };
-
-  this.suiteComplete = function(suite) {
-    var suiteView = this.views.suites[suite.id];
-    if (isUndefined(suiteView)) {
-      return;
-    }
-    suiteView.refresh();
-  };
-
-  this.refresh = function() {
-
-    if (isUndefined(this.resultsMenu)) {
-      this.createResultsMenu();
-    }
-
-    // currently running UI
-    if (isUndefined(this.runningAlert)) {
-      this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
-      dom.alert.appendChild(this.runningAlert);
-    }
-    this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
-
-    // skipped specs UI
-    if (isUndefined(this.skippedAlert)) {
-      this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
-    }
-
-    this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
-
-    if (this.skippedCount === 1 && isDefined(dom.alert)) {
-      dom.alert.appendChild(this.skippedAlert);
-    }
-
-    // passing specs UI
-    if (isUndefined(this.passedAlert)) {
-      this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
-    }
-    this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
-
-    // failing specs UI
-    if (isUndefined(this.failedAlert)) {
-      this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
-    }
-    this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
-
-    if (this.failedCount === 1 && isDefined(dom.alert)) {
-      dom.alert.appendChild(this.failedAlert);
-      dom.alert.appendChild(this.resultsMenu);
-    }
-
-    // summary info
-    this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
-    this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
-  };
-
-  this.complete = function() {
-    dom.alert.removeChild(this.runningAlert);
-
-    this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
-
-    if (this.failedCount === 0) {
-      dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
-    } else {
-      showDetails();
-    }
-
-    dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
-  };
-
-  return this;
-
-  function showDetails() {
-    if (dom.reporter.className.search(/showDetails/) === -1) {
-      dom.reporter.className += " showDetails";
-    }
-  }
-
-  function isUndefined(obj) {
-    return typeof obj === 'undefined';
-  }
-
-  function isDefined(obj) {
-    return !isUndefined(obj);
-  }
-
-  function specPluralizedFor(count) {
-    var str = count + " spec";
-    if (count > 1) {
-      str += "s"
-    }
-    return str;
-  }
-
-};
-
-jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
-
-
-jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
-  this.spec = spec;
-  this.dom = dom;
-  this.views = views;
-
-  this.symbol = this.createDom('li', { className: 'pending' });
-  this.dom.symbolSummary.appendChild(this.symbol);
-
-  this.summary = this.createDom('div', { className: 'specSummary' },
-      this.createDom('a', {
-        className: 'description',
-        href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
-        title: this.spec.getFullName()
-      }, this.spec.description)
-  );
-
-  this.detail = this.createDom('div', { className: 'specDetail' },
-      this.createDom('a', {
-        className: 'description',
-        href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
-        title: this.spec.getFullName()
-      }, this.spec.getFullName())
-  );
-};
-
-jasmine.HtmlReporter.SpecView.prototype.status = function() {
-  return this.getSpecStatus(this.spec);
-};
-
-jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
-  this.symbol.className = this.status();
-
-  switch (this.status()) {
-    case 'skipped':
-      break;
-
-    case 'passed':
-      this.appendSummaryToSuiteDiv();
-      break;
-
-    case 'failed':
-      this.appendSummaryToSuiteDiv();
-      this.appendFailureDetail();
-      break;
-  }
-};
-
-jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
-  this.summary.className += ' ' + this.status();
-  this.appendToSummary(this.spec, this.summary);
-};
-
-jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
-  this.detail.className += ' ' + this.status();
-
-  var resultItems = this.spec.results().getItems();
-  var messagesDiv = this.createDom('div', { className: 'messages' });
-
-  for (var i = 0; i < resultItems.length; i++) {
-    var result = resultItems[i];
-
-    if (result.type == 'log') {
-      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
-    } else if (result.type == 'expect' && result.passed && !result.passed()) {
-      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
-
-      if (result.trace.stack) {
-        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
-      }
-    }
-  }
-
-  if (messagesDiv.childNodes.length > 0) {
-    this.detail.appendChild(messagesDiv);
-    this.dom.details.appendChild(this.detail);
-  }
-};
-
-jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
-  this.suite = suite;
-  this.dom = dom;
-  this.views = views;
-
-  this.element = this.createDom('div', { className: 'suite' },
-      this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
-  );
-
-  this.appendToSummary(this.suite, this.element);
-};
-
-jasmine.HtmlReporter.SuiteView.prototype.status = function() {
-  return this.getSpecStatus(this.suite);
-};
-
-jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
-  this.element.className += " " + this.status();
-};
-
-jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
-
-/* @deprecated Use jasmine.HtmlReporter instead
- */
-jasmine.TrivialReporter = function(doc) {
-  this.document = doc || document;
-  this.suiteDivs = {};
-  this.logRunningSpecs = false;
-};
-
-jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
-  var el = document.createElement(type);
-
-  for (var i = 2; i < arguments.length; i++) {
-    var child = arguments[i];
-
-    if (typeof child === 'string') {
-      el.appendChild(document.createTextNode(child));
-    } else {
-      if (child) { el.appendChild(child); }
-    }
-  }
-
-  for (var attr in attrs) {
-    if (attr == "className") {
-      el[attr] = attrs[attr];
-    } else {
-      el.setAttribute(attr, attrs[attr]);
-    }
-  }
-
-  return el;
-};
-
-jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
-  var showPassed, showSkipped;
-
-  this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
-      this.createDom('div', { className: 'banner' },
-        this.createDom('div', { className: 'logo' },
-            this.createDom('span', { className: 'title' }, "Jasmine"),
-            this.createDom('span', { className: 'version' }, runner.env.versionString())),
-        this.createDom('div', { className: 'options' },
-            "Show ",
-            showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
-            this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
-            showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
-            this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
-            )
-          ),
-
-      this.runnerDiv = this.createDom('div', { className: 'runner running' },
-          this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
-          this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
-          this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
-      );
-
-  this.document.body.appendChild(this.outerDiv);
-
-  var suites = runner.suites();
-  for (var i = 0; i < suites.length; i++) {
-    var suite = suites[i];
-    var suiteDiv = this.createDom('div', { className: 'suite' },
-        this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
-        this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
-    this.suiteDivs[suite.id] = suiteDiv;
-    var parentDiv = this.outerDiv;
-    if (suite.parentSuite) {
-      parentDiv = this.suiteDivs[suite.parentSuite.id];
-    }
-    parentDiv.appendChild(suiteDiv);
-  }
-
-  this.startedAt = new Date();
-
-  var self = this;
-  showPassed.onclick = function(evt) {
-    if (showPassed.checked) {
-      self.outerDiv.className += ' show-passed';
-    } else {
-      self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
-    }
-  };
-
-  showSkipped.onclick = function(evt) {
-    if (showSkipped.checked) {
-      self.outerDiv.className += ' show-skipped';
-    } else {
-      self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
-    }
-  };
-};
-
-jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
-  var results = runner.results();
-  var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
-  this.runnerDiv.setAttribute("class", className);
-  //do it twice for IE
-  this.runnerDiv.setAttribute("className", className);
-  var specs = runner.specs();
-  var specCount = 0;
-  for (var i = 0; i < specs.length; i++) {
-    if (this.specFilter(specs[i])) {
-      specCount++;
-    }
-  }
-  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
-  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
-  this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
-
-  this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
-};
-
-jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
-  var results = suite.results();
-  var status = results.passed() ? 'passed' : 'failed';
-  if (results.totalCount === 0) { // todo: change this to check results.skipped
-    status = 'skipped';
-  }
-  this.suiteDivs[suite.id].className += " " + status;
-};
-
-jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
-  if (this.logRunningSpecs) {
-    this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
-  }
-};
-
-jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
-  var results = spec.results();
-  var status = results.passed() ? 'passed' : 'failed';
-  if (results.skipped) {
-    status = 'skipped';
-  }
-  var specDiv = this.createDom('div', { className: 'spec '  + status },
-      this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
-      this.createDom('a', {
-        className: 'description',
-        href: '?spec=' + encodeURIComponent(spec.getFullName()),
-        title: spec.getFullName()
-      }, spec.description));
-
-
-  var resultItems = results.getItems();
-  var messagesDiv = this.createDom('div', { className: 'messages' });
-  for (var i = 0; i < resultItems.length; i++) {
-    var result = resultItems[i];
-
-    if (result.type == 'log') {
-      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
-    } else if (result.type == 'expect' && result.passed && !result.passed()) {
-      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
-
-      if (result.trace.stack) {
-        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
-      }
-    }
-  }
-
-  if (messagesDiv.childNodes.length > 0) {
-    specDiv.appendChild(messagesDiv);
-  }
-
-  this.suiteDivs[spec.suite.id].appendChild(specDiv);
-};
-
-jasmine.TrivialReporter.prototype.log = function() {
-  var console = jasmine.getGlobal().console;
-  if (console && console.log) {
-    if (console.log.apply) {
-      console.log.apply(console, arguments);
-    } else {
-      console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
-    }
-  }
-};
-
-jasmine.TrivialReporter.prototype.getLocation = function() {
-  return this.document.location;
-};
-
-jasmine.TrivialReporter.prototype.specFilter = function(spec) {
-  var paramMap = {};
-  var params = this.getLocation().search.substring(1).split('&');
-  for (var i = 0; i < params.length; i++) {
-    var p = params[i].split('=');
-    paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
-  }
-
-  if (!paramMap.spec) {
-    return true;
-  }
-  return spec.getFullName().indexOf(paramMap.spec) === 0;
-};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/spec/lib/jasmine-1.2.0/jasmine.css
----------------------------------------------------------------------
diff --git a/templates/app/www/spec/lib/jasmine-1.2.0/jasmine.css b/templates/app/www/spec/lib/jasmine-1.2.0/jasmine.css
deleted file mode 100644
index 826e575..0000000
--- a/templates/app/www/spec/lib/jasmine-1.2.0/jasmine.css
+++ /dev/null
@@ -1,81 +0,0 @@
-body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
-
-#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
-#HTMLReporter a { text-decoration: none; }
-#HTMLReporter a:hover { text-decoration: underline; }
-#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
-#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
-#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
-#HTMLReporter .version { color: #aaaaaa; }
-#HTMLReporter .banner { margin-top: 14px; }
-#HTMLReporter .duration { color: #aaaaaa; float: right; }
-#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
-#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
-#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
-#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
-#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
-#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
-#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
-#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
-#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
-#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
-#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
-#HTMLReporter .runningAlert { background-color: #666666; }
-#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
-#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
-#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
-#HTMLReporter .passingAlert { background-color: #a6b779; }
-#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
-#HTMLReporter .failingAlert { background-color: #cf867e; }
-#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
-#HTMLReporter .results { margin-top: 14px; }
-#HTMLReporter #details { display: none; }
-#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
-#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
-#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
-#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
-#HTMLReporter.showDetails .summary { display: none; }
-#HTMLReporter.showDetails #details { display: block; }
-#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
-#HTMLReporter .summary { margin-top: 14px; }
-#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
-#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
-#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
-#HTMLReporter .description + .suite { margin-top: 0; }
-#HTMLReporter .suite { margin-top: 14px; }
-#HTMLReporter .suite a { color: #333333; }
-#HTMLReporter #details .specDetail { margin-bottom: 28px; }
-#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
-#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
-#HTMLReporter .resultMessage span.result { display: block; }
-#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
-
-#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
-#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
-#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
-#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
-#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
-#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
-#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
-#TrivialReporter .runner.running { background-color: yellow; }
-#TrivialReporter .options { text-align: right; font-size: .8em; }
-#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
-#TrivialReporter .suite .suite { margin: 5px; }
-#TrivialReporter .suite.passed { background-color: #dfd; }
-#TrivialReporter .suite.failed { background-color: #fdd; }
-#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
-#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
-#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
-#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
-#TrivialReporter .spec.skipped { background-color: #bbb; }
-#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
-#TrivialReporter .passed { background-color: #cfc; display: none; }
-#TrivialReporter .failed { background-color: #fbb; }
-#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
-#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
-#TrivialReporter .resultMessage .mismatch { color: black; }
-#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
-#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
-#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
-#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
-#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }


[42/50] git commit: [CB-3419] Change plugin XML namespace to cordova.apache.org

Posted by br...@apache.org.
[CB-3419] Change plugin XML namespace to cordova.apache.org


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

Branch: refs/heads/master2
Commit: b9815885f988a57cb83bf7351407ad3951907084
Parents: 37583c2
Author: Ian Clelland <ic...@chromium.org>
Authored: Fri May 17 13:55:14 2013 -0400
Committer: Ian Clelland <ic...@chromium.org>
Committed: Thu May 23 16:04:02 2013 -0400

----------------------------------------------------------------------
 templates/www/config.xml |   46 ++++++++++++++++++++--------------------
 1 files changed, 23 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/b9815885/templates/www/config.xml
----------------------------------------------------------------------
diff --git a/templates/www/config.xml b/templates/www/config.xml
index 206bc56..aca4d5e 100644
--- a/templates/www/config.xml
+++ b/templates/www/config.xml
@@ -1,6 +1,6 @@
 <?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>
@@ -14,29 +14,29 @@
     </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" />
 


[25/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/www/css/index.css
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/www/css/index.css b/lib/cordova-wp7/templates/standalone/www/css/index.css
new file mode 100644
index 0000000..1d34d88
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent WebKit from resizing text to fit */
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+}
+
+body {
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/www/img/logo.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/www/img/logo.png b/lib/cordova-wp7/templates/standalone/www/img/logo.png
new file mode 100644
index 0000000..9519e7d
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/www/img/logo.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/www/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/www/index.html b/lib/cordova-wp7/templates/standalone/www/index.html
new file mode 100644
index 0000000..31f61f4
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/www/index.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!--
+       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.
+-->
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova-2.7.0.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/www/js/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/www/js/index.js b/lib/cordova-wp7/templates/standalone/www/js/index.js
new file mode 100644
index 0000000..bf02e98
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // `load`, `deviceready`, `offline`, and `online`.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of `this` is the event. In order to call the `receivedEvent`
+    // function, we must explicitly call `app.receivedEvent(...);`
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/vs/MyTemplateStandAlone.vstemplate
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/vs/MyTemplateStandAlone.vstemplate b/lib/cordova-wp7/templates/vs/MyTemplateStandAlone.vstemplate
new file mode 100644
index 0000000..34b949d
--- /dev/null
+++ b/lib/cordova-wp7/templates/vs/MyTemplateStandAlone.vstemplate
@@ -0,0 +1,115 @@
+<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
+  <TemplateData>
+    <Name>CordovaWP7_2_7_0</Name>
+    <Description>Cordova 2.7.0 for Windows Phone 7.5 using the Cordova source code directly.</Description>
+    <ProjectType>CSharp</ProjectType>
+    <ProjectSubType>
+    </ProjectSubType>
+    <SortOrder>1000</SortOrder>
+    <CreateNewFolder>true</CreateNewFolder>
+    <DefaultName>CordovaWP7_2_7_0</DefaultName>
+    <ProvideDefaultName>true</ProvideDefaultName>
+    <LocationField>Enabled</LocationField>
+    <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
+    <Icon>__TemplateIcon.png</Icon>
+    <PreviewImage>__PreviewImage.jpg</PreviewImage>
+  </TemplateData>
+  <TemplateContent>
+    <Project TargetFileName="CordovaAppProj.csproj" File="CordovaAppProj.csproj" ReplaceParameters="true">
+      <ProjectItem ReplaceParameters="true" TargetFileName="App.xaml">App.xaml</ProjectItem>
+      <ProjectItem ReplaceParameters="true" TargetFileName="App.xaml.cs">App.xaml.cs</ProjectItem>
+      <ProjectItem ReplaceParameters="false" TargetFileName="ApplicationIcon.png">ApplicationIcon.png</ProjectItem>
+      <ProjectItem ReplaceParameters="false" TargetFileName="Background.png">Background.png</ProjectItem>
+      <ProjectItem ReplaceParameters="true" TargetFileName="BuildManifestProcessor.js">BuildManifestProcessor.js</ProjectItem>
+      <ProjectItem ReplaceParameters="true" TargetFileName="config.xml">config.xml</ProjectItem>
+      <Folder Name="cordovalib" TargetFolderName="cordovalib">
+        <ProjectItem ReplaceParameters="true" TargetFileName="BrowserMouseHelper.cs">BrowserMouseHelper.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="CommandFactory.cs">CommandFactory.cs</ProjectItem>
+        <Folder Name="Commands" TargetFolderName="Commands">
+          <ProjectItem ReplaceParameters="true" TargetFileName="BaseCommand.cs">BaseCommand.cs</ProjectItem>
+        </Folder>
+        <ProjectItem ReplaceParameters="true" TargetFileName="ConfigHandler.cs">ConfigHandler.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="CordovaCommandCall.cs">CordovaCommandCall.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="CordovaView.xaml">CordovaView.xaml</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="CordovaView.xaml.cs">CordovaView.xaml.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="DOMStorageHelper.cs">DOMStorageHelper.cs</ProjectItem>
+        <Folder Name="JSON" TargetFolderName="JSON">
+          <ProjectItem ReplaceParameters="true" TargetFileName="JsonHelper.cs">JsonHelper.cs</ProjectItem>
+        </Folder>
+        <ProjectItem ReplaceParameters="true" TargetFileName="NativeExecution.cs">NativeExecution.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="OrientationHelper.cs">OrientationHelper.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="PluginResult.cs">PluginResult.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="ScriptCallback.cs">ScriptCallback.cs</ProjectItem>
+      </Folder>
+      <ProjectItem ReplaceParameters="true" TargetFileName="CordovaSourceDictionary.xml">CordovaSourceDictionary.xml</ProjectItem>
+      <Folder Name="Images" TargetFolderName="Images">
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.back.rest.png">appbar.back.rest.png</ProjectItem>
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.close.rest.png">appbar.close.rest.png</ProjectItem>
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.feature.video.rest.png">appbar.feature.video.rest.png</ProjectItem>
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.next.rest.png">appbar.next.rest.png</ProjectItem>
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.save.rest.png">appbar.save.rest.png</ProjectItem>
+        <ProjectItem ReplaceParameters="false" TargetFileName="appbar.stop.rest.png">appbar.stop.rest.png</ProjectItem>
+      </Folder>
+      <ProjectItem ReplaceParameters="true" TargetFileName="MainPage.xaml">MainPage.xaml</ProjectItem>
+      <ProjectItem ReplaceParameters="true" TargetFileName="MainPage.xaml.cs">MainPage.xaml.cs</ProjectItem>
+      <Folder Name="Plugins" TargetFolderName="Plugins">
+        <ProjectItem ReplaceParameters="true" TargetFileName="Accelerometer.cs">Accelerometer.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="AudioFormatsHelper.cs">AudioFormatsHelper.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="AudioPlayer.cs">AudioPlayer.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Battery.cs">Battery.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Camera.cs">Camera.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Capture.cs">Capture.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Compass.cs">Compass.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Contacts.cs">Contacts.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="DebugConsole.cs">DebugConsole.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Device.cs">Device.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="File.cs">File.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="FileTransfer.cs">FileTransfer.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="GeoLocation.cs">GeoLocation.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Globalization.cs">Globalization.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="ImageExifHelper.cs">ImageExifHelper.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="InAppBrowser.cs">InAppBrowser.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Media.cs">Media.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="MimeTypeMapper.cs">MimeTypeMapper.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="NetworkStatus.cs">NetworkStatus.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="Notification.cs">Notification.cs</ProjectItem>
+        <Folder Name="UI" TargetFolderName="UI">
+          <ProjectItem ReplaceParameters="true" TargetFileName="AudioCaptureTask.cs">AudioCaptureTask.cs</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="AudioRecorder.xaml">AudioRecorder.xaml</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="AudioRecorder.xaml.cs">AudioRecorder.xaml.cs</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="ImageCapture.xaml">ImageCapture.xaml</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="ImageCapture.xaml.cs">ImageCapture.xaml.cs</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="NotificationBox.xaml">NotificationBox.xaml</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="NotificationBox.xaml.cs">NotificationBox.xaml.cs</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="VideoCaptureTask.cs">VideoCaptureTask.cs</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="VideoRecorder.xaml">VideoRecorder.xaml</ProjectItem>
+          <ProjectItem ReplaceParameters="true" TargetFileName="VideoRecorder.xaml.cs">VideoRecorder.xaml.cs</ProjectItem>
+        </Folder>
+      </Folder>
+      <Folder Name="Properties" TargetFolderName="Properties">
+        <ProjectItem ReplaceParameters="true" TargetFileName="AppManifest.xml">AppManifest.xml</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="AssemblyInfo.cs">AssemblyInfo.cs</ProjectItem>
+        <ProjectItem ReplaceParameters="true" TargetFileName="WMAppManifest.xml">WMAppManifest.xml</ProjectItem>
+      </Folder>
+      <Folder Name="resources" TargetFolderName="resources">
+        <ProjectItem ReplaceParameters="false" TargetFileName="notification-beep.wav">notification-beep.wav</ProjectItem>
+      </Folder>
+      <Folder Name="Service References" TargetFolderName="Service References" />
+      <ProjectItem ReplaceParameters="false" TargetFileName="SplashScreenImage.jpg">SplashScreenImage.jpg</ProjectItem>
+      <ProjectItem ReplaceParameters="false" TargetFileName="VERSION">VERSION</ProjectItem>
+      <Folder Name="www" TargetFolderName="www">
+        <ProjectItem ReplaceParameters="true" TargetFileName="cordova-2.7.0.js">cordova-2.7.0.js</ProjectItem>
+        <Folder Name="css" TargetFolderName="css">
+          <ProjectItem ReplaceParameters="true" TargetFileName="index.css">index.css</ProjectItem>
+        </Folder>
+        <Folder Name="img" TargetFolderName="img">
+          <ProjectItem ReplaceParameters="false" TargetFileName="logo.png">logo.png</ProjectItem>
+        </Folder>
+        <ProjectItem ReplaceParameters="true" TargetFileName="index.html">index.html</ProjectItem>
+        <Folder Name="js" TargetFolderName="js">
+          <ProjectItem ReplaceParameters="true" TargetFileName="index.js">index.js</ProjectItem>
+        </Folder>
+      </Folder>
+    </Project>
+  </TemplateContent>
+</VSTemplate>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/vs/description.txt
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/vs/description.txt b/lib/cordova-wp7/templates/vs/description.txt
new file mode 100644
index 0000000..089ad81
--- /dev/null
+++ b/lib/cordova-wp7/templates/vs/description.txt
@@ -0,0 +1,4 @@
+CordovaStarter
+
+Starter project for building a Cordova app for Windows Phone version: 2.6.0
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/vs/pg_templateIcon.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/vs/pg_templateIcon.png b/lib/cordova-wp7/templates/vs/pg_templateIcon.png
new file mode 100644
index 0000000..75c17c7
Binary files /dev/null and b/lib/cordova-wp7/templates/vs/pg_templateIcon.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/vs/pg_templatePreview.jpg
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/vs/pg_templatePreview.jpg b/lib/cordova-wp7/templates/vs/pg_templatePreview.jpg
new file mode 100644
index 0000000..1d72941
Binary files /dev/null and b/lib/cordova-wp7/templates/vs/pg_templatePreview.jpg differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tests/README.md
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tests/README.md b/lib/cordova-wp7/tests/README.md
new file mode 100644
index 0000000..cdf9516
--- /dev/null
+++ b/lib/cordova-wp7/tests/README.md
@@ -0,0 +1,7 @@
+Tests Have Moved
+===
+
+Tests now live in the MobileSpec repo at:
+https://github.com/apache/incubator-cordova-mobile-spec
+
+You will need to create a new project, and add the mobile-spec files to it to run the tests.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tooling/scripts/buildjs.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tooling/scripts/buildjs.bat b/lib/cordova-wp7/tooling/scripts/buildjs.bat
new file mode 100644
index 0000000..28d2423
--- /dev/null
+++ b/lib/cordova-wp7/tooling/scripts/buildjs.bat
@@ -0,0 +1,2 @@
+@echo off
+cscript "%~dp0\buildjs.js" %* //nologo
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tooling/scripts/buildjs.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tooling/scripts/buildjs.js b/lib/cordova-wp7/tooling/scripts/buildjs.js
new file mode 100644
index 0000000..b5c51ad
--- /dev/null
+++ b/lib/cordova-wp7/tooling/scripts/buildjs.js
@@ -0,0 +1,209 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject'),
+    shell = WScript.CreateObject("shell.application"),
+    wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments,
+    //Root folder of cordova-wp7 (i.e C:\Cordova\cordova-wp7)
+    ROOT = WScript.ScriptFullName.split('\\tooling\\', 1),
+    //Sub folder containing templates
+    TEMPLATES_PATH = '\\templates',
+    //Sub folder for standalone project
+    STANDALONE_PATH = TEMPLATES_PATH + '\\standalone',
+    //Sub folder containing framework
+    FRAMEWORK_PATH = '\\framework',
+    //Subfolder containing example project
+    EXAMPLE_PATH = '\\example',
+    //Git Repositories
+    CORDOVA_JS = "https://git-wip-us.apache.org/repos/asf/cordova-js.git",
+    // get version
+    VERSION = read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,''),
+    BUILD_DESTINATION;
+
+function Log(msg) {
+    WScript.StdOut.WriteLine(msg);
+}
+
+// help function
+function Usage()
+{
+    Log("");
+    Log("This Script builds the given virsion of cordova.js and injects it into this or the given cordova-wp7 ");
+    Log("");
+    Log("Usage: buildjs [ Version PathTOCordovaWP7 ]");
+    Log("    Version : The version of cordova.js to build (must already be tagged)");
+    Log("    PathTOCordovaWP7 : The path to the cordova directory where the new cordova.js will go.");
+    Log("examples:");
+    Log("    buildjs 2.5.0rc1  //Puts cordova-2.5.0rc1 as the cordova.js in the current working directory");
+    Log("    buildjs 2.4.0 C:\\Users\\anonymous\\Desktop\\cordova-wp7  //Puts cordova-2.4.0.js in the given directory");
+    Log("    buildjs //Builds the version of cordova.js from the root folder and adds it to the working directory repo");
+    Log("");
+}
+
+// returns the contents of a file
+function read(filename) {
+    //Log('Reading in ' + filename);
+    if(fso.FileExists(filename))
+    {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else
+    {
+        Log('Cannot read non-existant file : ' + filename);
+        WScript.Quit(1);
+    }
+    return null;
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        WScript.sleep(100);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    //Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if(!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if(!oShell.StdErr.AtEndOfStream)
+    {
+        var err_line = oShell.StdErr.ReadAll();
+        WScript.StdErr.WriteLine(err_line);
+        WScript.Quit(1);
+    }
+}
+
+function build_js(path)
+{
+    if(fso.FolderExists(path + '\\temp'))
+    {
+        fso.DeleteFolder(path + '\\temp', true);
+    }
+    fso.CreateFolder(path + '\\temp');
+    wscript_shell.CurrentDirectory = path + '\\temp';
+
+    Log('\tCloning js tagged with ' + VERSION + '...');
+    exec('%comspec% /c git clone ' + CORDOVA_JS + ' && cd cordova-js && git fetch && git checkout ' + VERSION );
+    if(!fso.FolderExists(path + '\\temp\\cordova-js'))
+    {
+        WScript.StdErr.WriteLine("ERROR: Failed to clone cordova-js. Aborting...");
+        WScript.Quit(1);
+    }
+    wscript_shell.CurrentDirectory = path + '\\temp\\cordova-js';
+    Log("Building Cordova.js...");
+
+    exec_verbose('%comspec% /c jake build');
+    if(!fso.FolderExists(path + '\\temp\\cordova-js\\pkg'))
+    {
+        WScript.StdErr.WriteLine("ERROR: Failed to build cordova-js. Aborting...");
+        WScript.Quit(1);
+    }
+
+    //copy the javascript wherever it needs to go.
+    wscript_shell.CurrentDirectory = path + '\\temp\\cordova-js\\pkg';
+    exec('%comspec% /c copy /Y cordova.windowsphone.js ' + path + STANDALONE_PATH + '\\www\\cordova-' + VERSION + '.js');
+    exec('%comspec% /c copy /Y cordova.windowsphone.js ' + path + EXAMPLE_PATH + '\\www\\cordova-' + VERSION + '.js');
+
+    //TODO: Delete old cordova.js (done in reversion.js)
+
+    Log("SUCESS");
+}
+
+function set_path(some_arg)
+{
+    if(some_arg.indexOf('-p:')!= -1)
+    {
+        var path = some_arg.split('-p:')[1];
+        if(fso.FolderExists(path) && fso.FolderExists(path + '\\tooling'))
+        {
+            BUILD_DESTINATION = path;
+            return true;
+        }
+        else
+        {
+            Log("ERROR: The given path is not a cordova-wp7 repo, or");
+            Log(" does not exist. If your trying to reversion a");
+            Log(" cordova repo other then this one, please provide");
+            Log(" it's path in the form: -p:C:\\Path\\to\\repo");
+            WScript.Quit(1);
+        }
+        
+    }
+    return false;
+}
+
+Log("");
+
+if(args.Count() > 1)
+{
+    set_path(args(1));
+}
+
+if(args.Count() > 0)
+{
+    //Support help flags
+    if(args(0).indexOf("--help") > -1 ||
+         args(0).indexOf("/?") > -1 )
+    {
+        Usage();
+        WScript.Quit(1);
+    }
+
+    if(args(0).match(/(\d+)[.](\d+)[.](\d+)(rc\d)?/))
+    {
+        VERSION = args(0);
+        if(args.Count()  == 1)
+        {
+            BUILD_DESTINATION = ROOT;
+        }
+    }
+    else if(set_path(arg(0))) {} //do nothing
+    else
+    {
+        Log("The provided version number is invalid, please provide");
+        Log(" a version number in the format Major.Minor.Fix[rc#]");
+        Usage();
+        WScript.Quit(1);
+    }
+}
+else
+{
+    BUILD_DESTINATION = ROOT;
+}
+
+//If we haven't quit by here, build the damn javascript!
+Log("Creating js for " + BUILD_DESTINATION);
+build_js(BUILD_DESTINATION);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tooling/scripts/dist.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tooling/scripts/dist.bat b/lib/cordova-wp7/tooling/scripts/dist.bat
new file mode 100644
index 0000000..a1c0e1d
--- /dev/null
+++ b/lib/cordova-wp7/tooling/scripts/dist.bat
@@ -0,0 +1,2 @@
+@echo off
+cscript "%~dp0\dist.js" %* //nologo

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tooling/scripts/dist.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tooling/scripts/dist.js b/lib/cordova-wp7/tooling/scripts/dist.js
new file mode 100644
index 0000000..4303202
--- /dev/null
+++ b/lib/cordova-wp7/tooling/scripts/dist.js
@@ -0,0 +1,217 @@
+/*
+       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.
+*/
+
+
+/*************************************************/
+/****************  REQUIREMENTS  *****************/
+/*************************************************/
+/*
+Paths:
+  - path to git.exe  -> C:\msysgit\bin
+  - path to msbuild -> C:\Windows\Microsoft.NET\Framework\v4.0.30319
+Famework
+  - .NET 4.0
+  - Windows phone SDKs
+
+
+/************ Globals ********/
+
+var fso = WScript.CreateObject('Scripting.FileSystemObject'),
+    wscript_shell = WScript.CreateObject("WScript.Shell");
+
+//Set up directory structure of current release
+    //arguments passed in
+var args = WScript.Arguments,
+    //Root folder of cordova-wp7 (i.e C:\Cordova\cordova-wp7)
+    ROOT = WScript.ScriptFullName.split('\\tooling\\', 1),
+    // tooling scripts
+    SCRIPTS = '\\tooling\\scripts';
+    //Get version number
+    VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
+
+//Destination to build to
+var BUILD_DESTINATION;
+//current script that is running
+var current_script = "dist";
+// replace the directory
+var replace = false;
+
+
+/*************************************************/
+/****************  FUNCTIONS  ********************/
+/*************************************************/
+
+
+// help function
+function Usage() {
+  Log("");
+  Log("This is a command line tool for building new releases. It will package a new release");
+  Log(" of a cordova-wp7 project, reversioning it to match the VERSION file in the root directory.");
+  Log("Usage: dist [ <NEW_PATH_FOR_BUILD> | -f ] ");
+  Log("                       -f : force tool to reversion the current repositoy.");
+  Log("     <NEW_PATH_FOR_BUILD> : path to create the new reversioned repositoy in.");
+  Log("");
+}
+
+// logs messaged to stdout and stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// returns the contents of a file
+function read(filename) {
+    //Log('Reading in ' + filename);
+    if(fso.FileExists(filename)) {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else {
+        Log('Cannot read non-existant file : ' + filename, true);
+        WScript.Quit(1);
+    }
+    return null;
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if(!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if(!oShell.StdErr.AtEndOfStream) {
+        var err_line = oShell.StdErr.ReadAll();
+        Log(err_line, true);
+        Log("ERROR: Could not complete distribution, failed while running: " + current_script, true);
+        WScript.Quit(1);
+    }
+}
+
+function space() {
+    Log("");
+    Log("*****************************************************");
+    Log("");
+}
+
+
+/*************************************************/
+/**************  MAIN SCRIPT  ********************/
+/*************************************************/
+
+if (args.Count() > 0) {
+    if (args.Count() == 1) {
+        // support help flags
+        if (args(0).indexOf("--help") > -1 ||
+              args(0).indexOf("/?") > -1 ) {
+            Usage();
+            WScript.Quit(1);
+        }
+        else if (args(0) == '-f') {
+          BUILD_DESTINATION = ROOT;
+          replace = true;
+        }
+        else {
+          BUILD_DESTINATION = args(0);
+        }
+
+    }
+    else if (args.Count() == 2) {
+        if (args(0) == '-f') {
+            replace = true;
+            BUILD_DESTINATION = args(1);
+        } else {
+           BUILD_DESTINATION = args(0);
+           if (args(1) == '-f') {
+              replace = true;
+           }
+           else {
+              Log('WARNING : "' + args(1) + '" is not regognized, attempting to continue distribution.');
+           }
+        }
+    }
+    else {
+        Log("Error : too many arguments provided.", true);
+        WScript.Quit(1);
+    }
+    
+}
+else {
+    Usage();
+    WScript.Quit(1);
+}
+
+
+/*************************************************/
+/******************  Step 1  *********************/
+/*************************************************/
+/** - Copy source code to new directory         **/
+/*************************************************/
+if (!replace) {
+  current_script = "new.js";
+  exec('cscript ' + ROOT + SCRIPTS + '\\new.js ' + BUILD_DESTINATION + ' //nologo');
+  space();
+}
+
+/*************************************************/
+/******************  Step 2  *********************/
+/*************************************************/
+/** - Retag everything with new version numbers **/
+/** - Delete any generated files and cordova.js **/
+/** - Rebuild dll                               **/
+/*************************************************/
+current_script = "reversion.js";
+exec('cscript ' + BUILD_DESTINATION + SCRIPTS + '\\reversion.js ' + VERSION + ' //nologo');
+space();
+
+/*************************************************/
+/******************  Step 3  *********************/
+/*************************************************/
+/** - Download tagged version of cordova.js     **/
+/** - build cordova.js                          **/
+/** - windows.cordova.js -> templates + example **/
+/*************************************************/
+current_script = "buildjs.js";
+exec('cscript ' + BUILD_DESTINATION + SCRIPTS + '\\buildjs.js //nologo');
+space();
+
+/*************************************************/
+/******************  Step 5  *********************/
+/*************************************************/
+/** - Build templates                           **/
+/** - Zip templates                             **/
+/** - inject into Visual Studio                 **/
+/*************************************************/
+current_script = "package.js";
+exec('cscript ' + BUILD_DESTINATION + SCRIPTS + '\\package.js //nologo');
+space();
+Log("Distribution Complete.");
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tooling/scripts/new.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tooling/scripts/new.bat b/lib/cordova-wp7/tooling/scripts/new.bat
new file mode 100644
index 0000000..cb08e2e
--- /dev/null
+++ b/lib/cordova-wp7/tooling/scripts/new.bat
@@ -0,0 +1,2 @@
+@echo off
+cscript "%~dp0\new.js" %* //nologo
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tooling/scripts/new.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tooling/scripts/new.js b/lib/cordova-wp7/tooling/scripts/new.js
new file mode 100644
index 0000000..c17dc94
--- /dev/null
+++ b/lib/cordova-wp7/tooling/scripts/new.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 fso = WScript.CreateObject('Scripting.FileSystemObject'),
+    shell = WScript.CreateObject("shell.application"),
+    wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments,
+    //Root folder of cordova-wp7 (i.e C:\Cordova\cordova-wp7)
+    ROOT = WScript.ScriptFullName.split('\\tooling\\', 1),
+    //Sub folder containing templates
+    TEMPLATES_PATH = '\\templates',
+    //Sub folder containing framework
+    FRAMEWORK_PATH = '\\framework',
+    //Subfolder containing example project
+    EXAMPLE_PATH = '\\example';
+// git repo for cordova-wp7
+var CORDOVA_WP7 = 'https://git-wip-us.apache.org/repos/asf/cordova-wp7.git';
+//Destination to build to
+var BUILD_DESTINATION;
+// pull the project down from github?
+var GET_NEW = false;
+
+// help function
+function Usage()
+{
+    WScript.StdOut.WriteLine("");
+    WScript.StdOut.WriteLine("Usage: new [ PathToDestinationFolder ]");
+    WScript.StdOut.WriteLine("    PathToDestinationFolder : Folder you wish to be created for a new cordova-wp7 repo");
+    WScript.StdOut.WriteLine("examples:");
+    WScript.StdOut.WriteLine("    new C:\\Users\\anonymous\\Desktop\\cordova-wp7");
+    WScript.StdOut.WriteLine("");
+}
+
+// returns the contents of a file
+function read(filename) {
+    //WScript.StdOut.WriteLine('Reading in ' + filename);
+    if(fso.FileExists(filename))
+    {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else
+    {
+        WScript.StdErr.WriteLine('Cannot read non-existant file : ' + filename);
+        WScript.Quit(1);
+    }
+    return null;
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        WScript.sleep(100);
+    }
+}
+
+function copy_to(path)
+{
+    //Copy everything over to BUILD_DESTINATION
+    var dest = shell.NameSpace(path);
+    WScript.StdOut.WriteLine("Copying files to build directory...");
+
+    /** copy by file instead? (just what we need)**/
+    dest.CopyHere(ROOT + "\\bin", 4|20);
+    dest.CopyHere(ROOT + EXAMPLE_PATH, 4|20);      //Should mostly be copied from standalone
+    dest.CopyHere(ROOT + FRAMEWORK_PATH, 4|20);
+    dest.CopyHere(ROOT + TEMPLATES_PATH, 4|20);
+    dest.CopyHere(ROOT + "\\tests", 4|20);
+    dest.CopyHere(ROOT + "\\tooling", 4|20);
+    dest.CopyHere(ROOT + "\\.gitignore", 4|20);
+    dest.CopyHere(ROOT + "\\LICENSE", 4|20);
+    dest.CopyHere(ROOT + "\\NOTICE", 4|20);
+    dest.CopyHere(ROOT + "\\README.md", 4|20);
+    dest.CopyHere(ROOT + "\\VERSION", 4|20);
+}
+
+WScript.StdOut.WriteLine("");
+
+if(args.Count() > 0)
+{
+    if(fso.FolderExists(args(0)))
+    {
+        WScript.StdErr.WriteLine("The given directory already exists!");
+        Usage();
+        WScript.Quit(1);
+    }
+    else
+    {
+        BUILD_DESTINATION = args(0);
+
+    }
+
+    if(!GET_NEW) {
+
+        if(fso.FolderExists(BUILD_DESTINATION))
+        {
+            WScript.StdErr.WriteLine("The given directory already exists!");
+            Usage();
+            WScript.Quit(1);
+        }
+        else
+        {
+            BUILD_DESTINATION = args(0);
+        }
+
+        // set up file structure
+        fso.CreateFolder(BUILD_DESTINATION);
+        // copy nessisary files
+        copy_to(BUILD_DESTINATION);
+    }
+    else
+    {
+        wscript_shell.CurrentDirectory = arg(0) + '\\..';
+        BUILD_DESTINATION = wscript_shell.CurrentDirectory + '\\cordova-wp7';
+
+        WScript.StdOut.WriteLine('Cloning cordova-wp7 from git, build destination now ' + BUILD_DESTINATION);
+        if(fso.FolderExists(BUILD_DESTINATION))
+        {
+            WScript.StdErr.WriteLine("Could not clone cordova-wp7 from git because it's directory already exists!");
+            WScript.Quit(1);
+        }
+
+        exec('git clone ' + CORDOVA_WP7); //git fetch --tags && git checkout?
+
+    }
+}
+else
+{
+    Usage();
+    WScript.Quit(1);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tooling/scripts/package.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tooling/scripts/package.bat b/lib/cordova-wp7/tooling/scripts/package.bat
new file mode 100644
index 0000000..c00551e
--- /dev/null
+++ b/lib/cordova-wp7/tooling/scripts/package.bat
@@ -0,0 +1,2 @@
+@echo off
+cscript "%~dp0\package.js" %* //nologo
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tooling/scripts/package.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tooling/scripts/package.js b/lib/cordova-wp7/tooling/scripts/package.js
new file mode 100644
index 0000000..f3970a8
--- /dev/null
+++ b/lib/cordova-wp7/tooling/scripts/package.js
@@ -0,0 +1,213 @@
+/*
+       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 fso = WScript.CreateObject('Scripting.FileSystemObject'),
+    shell = WScript.CreateObject("shell.application"),
+    wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var args = WScript.Arguments,
+    // root folder of cordova-wp7 (i.e C:\Cordova\cordova-wp7)
+    ROOT = WScript.ScriptFullName.split('\\tooling\\', 1),
+    // sub folder containing templates
+    TEMPLATES_PATH = '\\templates',
+    // sub folder for standalone project
+    STANDALONE_PATH = TEMPLATES_PATH + '\\standalone',
+    // sub folder containing framework
+    FRAMEWORK_PATH = '\\framework',
+    // subfolder containing example project
+    EXAMPLE_PATH = '\\example',
+    // get version number
+    VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,''),
+    BASE_VERSION = VERSION.split('rc', 1) + ".0";
+
+// destination directory to package
+var BUILD_DESTINATION;
+// add templates to visual studio?
+var ADD_TO_VS = false;
+// build the dll?
+var BUILD_DLL = false;
+
+function Log(msg) { WScript.StdOut.WriteLine(msg); }
+
+// help function
+function Usage()
+{
+    Log("");
+    Log("Usage: package [ PathToCordovaWP7 ]");
+    Log("    PathToCordovaWP7 : Cordova-wp7 repo you wish to package for release");
+    Log("examples:");
+    Log("    package C:\\Users\\anonymous\\Desktop\\cordova-wp7");
+    Log("    package     // packages current cordova directory");
+    Log("");
+}
+
+// returns the contents of a file
+function read(filename) {
+    //Log('Reading in ' + filename);
+    if(fso.FileExists(filename))
+    {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else
+    {
+        Log('ERROR: Cannot read non-existant file : ' + filename);
+        WScript.Quit(1);
+    }
+    return null;
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        WScript.sleep(100);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if(!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if(!oShell.StdErr.AtEndOfStream)
+    {
+        var err_line = oShell.StdErr.ReadAll();
+        WScript.StdErr.WriteLine(err_line);
+        WScript.Quit(1);
+    }
+}
+
+// packages templates into .zip
+function package_templates()
+{
+    Log("Creating template .zip files ...");
+
+    var template_path = BUILD_DESTINATION + '\\CordovaWP7_' + VERSION.replace(/\./g, '_') + '.zip';
+    if(fso.FileExists(template_path))
+    {
+      fso.DeleteFile(template_path);
+    }
+
+    exec('%comspec% /c copy /Y ' + BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\MyTemplateStandAlone.vstemplate ' + BUILD_DESTINATION + STANDALONE_PATH + '\\MyTemplate.vstemplate');
+    exec('%comspec% /c copy /Y ' + BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\pg_templateIcon.png ' + BUILD_DESTINATION + STANDALONE_PATH + '\\__TemplateIcon.png');
+    exec('%comspec% /c copy /Y ' + BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\pg_templatePreview.jpg ' + BUILD_DESTINATION + STANDALONE_PATH + '\\__PreviewImage.jpg');
+    exec('%comspec% /c copy /Y ' + BUILD_DESTINATION + '\\VERSION ' + BUILD_DESTINATION + STANDALONE_PATH);
+
+    exec_verbose('cscript ' + BUILD_DESTINATION + '\\tooling\\scripts\\win-zip.js ' + template_path + ' ' + BUILD_DESTINATION + STANDALONE_PATH + '\\ //nologo');
+
+
+    if(ADD_TO_VS)
+    {
+        var template_dir = wscript_shell.ExpandEnvironmentStrings("%USERPROFILE%") + '\\Documents\\Visual Studio 2012\\Templates\\ProjectTemplates';
+        if(fso.FolderExists(template_dir ))
+        {
+            dest = shell.NameSpace(template_dir);
+            dest.CopyHere(template_path, 4|20);
+        }
+        else
+        {
+            Log("WARNING: Could not find template directory in Visual Studio,\n you can manually copy over the template .zip files.");
+        }
+  }
+}
+
+// builds the new cordova dll and copys it to the full template (only done because of the version referance in Device.cs)
+function build_dll()
+{
+    Log("Packaging .dll ...");
+    // move to framework directory
+    wscript_shell.CurrentDirectory = BUILD_DESTINATION + FRAMEWORK_PATH;
+    // build .dll in Release
+    exec_verbose('msbuild /p:Configuration=Release;VersionNumber=' + VERSION + ';BaseVersionNumber=' + BASE_VERSION);
+    //Check if file dll was created
+    if(!fso.FileExists(BUILD_DESTINATION + FRAMEWORK_PATH + '\\Bin\\Release\\WPCordovaClassLib.dll'))
+    {
+        WScript.StdErr.WriteLine('ERROR: MSBuild failed to create .dll.');
+        WScript.Quit(1);
+    }
+
+    Log("SUCESS");
+}
+
+// delete any unnessisary files when finished
+function cleanUp() {
+
+  if(fso.FileExists(BUILD_DESTINATION + STANDALONE_PATH + '\\MyTemplate.vstemplate')) {
+      fso.DeleteFile(BUILD_DESTINATION + STANDALONE_PATH + '\\MyTemplate.vstemplate');
+  }
+  if(fso.FileExists(BUILD_DESTINATION + STANDALONE_PATH + '\\__PreviewImage.jpg')) {
+      fso.DeleteFile(BUILD_DESTINATION + STANDALONE_PATH + '\\__PreviewImage.jpg');
+  }
+  if(fso.FileExists(BUILD_DESTINATION + STANDALONE_PATH + '\\__TemplateIcon.png')) {
+      fso.DeleteFile(BUILD_DESTINATION + STANDALONE_PATH + '\\__TemplateIcon.png');
+  }
+  //Add any other cleanup here
+}
+
+
+Log("");
+
+if(args.Count() > 0)
+{
+    //Support help flags
+    if(args(0).indexOf("--help") > -1 ||
+         args(0).indexOf("/?") > -1 )
+    {
+        Usage();
+        WScript.Quit(1);
+    }
+
+    if(fso.FolderExists(args(0)) && fso.FolderExists(args(0) + '\\tooling'))
+    {
+        BUILD_DESTINATION = args(0);
+    }
+    else
+    {
+        Log("ERROR: The given directory is not a cordova-wp7 repo.");
+        Usage();
+        WScript.Quit(1);
+
+    }
+}
+else
+{
+    BUILD_DESTINATION = ROOT;
+}
+
+// build dll for full template
+if (BUILD_DLL) {
+  build_dll();
+}
+
+// build/package the templates
+package_templates(BUILD_DESTINATION);
+
+cleanUp();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tooling/scripts/reversion.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tooling/scripts/reversion.bat b/lib/cordova-wp7/tooling/scripts/reversion.bat
new file mode 100644
index 0000000..b35ab08
--- /dev/null
+++ b/lib/cordova-wp7/tooling/scripts/reversion.bat
@@ -0,0 +1,2 @@
+@echo off
+cscript "%~dp0\reversion.js" %* //nologo
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tooling/scripts/reversion.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tooling/scripts/reversion.js b/lib/cordova-wp7/tooling/scripts/reversion.js
new file mode 100644
index 0000000..3b42fc4
--- /dev/null
+++ b/lib/cordova-wp7/tooling/scripts/reversion.js
@@ -0,0 +1,277 @@
+/*
+       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.
+*/
+
+
+/************ Globals ********/
+
+var fso = WScript.CreateObject('Scripting.FileSystemObject'),
+    shell = WScript.CreateObject("shell.application"),
+    wscript_shell = WScript.CreateObject("WScript.Shell");
+
+//Set up directory structure of current release
+    //arguments passed in
+var args = WScript.Arguments,
+    //Root folder of cordova-wp7 (i.e C:\Cordova\cordova-wp7)
+    ROOT = WScript.ScriptFullName.split('\\tooling\\', 1),
+    //Sub folder containing templates
+    TEMPLATES_PATH = '\\templates',
+    //Sub folder for standalone project
+    STANDALONE_PATH = TEMPLATES_PATH + '\\standalone',
+    //Sub folder containing framework
+    FRAMEWORK_PATH = '\\framework',
+    //Subfolder containing example project
+    EXAMPLE_PATH = '\\example',
+    //Path to cordovalib folder, containing source for .dll
+    CORDOVA_LIB = STANDALONE_PATH + '\\cordovalib',
+    //Get version number
+    VERSION='',
+    BASE_VERSION = '';
+
+//Destination to build to
+var BUILD_DESTINATION;
+
+// help function
+function Usage()
+{
+    WScript.StdOut.WriteLine("");
+    WScript.StdOut.WriteLine("Usage: reversion [ Version PathTOCordovaWP7 ]");
+    WScript.StdOut.WriteLine("    Version : The new version for codova-wp7");
+    WScript.StdOut.WriteLine("    PathTOCordovaWP7 : The path to the cordova directory being reversioned.");
+    WScript.StdOut.WriteLine("examples:");
+    WScript.StdOut.WriteLine("    reversion 2.5.0rc1  //Reversions the current working directory");
+    WScript.StdOut.WriteLine("    reversion 2.5.0 C :\\Users\\anonymous\\Desktop\\cordova-wp7");
+    WScript.StdOut.WriteLine("");
+}
+
+var ForReading = 1, ForWriting = 2, ForAppending = 8;
+var TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0;
+
+// returns the contents of a file
+function read(filename) {
+    //WScript.Echo('Reading in ' + filename);
+    if(fso.FileExists(filename))
+    {
+        var f=fso.OpenTextFile(filename, 1,2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else
+    {
+        WScript.StdErr.WriteLine('Cannot read non-existant file : ' + filename);
+        WScript.Quit(1);
+    }
+    return null;
+}
+
+// writes the contents to the specified file
+function write(filename, contents) {
+    var f=fso.OpenTextFile(filename, ForWriting, TristateTrue);
+    f.Write(contents);
+    f.Close();
+}
+
+// replaces the matches of regexp with replacement
+function replaceInFile(filename, regexp, replacement) {
+    //WScript.Echo("Replaceing with "+replacement+ " in:");
+    var text = read(filename).replace(regexp,replacement);
+    //WScript.Echo(text);
+    write(filename,text);
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        WScript.sleep(100);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    //WScript.StdOut.WriteLine("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if(!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            WScript.StdOut.WriteLine(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if(!oShell.StdErr.AtEndOfStream)
+    {
+        var err_line = oShell.StdErr.ReadAll();
+        WScript.StdErr.WriteLine(err_line);
+        WScript.Quit(1);
+    }
+}
+
+function updateVersionNumbers() {
+    WScript.StdOut.WriteLine("Updating version numbers....");
+    var version_regex = /(\d+)[.](\d+)[.](\d+)(rc\d)?/;
+    replaceInFile(BUILD_DESTINATION + '\\VERSION', version_regex,  VERSION);
+    // replace assembaly versions in framework
+    var framework_regex = /Description\(\"(\d+)[.](\d+)[.](\d+)(rc\d)?\"\)\]/; //Will match ("x.x.x[rcx]")]
+    replaceInFile(BUILD_DESTINATION + FRAMEWORK_PATH + "\\Properties\\AssemblyInfo.cs", framework_regex, "Description(\"" + VERSION + "\")]");
+    framework_regex = /Version\(\"(\d+)[.](\d+)[.](\d+)[.](\d+)\"\)\]/g;
+    replaceInFile(BUILD_DESTINATION + FRAMEWORK_PATH + "\\Properties\\AssemblyInfo.cs", framework_regex, "Version(\"" + BASE_VERSION + "\")]");
+
+    // update standalone project
+    exec('%comspec% /c copy /Y /V ' + BUILD_DESTINATION + "\\VERSION " + BUILD_DESTINATION + STANDALONE_PATH + "\\VERSION");
+    var cordova_regex = /cordova-(\d+)[.](\d+)[.](\d+)(rc\d)?/g; //Matches *first* cordova-x.x.x[rcx] (just ad g at end to make global)
+    replaceInFile(BUILD_DESTINATION + STANDALONE_PATH + '\\CordovaAppProj.csproj', cordova_regex,  "cordova-" + VERSION);
+    replaceInFile(BUILD_DESTINATION + STANDALONE_PATH + '\\www\\index.html', cordova_regex,  "cordova-" + VERSION);
+    version_regex = /return\s*\"(\d+)[.](\d+)[.](\d+)(rc\d)?/; //Matches return "x.x.x[rcx]
+
+    WScript.StdOut.WriteLine("path = " + BUILD_DESTINATION + CORDOVA_LIB + '\\Plugins\\Device.cs');
+    replaceInFile(BUILD_DESTINATION + CORDOVA_LIB + '\\..\\Plugins\\Device.cs', version_regex,  "return \"" + VERSION);
+
+    // update example proj
+    replaceInFile(BUILD_DESTINATION + EXAMPLE_PATH + '\\CordovaExample.csproj', cordova_regex,  "cordova-" + VERSION);
+    version_regex = /VERSION\s*\=\s*\'(\d+)[.](\d+)[.](\d+)(rc\d)?/;  //Matches VERSION = x.x.x[rcx]
+    replaceInFile(BUILD_DESTINATION + EXAMPLE_PATH + '\\www\\cordova-current.js', version_regex,  "VERSION = \'" + VERSION);
+
+    // update template discription
+    version_regex = /Cordova\s*(\d+)[.](\d+)[.](\d+)(rc\d)?\s*Windows/g; //Matches version: x.x.x[rcx]
+    replaceInFile(BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\description.txt', version_regex,  "Cordova " + VERSION + " Windows");
+
+    // update .vstemplate files for the template zips.
+    var name_regex = /CordovaWP7[_](\d+)[_](\d+)[_](\d+)(rc\d)?/g;
+    var discript_regex = /Cordova\s*(\d+)[.](\d+)[.](\d+)(rc\d)?/;
+
+    replaceInFile(BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\MyTemplateStandAlone.vstemplate', name_regex,  'CordovaWP7_' + VERSION.replace(/\./g, '_'));
+    replaceInFile(BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\MyTemplateStandAlone.vstemplate', discript_regex,  "Cordova " + VERSION);
+    replaceInFile(BUILD_DESTINATION + TEMPLATES_PATH + '\\vs\\MyTemplateStandAlone.vstemplate', cordova_regex,  "cordova-" + VERSION);
+}
+
+// delete all cordova.js and generated files (templates) from old version numbers
+function cleanup()
+{
+    WScript.StdOut.WriteLine("Cleanup");
+    // remove old template .zip files
+    if(fso.FileExists(BUILD_DESTINATION + '\\*.zip'))
+    {
+        fso.DeleteFile(BUILD_DESTINATION + '\\*.zip');
+    }
+    // remove any generated framework files
+    if(fso.FolderExists(BUILD_DESTINATION + FRAMEWORK_PATH + '\\Bin'))
+    {
+        fso.DeleteFolder(BUILD_DESTINATION + FRAMEWORK_PATH + '\\Bin');
+    }
+    if(fso.FolderExists(BUILD_DESTINATION + FRAMEWORK_PATH + '\\obj'))
+    {
+        fso.DeleteFolder(BUILD_DESTINATION + FRAMEWORK_PATH + '\\obj');
+    }
+    // remove any generated CordovaDeploy
+    if(fso.FolderExists(BUILD_DESTINATION + '\\tooling\\CordovaDeploy\\CordovaDeploy\\bin'))
+    {
+        fso.DeleteFolder(BUILD_DESTINATION + '\\tooling\\CordovaDeploy\\CordovaDeploy\\bin');
+    }
+    if(fso.FolderExists(BUILD_DESTINATION + '\\tooling\\CordovaDeploy\\CordovaDeploy\\obj'))
+    {
+        fso.DeleteFolder(BUILD_DESTINATION + '\\tooling\\CordovaDeploy\\CordovaDeploy\\obj');
+    }
+    //remove old template .zip files
+    WScript.Echo(BUILD_DESTINATION);
+    var root_folder = shell.NameSpace(BUILD_DESTINATION + '\\').Items();
+    for(var i = 0; i < root_folder.Count; i++)
+    {
+        if(root_folder.Item(i).Name.match(/CordovaWP7[_](\d+)[_](\d+)[_](\d+)(rc\d)?/))
+        {
+            fso.DeleteFile(BUILD_DESTINATION + '\\' + root_folder.Item(i).Name);
+        }
+    }
+    // remove old cordova.js
+    var example_www = shell.NameSpace(BUILD_DESTINATION + EXAMPLE_PATH + '\\www').Items();
+    for(i = 0; i < example_www.Count; i++)
+    {
+        if(example_www.Item(i).Name.match(/cordova\-(\d+)[.](\d+)[.](\d+)(rc\d)?[.]js/))
+        {
+            fso.DeleteFile(BUILD_DESTINATION + EXAMPLE_PATH + '\\www\\' + example_www.Item(i).Name);
+        }
+    }
+
+    var standalone_www = shell.NameSpace(BUILD_DESTINATION + STANDALONE_PATH + '\\www').Items();
+    for(i = 0; i < standalone_www.Count; i++)
+    {
+        if(standalone_www.Item(i).Name.match(/cordova\-(\d+)[.](\d+)[.](\d+)(rc\d)?[.]js/))
+        {
+            fso.DeleteFile(BUILD_DESTINATION + STANDALONE_PATH + '\\www\\' + standalone_www.Item(i).Name);
+        }
+    }
+}
+
+
+WScript.StdOut.WriteLine("");
+
+if(args.Count() > 1)
+{
+    if(fso.FolderExists(args(1)) && fso.FolderExists(args(1) + '\\tooling'))
+    {
+        BUILD_DESTINATION = args(1);
+    }
+    else
+    {
+        WScript.StdErr.WriteLine("The given path is not a cordova-wp7 repo, if");
+        WScript.StdErr.WriteLine(" your trying to reversion a cordova-wp7 repo");
+        WScript.StdErr.WriteLine(" other then this one, please provide its path.");
+        Usage();
+        WScript.Quit(1);
+    }
+}
+
+if(args.Count() > 0)
+{
+    //Support help flags
+    if(args(0).indexOf("--help") > -1 ||
+         args(0).indexOf("/?") > -1 )
+    {
+        Usage();
+        WScript.Quit(1);
+    }
+
+    if(args(0).match(/(\d+)[.](\d+)[.](\d+)(rc\d)?/))
+    {
+        VERSION = args(0);
+        BASE_VERSION = VERSION.split('rc', 1) + ".0";
+        if(args.Count() < 2)
+        {
+          BUILD_DESTINATION = ROOT;
+        }
+        // remove old cordova.js files and any generated files
+        cleanup();
+        // update version numbers
+        updateVersionNumbers();
+    }
+    else
+    {
+        WScript.StdOut.WriteLine("The  version number is invalid, please provide");
+        WScript.StdOut.WriteLine(" a version number in the format Major.Minor.Fix[rc#]");
+        Usage();
+        WScript.Quit(1);
+    }
+}
+else
+{
+    Usage();
+    WScript.Quit(1);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/tooling/scripts/win-zip.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/tooling/scripts/win-zip.js b/lib/cordova-wp7/tooling/scripts/win-zip.js
new file mode 100644
index 0000000..3d8dc1a
--- /dev/null
+++ b/lib/cordova-wp7/tooling/scripts/win-zip.js
@@ -0,0 +1,32 @@
+/*
+ * Script for zipping the contents of a directory.
+ */
+
+// get commman line arguments
+var objArgs = WScript.Arguments;
+var zipPath = objArgs(0);
+var sourcePath = objArgs(1);
+
+
+// create empty ZIP file and open for adding
+var fso = new ActiveXObject("Scripting.FileSystemObject");
+var file = fso.CreateTextFile(zipPath, true);
+
+// create twenty-two byte "fingerprint" for .zip
+file.write("PK");
+file.write(String.fromCharCode(5));
+file.write(String.fromCharCode(6));
+file.write('\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0');
+file.Close();
+
+// open .zip foder and copy contents of sourcePath
+var objShell = new ActiveXObject("shell.application");
+var zipFolder = objShell.NameSpace(zipPath);
+var sourceItems = objShell.NameSpace(sourcePath).items();
+if (zipFolder !== null) {
+    zipFolder.CopyHere(sourceItems, 4|20|16);
+    WScript.Sleep(4000);
+}
+else {
+	WScript.StdErr.WriteLine('Failed to create .zip file.');
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/LICENSE
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/LICENSE b/lib/cordova-wp8/LICENSE
new file mode 100644
index 0000000..6a504ba
--- /dev/null
+++ b/lib/cordova-wp8/LICENSE
@@ -0,0 +1,12 @@
+   
+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.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/NOTICE
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/NOTICE b/lib/cordova-wp8/NOTICE
new file mode 100644
index 0000000..c38e7d7
--- /dev/null
+++ b/lib/cordova-wp8/NOTICE
@@ -0,0 +1,5 @@
+Apache Cordova
+Copyright 2012 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/README.md
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/README.md b/lib/cordova-wp8/README.md
new file mode 100644
index 0000000..8d772b3
--- /dev/null
+++ b/lib/cordova-wp8/README.md
@@ -0,0 +1,42 @@
+Apache Cordova for Windows Phone 8
+===
+
+Apache Cordova WP8 is a .net application library that lets you create Apache Cordova applications targeting Windows Phone 8 devices.
+Apache Cordova based applications are, at the core, an application written with web technology: HTML, CSS and JavaScript.
+
+Requires
+---
+
+- Windows Phone SDK 8 [http://www.microsoft.com/en-us/download/details.aspx?id=35471]
+
+
+Getting Started
+---
+
+- copy the file templates/CordovaStarter-x.x.x.zip to the folder : \My Documents\Visual Studio 2012\Templates\ProjectTemplates\
+ - if you have just installed VisualStudio, you should launch it once to create this folder
+ - if you prefer, you may add the project instead to the "Silverlight for Windows Phone" subfolder of "Visual C#".  This is up to you, and only affects where the project template is shown when creating a new project. Also, You may need to create this folder.
+- Launch Visual Studio 2012 and select to create a new project
+ - CordovaStarter should be listed as an option, give your new project a name
+  - Note: The description will let you know the version of Cordova you are targetting, if you have multiple templates.
+ - If you do not see it, you may have to select the top level 'Visual C#' to see it
+- Build and Run it!
+
+
+Known Problem Areas
+---
+
+Many of the Media APIs will not function as expected when debugging while connect to the device with the Zune software.
+To get around this, you need to use the Windows Phone Connect tool. For details, please check out this [MSDN blog article](http://blogs.msdn.com/b/jaimer/archive/2010/11/03/tips-for-debugging-wp7-media-apps-with-wpconnect.aspx).
+
+
+BUGS?
+-----
+File them at Apache Incubator
+https://issues.apache.org/jira/browse/CB
+
+Further Reading
+---
+
+- [http://docs.phonegap.com](http://docs.phonegap.com)
+- [http://wiki.phonegap.com](http://wiki.phonegap.com)

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/VERSION
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/VERSION b/lib/cordova-wp8/VERSION
new file mode 100644
index 0000000..9aa3464
--- /dev/null
+++ b/lib/cordova-wp8/VERSION
@@ -0,0 +1 @@
+2.7.0
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/framework/Images/appbar.back.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/framework/Images/appbar.back.rest.png b/lib/cordova-wp8/framework/Images/appbar.back.rest.png
new file mode 100644
index 0000000..4bc2b92
Binary files /dev/null and b/lib/cordova-wp8/framework/Images/appbar.back.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/framework/Images/appbar.close.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/framework/Images/appbar.close.rest.png b/lib/cordova-wp8/framework/Images/appbar.close.rest.png
new file mode 100644
index 0000000..8166a1c
Binary files /dev/null and b/lib/cordova-wp8/framework/Images/appbar.close.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/framework/Images/appbar.feature.video.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/framework/Images/appbar.feature.video.rest.png b/lib/cordova-wp8/framework/Images/appbar.feature.video.rest.png
new file mode 100644
index 0000000..baff565
Binary files /dev/null and b/lib/cordova-wp8/framework/Images/appbar.feature.video.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/framework/Images/appbar.next.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/framework/Images/appbar.next.rest.png b/lib/cordova-wp8/framework/Images/appbar.next.rest.png
new file mode 100644
index 0000000..ed577d7
Binary files /dev/null and b/lib/cordova-wp8/framework/Images/appbar.next.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/framework/Images/appbar.save.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/framework/Images/appbar.save.rest.png b/lib/cordova-wp8/framework/Images/appbar.save.rest.png
new file mode 100644
index 0000000..d49940e
Binary files /dev/null and b/lib/cordova-wp8/framework/Images/appbar.save.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/framework/Images/appbar.stop.rest.png
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/framework/Images/appbar.stop.rest.png b/lib/cordova-wp8/framework/Images/appbar.stop.rest.png
new file mode 100644
index 0000000..4dd724f
Binary files /dev/null and b/lib/cordova-wp8/framework/Images/appbar.stop.rest.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/framework/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/framework/Properties/AssemblyInfo.cs b/lib/cordova-wp8/framework/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..4ed7003
--- /dev/null
+++ b/lib/cordova-wp8/framework/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("WPCordovaClassLib")]
+[assembly: AssemblyDescription("2.7.0")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Apache Cordova")]
+[assembly: AssemblyProduct("WPCordovaClassLib")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("633ee7ad-9a75-4b68-96e9-281528c50275")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("2.7.0.0")]
+[assembly: AssemblyFileVersion("2.7.0.0")]

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/framework/WPCordovaClassLib.csproj
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/framework/WPCordovaClassLib.csproj b/lib/cordova-wp8/framework/WPCordovaClassLib.csproj
new file mode 100644
index 0000000..55da878
--- /dev/null
+++ b/lib/cordova-wp8/framework/WPCordovaClassLib.csproj
@@ -0,0 +1,313 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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. 
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>10.0.20506</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{FC6A1A70-892D-46AD-9E4A-9793F72AF780}</ProjectGuid>
+    <ProjectTypeGuids>{C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>WPCordovaClassLib</RootNamespace>
+    <AssemblyName>WPCordovaClassLib</AssemblyName>
+    <AssemblyVersion>$(BaseVersionNumber)</AssemblyVersion>
+    <AssemblyFileVersion>$(BaseVersionNumber)</AssemblyFileVersion>
+    <AssemblyDescription>$(VersionNumber)</AssemblyDescription>
+    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
+    <SilverlightVersion>
+    </SilverlightVersion>
+    <TargetFrameworkProfile>
+    </TargetFrameworkProfile>
+    <TargetFrameworkIdentifier>WindowsPhone</TargetFrameworkIdentifier>
+    <SilverlightApplication>false</SilverlightApplication>
+    <ValidateXaml>true</ValidateXaml>
+    <ThrowErrorsInValidation>true</ThrowErrorsInValidation>
+    <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>Bin\Debug</OutputPath>
+    <DefineConstants>TRACE;DEBUG;SILVERLIGHT;WINDOWS_PHONE;CORDOVA_CLASSLIB</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>Bin\Release</OutputPath>
+    <DefineConstants>TRACE;DEBUG;SILVERLIGHT;WINDOWS_PHONE;CORDOVA_CLASSLIB</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <DocumentationFile>
+    </DocumentationFile>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>Bin\x86\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>full</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+    <Optimize>false</Optimize>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <OutputPath>Bin\x86\Release</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <Optimize>true</Optimize>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>Bin\ARM\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>full</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+    <Optimize>false</Optimize>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
+    <OutputPath>Bin\ARM\Release</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <Optimize>true</Optimize>
+    <NoStdLib>true</NoStdLib>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>
+    </PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="..\templates\standalone\cordovalib\BrowserMouseHelper.cs">
+      <Link>CordovaLib\BrowserMouseHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\CommandFactory.cs">
+      <Link>CordovaLib\CommandFactory.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\Commands\BaseCommand.cs">
+      <Link>CordovaLib\Commands\BaseCommand.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\ConfigHandler.cs">
+      <Link>CordovaLib\ConfigHandler.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\CordovaCommandCall.cs">
+      <Link>CordovaLib\CordovaCommandCall.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\CordovaView.xaml.cs">
+      <Link>CordovaLib\CordovaView.xaml.cs</Link>
+      <DependentUpon>CordovaView.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\DOMStorageHelper.cs">
+      <Link>CordovaLib\DOMStorageHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\JSON\JsonHelper.cs">
+      <Link>CordovaLib\JSON\JsonHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\NativeExecution.cs">
+      <Link>CordovaLib\NativeExecution.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\OrientationHelper.cs">
+      <Link>CordovaLib\OrientationHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\PluginResult.cs">
+      <Link>CordovaLib\PluginResult.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\cordovalib\ScriptCallback.cs">
+      <Link>CordovaLib\ScriptCallback.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Accelerometer.cs">
+      <Link>CordovaLib\Plugins\Accelerometer.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\AudioFormatsHelper.cs">
+      <Link>CordovaLib\Plugins\AudioFormatsHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\AudioPlayer.cs">
+      <Link>CordovaLib\Plugins\AudioPlayer.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Battery.cs">
+      <Link>CordovaLib\Plugins\Battery.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Camera.cs">
+      <Link>CordovaLib\Plugins\Camera.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Capture.cs">
+      <Link>CordovaLib\Plugins\Capture.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Compass.cs">
+      <Link>CordovaLib\Plugins\Compass.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Contacts.cs">
+      <Link>CordovaLib\Plugins\Contacts.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\DebugConsole.cs">
+      <Link>CordovaLib\Plugins\DebugConsole.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Device.cs">
+      <Link>CordovaLib\Plugins\Device.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\File.cs">
+      <Link>CordovaLib\Plugins\File.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\FileTransfer.cs">
+      <Link>CordovaLib\Plugins\FileTransfer.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\GeoLocation.cs">
+      <Link>CordovaLib\Plugins\GeoLocation.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Globalization.cs">
+      <Link>CordovaLib\Plugins\Globalization.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\ImageExifHelper.cs">
+      <Link>CordovaLib\Plugins\ImageExifHelper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\InAppBrowser.cs">
+      <Link>CordovaLib\Plugins\InAppBrowser.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Media.cs">
+      <Link>CordovaLib\Plugins\Media.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\MimeTypeMapper.cs">
+      <Link>CordovaLib\Plugins\MimeTypeMapper.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\NetworkStatus.cs">
+      <Link>CordovaLib\Plugins\NetworkStatus.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\Notification.cs">
+      <Link>CordovaLib\Plugins\Notification.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\AudioCaptureTask.cs">
+      <Link>CordovaLib\Plugins\UI\AudioCaptureTask.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\AudioRecorder.xaml.cs">
+      <Link>CordovaLib\Plugins\UI\AudioRecorder.xaml.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\ImageCapture.xaml.cs">
+      <Link>CordovaLib\Plugins\UI\ImageCapture.xaml.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\NotificationBox.xaml.cs">
+      <Link>CordovaLib\Plugins\UI\NotificationBox.xaml.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\VideoCaptureTask.cs">
+      <Link>CordovaLib\Plugins\UI\VideoCaptureTask.cs</Link>
+    </Compile>
+    <Compile Include="..\templates\standalone\Plugins\UI\VideoRecorder.xaml.cs">
+      <Link>CordovaLib\Plugins\UI\VideoRecorder.xaml.cs</Link>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="..\templates\standalone\cordovalib\resources\notification-beep.wav">
+      <Link>CordovaLib\resources\notification-beep.wav</Link>
+    </Content>
+    <Content Include="Images\appbar.back.rest.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Images\appbar.close.rest.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Images\appbar.feature.video.rest.png">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Images\appbar.next.rest.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Images\appbar.stop.rest.png">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Images\appbar.save.rest.png">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Resource Include="resources\notification-beep.wav" />
+  </ItemGroup>
+  <ItemGroup>
+    <Page Include="..\templates\standalone\cordovalib\CordovaView.xaml">
+      <Link>CordovaLib\CordovaView.xaml</Link>
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="..\templates\standalone\Plugins\UI\AudioRecorder.xaml">
+      <Link>CordovaLib\Plugins\UI\AudioRecorder.xaml</Link>
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="..\templates\standalone\Plugins\UI\ImageCapture.xaml">
+      <Link>CordovaLib\Plugins\UI\ImageCapture.xaml</Link>
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="..\templates\standalone\Plugins\UI\NotificationBox.xaml">
+      <Link>CordovaLib\Plugins\UI\NotificationBox.xaml</Link>
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="..\templates\standalone\Plugins\UI\VideoRecorder.xaml">
+      <Link>CordovaLib\Plugins\UI\VideoRecorder.xaml</Link>
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).$(TargetFrameworkVersion).Overrides.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).CSharp.targets" />
+  <ProjectExtensions />
+  <PropertyGroup>
+    <PreBuildEvent>
+    </PreBuildEvent>
+  </PropertyGroup>
+  <PropertyGroup>
+    <PostBuildEvent>
+    </PostBuildEvent>
+  </PropertyGroup>
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file


[41/50] git commit: Version 2.7.3

Posted by br...@apache.org.
Version 2.7.3


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

Branch: refs/heads/master2
Commit: 37583c263c2a75f78da82654ebe7d7f4ef497907
Parents: ea1cf87
Author: Michael Brooks <mi...@michaelbrooks.ca>
Authored: Wed May 15 13:53:41 2013 -0700
Committer: Ian Clelland <ic...@chromium.org>
Committed: Thu May 23 16:04:02 2013 -0400

----------------------------------------------------------------------
 package.json |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37583c26/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index b706c93..e62415e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "cordova",
-  "version": "2.7.2",
+  "version": "2.7.3",
   "preferGlobal": "true",
   "description": "Cordova command line interface tool",
   "main": "cordova",


[39/50] git commit: Add Windows support to Android platform-scripts.

Posted by br...@apache.org.
Add Windows support to Android platform-scripts.


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

Branch: refs/heads/master2
Commit: ea1cf879a18db03f7b74e4b7c29538f2dd87910e
Parents: 0fd1903
Author: Benn Mapes <be...@gmail.com>
Authored: Tue May 14 19:23:16 2013 -0700
Committer: Ian Clelland <ic...@chromium.org>
Committed: Thu May 23 16:04:02 2013 -0400

----------------------------------------------------------------------
 lib/cordova-android/bin/check_reqs                 |   34 +
 lib/cordova-android/bin/check_reqs.bat             |    9 +
 lib/cordova-android/bin/check_reqs.js              |   81 +
 lib/cordova-android/bin/create                     |   30 +-
 lib/cordova-android/bin/create.bat                 |   34 +-
 lib/cordova-android/bin/create.js                  |  125 +-
 lib/cordova-android/bin/templates/cordova/build    |    5 +-
 .../bin/templates/cordova/build.bat                |   20 +-
 lib/cordova-android/bin/templates/cordova/clean    |    5 +-
 .../bin/templates/cordova/clean.bat                |   20 +-
 lib/cordova-android/bin/templates/cordova/cordova  |  159 -
 .../bin/templates/cordova/cordova.bat              |    7 +-
 .../bin/templates/cordova/cordova.js               |  137 -
 .../bin/templates/cordova/lib/cordova              |  386 +
 .../bin/templates/cordova/lib/cordova.js           |  593 ++
 .../bin/templates/cordova/lib/install-device       |   23 +
 .../bin/templates/cordova/lib/install-device.bat   |    9 +
 .../bin/templates/cordova/lib/install-emulator     |   23 +
 .../bin/templates/cordova/lib/install-emulator.bat |    9 +
 .../bin/templates/cordova/lib/list-devices         |   23 +
 .../bin/templates/cordova/lib/list-devices.bat     |    9 +
 .../bin/templates/cordova/lib/list-emulator-images |   23 +
 .../templates/cordova/lib/list-emulator-images.bat |    9 +
 .../templates/cordova/lib/list-started-emulators   |   23 +
 .../cordova/lib/list-started-emulators.bat         |    9 +
 .../bin/templates/cordova/lib/start-emulator       |   23 +
 .../bin/templates/cordova/lib/start-emulator.bat   |    9 +
 lib/cordova-android/bin/templates/cordova/log      |    5 +-
 lib/cordova-android/bin/templates/cordova/log.bat  |   20 +-
 lib/cordova-android/bin/templates/cordova/release  |   24 -
 lib/cordova-android/bin/templates/cordova/run      |    5 +-
 lib/cordova-android/bin/templates/cordova/run.bat  |    3 +-
 lib/cordova-android/bin/templates/cordova/version  |   32 +
 .../framework/assets/js/cordova.android.js         | 6836 ---------------
 .../framework/assets/www/cordova.js                | 6836 +++++++++++++++
 lib/cordova-android/framework/build.xml            |   35 +-
 src/metadata/android_parser.js                     |    2 +-
 37 files changed, 8301 insertions(+), 7334 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/check_reqs
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/check_reqs b/lib/cordova-android/bin/check_reqs
new file mode 100755
index 0000000..0032778
--- /dev/null
+++ b/lib/cordova-android/bin/check_reqs
@@ -0,0 +1,34 @@
+#! /bin/bash
+#       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.
+#
+ROOT="$( cd "$( dirname "$0" )/.." && pwd )"
+cmd=`android list target`
+if [[ $? != 0 ]]; then
+    echo "The command `android` failed. Make sure you have the latest Android SDK installed, and the `android` command (inside the tools/ folder) added to your path."
+    exit 2
+elif [[ ! $cmd =~ "android-17" ]]; then
+    echo "Please install Android target 17 (the Android 4.2 SDK). Make sure you have the latest Android tools installed as well. Run `android` from your command-line to install/update any missing SDKs or tools."
+    exit 2
+else
+    cmd="android update project -p $ROOT -t android-17 1> /dev/null 2>&1"
+    eval $cmd
+    if [[ $? != 0 ]]; then
+        echo "Error updating the Cordova library to work with your Android environment."
+        exit 2
+    fi
+fi
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/check_reqs.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/check_reqs.bat b/lib/cordova-android/bin/check_reqs.bat
new file mode 100644
index 0000000..65514c8
--- /dev/null
+++ b/lib/cordova-android/bin/check_reqs.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%check_reqs.js (
+        cscript "%full_path%check_reqs.js" //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'check_reqs.js' in 'bin' folder, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/check_reqs.js
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/check_reqs.js b/lib/cordova-android/bin/check_reqs.js
new file mode 100644
index 0000000..ef30991
--- /dev/null
+++ b/lib/cordova-android/bin/check_reqs.js
@@ -0,0 +1,81 @@
+// 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 ROOT  = WScript.ScriptFullName.split('\\bin\\check_reqs.js').join(''),
+    shell = WScript.CreateObject("WScript.Shell"),
+    fso   = WScript.CreateObject('Scripting.FileSystemObject');
+
+
+// executes a command in the shell, returns stdout or stderr if error
+function exec_out(command) {
+    var oExec=shell.Exec(command);
+    var output = new String();
+    while (oExec.Status == 0) {
+        if (!oExec.StdOut.AtEndOfStream) {
+            var line = oExec.StdOut.ReadAll();
+            // XXX: Change to verbose mode 
+            // WScript.StdOut.WriteLine(line);
+            output += line;
+        }
+        WScript.sleep(100);
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oExec.StdErr.AtEndOfStream) {
+        var line = oExec.StdErr.ReadAll();
+        return {'error' : true, 'output' : line};
+    } else if (!oExec.StdOut.AtEndOfStream) {
+            var line = oExec.StdOut.ReadAll();
+            // XXX: Change to verbose mode 
+            // WScript.StdOut.WriteLine(line);
+            output += line;
+    }
+    return {'error' : false, 'output' : output};
+}
+
+// log to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+} 
+
+// checks that android requirements are met
+function check_requirements() {
+    var result = exec_out('%comspec% /c android list target');
+    if(result.error) {
+        Log('The command `android` failed. Make sure you have the latest Android SDK installed, and the `android` command (inside the tools/ folder) added to your path. Output: ' + result.output, true);
+        WScript.Quit(2);
+    }
+    else if(!result.output.match(/android[-]17/)) {
+        Log('Please install Android target 17 (the Android 4.2 SDK). Make sure you have the latest Android tools installed as well. Run `android` from your command-line to install/update any missing SDKs or tools.', true);
+        Log('Output : ' + result.output);
+        WScript.Quit(2);
+    }
+    else {
+        var cmd = '%comspec% /c android update project -p ' + ROOT + '\\framework -t android-17';
+        result = exec_out(cmd);
+        if(result.error) {
+            Log('Error updating the Cordova library to work with your Android environment. Command run: "' + cmd + '", output: ' + result.output, true);
+            WScript.Quit(2);  
+        }
+    }
+}
+
+check_requirements();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/create
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/create b/lib/cordova-android/bin/create
index 35d3363..39aff6b 100755
--- a/lib/cordova-android/bin/create
+++ b/lib/cordova-android/bin/create
@@ -47,20 +47,6 @@ then
     exit 1
 fi
 
-# cleanup after exit and/or on error
-function on_exit {
-    # [ -f "$BUILD_PATH"/framework/libs/commons-codec-1.6.jar ] && rm "$BUILD_PATH"/framework/libs/commons-codec-1.6.jar
-    # [ -d "$BUILD_PATH"/framework/libs ] && rmdir "$BUILD_PATH"/framework/libs
-    if [ -f "$BUILD_PATH"/framework/assets/www/cordova-$VERSION.js ]
-    then
-        rm "$BUILD_PATH"/framework/assets/www/cordova-$VERSION.js
-    fi
-    if [ -f "$BUILD_PATH"/framework/cordova-$VERSION.jar ]
-    then
-        rm "$BUILD_PATH"/framework/cordova-$VERSION.jar
-    fi
-}
-
 function createAppInfoJar {
     pushd "$BUILD_PATH"/bin/templates/cordova/ApplicationInfo > /dev/null
     javac ApplicationInfo.java
@@ -91,7 +77,6 @@ function replace {
 # we do not want the script to silently fail
 trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG
 trap on_error ERR
-trap on_exit EXIT
 
 ANDROID_BIN="${ANDROID_BIN:=$( which android )}"
 PACKAGE_AS_PATH=$(echo $PACKAGE | sed 's/\./\//g')
@@ -141,11 +126,11 @@ cp -r "$BUILD_PATH"/bin/templates/project/res "$PROJECT_PATH"
 if [ -d "$BUILD_PATH"/framework ]
 then
     cp -r "$BUILD_PATH"/framework/res/xml "$PROJECT_PATH"/res
-    cp "$BUILD_PATH"/framework/assets/www/cordova-$VERSION.js "$PROJECT_PATH"/assets/www/cordova-$VERSION.js
+    cp "$BUILD_PATH"/framework/assets/www/cordova.js "$PROJECT_PATH"/assets/www/cordova.js
     cp "$BUILD_PATH"/framework/cordova-$VERSION.jar "$PROJECT_PATH"/libs/cordova-$VERSION.jar
 else
     cp -r "$BUILD_PATH"/xml "$PROJECT_PATH"/res/xml
-    cp "$BUILD_PATH"/cordova-$VERSION.js "$PROJECT_PATH"/assets/www/cordova-$VERSION.js
+    cp "$BUILD_PATH"/cordova.js "$PROJECT_PATH"/assets/www/cordova.js
     cp "$BUILD_PATH"/cordova-$VERSION.jar "$PROJECT_PATH"/libs/cordova-$VERSION.jar
 fi
 
@@ -161,11 +146,18 @@ replace "s/__APILEVEL__/${API_LEVEL}/g" "$MANIFEST_PATH"
 
 # creating cordova folder and copying run/build/log/launch scripts
 mkdir "$PROJECT_PATH"/cordova
+mkdir "$PROJECT_PATH"/cordova/lib
 createAppInfoJar
 cp "$BUILD_PATH"/bin/templates/cordova/appinfo.jar "$PROJECT_PATH"/cordova/appinfo.jar
-cp "$BUILD_PATH"/bin/templates/cordova/cordova "$PROJECT_PATH"/cordova/cordova
 cp "$BUILD_PATH"/bin/templates/cordova/build "$PROJECT_PATH"/cordova/build
-cp "$BUILD_PATH"/bin/templates/cordova/release "$PROJECT_PATH"/cordova/release
 cp "$BUILD_PATH"/bin/templates/cordova/clean "$PROJECT_PATH"/cordova/clean
 cp "$BUILD_PATH"/bin/templates/cordova/log "$PROJECT_PATH"/cordova/log
 cp "$BUILD_PATH"/bin/templates/cordova/run "$PROJECT_PATH"/cordova/run
+cp "$BUILD_PATH"/bin/templates/cordova/lib/cordova "$PROJECT_PATH"/cordova/lib/cordova
+cp "$BUILD_PATH"/bin/templates/cordova/lib/install-device "$PROJECT_PATH"/cordova/lib/install-device
+cp "$BUILD_PATH"/bin/templates/cordova/lib/install-emulator "$PROJECT_PATH"/cordova/lib/install-emulator
+cp "$BUILD_PATH"/bin/templates/cordova/lib/list-devices "$PROJECT_PATH"/cordova/lib/list-devices
+cp "$BUILD_PATH"/bin/templates/cordova/lib/list-emulator-images "$PROJECT_PATH"/cordova/lib/list-emulator-images
+cp "$BUILD_PATH"/bin/templates/cordova/lib/list-started-emulators "$PROJECT_PATH"/cordova/lib/list-started-emulators
+cp "$BUILD_PATH"/bin/templates/cordova/lib/start-emulator "$PROJECT_PATH"/cordova/lib/start-emulator
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/create.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/create.bat b/lib/cordova-android/bin/create.bat
index cdbd611..7f0346f 100644
--- a/lib/cordova-android/bin/create.bat
+++ b/lib/cordova-android/bin/create.bat
@@ -1,3 +1,5 @@
+@ECHO OFF
+GOTO BEGIN
 :: 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
@@ -15,23 +17,23 @@
 :: specific language governing permissions and limitations
 :: under the License.
 
-@ECHO OFF
-IF NOT DEFINED JAVA_HOME GOTO MISSING_JAVA_HOME
+:BEGIN
+        IF NOT DEFINED JAVA_HOME GOTO MISSING_JAVA_HOME
 
-FOR %%X in (java.exe javac.exe ant.bat android.bat) do (
-    IF [%%~$PATH:X]==[] (
-      ECHO Cannot locate %%X using the PATH environment variable.
-      ECHO Retry after adding directory containing %%X to the PATH variable.
-      ECHO Remember to open a new command window after updating the PATH variable.
-      IF "%%X"=="java.exe" GOTO GET_JAVA
-      IF "%%X"=="javac.exe" GOTO GET_JAVA
-      IF "%%X"=="ant.bat" GOTO GET_ANT
-      IF "%%X"=="android.bat" GOTO GET_ANDROID
-      GOTO ERROR
-    )
-)
-cscript "%~dp0\create.js" %*
-GOTO END
+        FOR %%X in (java.exe javac.exe ant.bat android.bat) do (
+            IF [%%~$PATH:X]==[] (
+              ECHO Cannot locate %%X using the PATH environment variable.
+              ECHO Retry after adding directory containing %%X to the PATH variable.
+              ECHO Remember to open a new command window after updating the PATH variable.
+              IF "%%X"=="java.exe" GOTO GET_JAVA
+              IF "%%X"=="javac.exe" GOTO GET_JAVA
+              IF "%%X"=="ant.bat" GOTO GET_ANT
+              IF "%%X"=="android.bat" GOTO GET_ANDROID
+              GOTO ERROR
+            )
+        )
+        cscript "%~dp0\create.js" %* //nologo
+        GOTO END
 :MISSING_JAVA_HOME
         ECHO The JAVA_HOME environment variable is not set.
         ECHO Set JAVA_HOME to an existing JRE directory.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/create.js
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/create.js b/lib/cordova-android/bin/create.js
index d0d9999..b1de5fe 100644
--- a/lib/cordova-android/bin/create.js
+++ b/lib/cordova-android/bin/create.js
@@ -24,7 +24,30 @@
  *  ./create [path package activity]
  */
 
-var fso = WScript.CreateObject('Scripting.FileSystemObject');
+var args = WScript.Arguments, PROJECT_PATH="example", 
+    PACKAGE="org.apache.cordova.example", ACTIVITY="cordovaExample",
+    shell=WScript.CreateObject("WScript.Shell"),
+    fso = WScript.CreateObject('Scripting.FileSystemObject');
+
+function Usage() {
+    Log("Usage: create PathTONewProject [ PackageName AppName ]");
+    Log("    PathTONewProject : The path to where you wish to create the project");
+    Log("    PackageName      : The package for the project (default is org.apache.cordova.example)")
+    Log("    AppName          : The name of the application/activity (default is cordovaExample)");
+    Log("examples:");
+    Log("    create C:\\Users\\anonymous\\Desktop\\MyProject");
+    Log("    create C:\\Users\\anonymous\\Desktop\\MyProject io.Cordova.Example AnApp");
+}
+
+// logs messaged to stdout and stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
 
 function read(filename) {
     var fso=WScript.CreateObject("Scripting.FileSystemObject");
@@ -36,7 +59,7 @@ function read(filename) {
 
 function checkTargets(targets) {
     if(!targets) {
-        WScript.Echo("You do not have any android targets setup. Please create at least one target with the `android` command");
+        Log("You do not have any android targets setup. Please create at least one target with the `android` command", true);
         WScript.Quit(69);
     }
 }
@@ -74,7 +97,7 @@ function exec(command) {
 
 function createAppInfoJar() {
     if(!fso.FileExists(ROOT+"\\bin\\templates\\cordova\\appinfo.jar")) {
-        WScript.Echo("Creating appinfo.jar...");
+        Log("Creating appinfo.jar...");
         var cur = shell.CurrentDirectory;
         shell.CurrentDirectory = ROOT+"\\bin\\templates\\cordova\\ApplicationInfo";
         exec("javac ApplicationInfo.java");
@@ -120,7 +143,7 @@ function downloadCommonsCodec() {
           stream.SaveToFile(savePath);
           stream.Close();
         } else {
-          WScript.Echo('Could not retrieve the commons-codec. Please download it yourself and put into the framework/libs directory. This process may fail now. Sorry.');
+          Log('Could not retrieve the commons-codec. Please download it yourself and put into the framework/libs directory. This process may fail now. Sorry.');
         }
       }
       var app = WScript.CreateObject('Shell.Application');
@@ -136,84 +159,104 @@ function downloadCommonsCodec() {
       fso.DeleteFolder(ROOT + '\\framework\\libs\\commons-codec-1.7', true);
     }
 }
-
-var args = WScript.Arguments, PROJECT_PATH="example", 
-    PACKAGE="org.apache.cordova.example", ACTIVITY="cordovaExample",
-    shell=WScript.CreateObject("WScript.Shell");
     
 // working dir
 var ROOT = WScript.ScriptFullName.split('\\bin\\create.js').join('');
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help" || args(0) == "-h") {
+        Usage();
+        WScript.Quit(2);
+    }
 
-if (args.Count() == 3) {
     PROJECT_PATH=args(0);
-    PACKAGE=args(1);
-    ACTIVITY=args(2);
+    if (args.Count() > 1) {
+        PACKAGE = args(1);
+    }
+    if (args.Count() > 2) {
+        ACTIVITY = args(2);
+    }
+}
+else {
+    Log("Error : No project path provided.");
+    Usage();
+    WScript.Quit(2);
 }
 
 if(fso.FolderExists(PROJECT_PATH)) {
-    WScript.Echo("Project already exists!");
-    WScript.Quit(1);
+    Log("Project path already exists!", true);
+    WScript.Quit(2);
 }
 
 var PACKAGE_AS_PATH=PACKAGE.replace(/\./g, '\\');
-var ACTIVITY_PATH=PROJECT_PATH+'\\src\\'+PACKAGE_AS_PATH+'\\'+ACTIVITY+'.java';
+var ACTIVITY_DIR=PROJECT_PATH + '\\src\\' + PACKAGE_AS_PATH;
+var ACTIVITY_PATH=ACTIVITY_DIR+'\\'+ACTIVITY+'.java';
 var MANIFEST_PATH=PROJECT_PATH+'\\AndroidManifest.xml';
 var TARGET=setTarget();
 var API_LEVEL=setApiLevel();
 var VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
 // create the project
-WScript.Echo("Creating new android project...");
-exec('android.bat create project --target '+TARGET+' --path '+PROJECT_PATH+' --package '+PACKAGE+' --activity '+ACTIVITY);
+Log("Creating new android project...");
+exec('android.bat create project --target "'+TARGET+'" --path "'+PROJECT_PATH+'" --package "'+PACKAGE+'" --activity "'+ACTIVITY+'"');
 
 // build from source. distro should have these files
 if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
-    !fso.FileExists(ROOT+'\\cordova-'+VERSION+'.js')) {
-    WScript.Echo("Building jar and js files...");
+    !fso.FileExists(ROOT+'\\cordova.js')) {
+    Log("Building jar and js files...");
     // update the cordova framework project to a target that exists on this machine
-    exec('android.bat update project --target '+TARGET+' --path '+ROOT+'\\framework');
+    exec('android.bat update project --target "'+TARGET+'" --path "'+ROOT+'\\framework"');
     // pull down commons codec if necessary
     downloadCommonsCodec();
-    exec('ant.bat -f \"'+ ROOT +'\\framework\\build.xml\" jar');
+    exec('ant.bat -f "'+ ROOT +'\\framework\\build.xml" jar');
 }
 
 // copy in the project template
-WScript.Echo("Copying template files...");
-exec('%comspec% /c xcopy "'+ ROOT + '"\\bin\\templates\\project\\res '+PROJECT_PATH+'\\res\\ /E /Y');
-exec('%comspec% /c xcopy "'+ ROOT + '"\\bin\\templates\\project\\assets '+PROJECT_PATH+'\\assets\\ /E /Y');
-exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\project\\AndroidManifest.xml ' + PROJECT_PATH + '\\AndroidManifest.xml /Y');
-exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\project\\Activity.java '+ ACTIVITY_PATH +' /Y');
+Log("Copying template files...");
+exec('%comspec% /c xcopy "'+ ROOT + '\\bin\\templates\\project\\res" "'+PROJECT_PATH+'\\res\\" /E /Y');
+exec('%comspec% /c xcopy "'+ ROOT + '\\bin\\templates\\project\\assets" "'+PROJECT_PATH+'\\assets\\" /E /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\project\\AndroidManifest.xml" "' + PROJECT_PATH + '\\AndroidManifest.xml" /Y');
+exec('%comspec% /c mkdir "' + ACTIVITY_DIR + '"');
+exec('%comspec% /c copy "' + ROOT + '"\\bin\\templates\\project\\Activity.java "' + ACTIVITY_PATH + '" /Y');
 
 // check if we have the source or the distro files
-WScript.Echo("Copying js, jar & config.xml files...");
+Log("Copying js, jar & config.xml files...");
 if(fso.FolderExists(ROOT + '\\framework')) {
-    exec('%comspec% /c copy "'+ROOT+'"\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
-    exec('%comspec% /c copy "'+ROOT+'"\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
+    exec('%comspec% /c copy "'+ROOT+'\\framework\\assets\\www\\cordova.js" "'+PROJECT_PATH+'\\assets\\www\\cordova.js" /Y');
+    exec('%comspec% /c copy "'+ROOT+'\\framework\\cordova-'+VERSION+'.jar" "'+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar" /Y');
     fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
-    exec('%comspec% /c copy "'+ROOT+'"\\framework\\res\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
+    exec('%comspec% /c copy "'+ROOT+'\\framework\\res\\xml\\config.xml" "' + PROJECT_PATH + '\\res\\xml\\config.xml" /Y');
 } else {
     // copy in cordova.js
-    exec('%comspec% /c copy "'+ROOT+'"\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
+    exec('%comspec% /c copy "'+ROOT+'\\cordova.js" "'+PROJECT_PATH+'\\assets\\www\\cordova.js" /Y');
     // copy in cordova.jar
-    exec('%comspec% /c copy "'+ROOT+'"\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
+    exec('%comspec% /c copy "'+ROOT+'\\cordova-'+VERSION+'.jar" "'+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar" /Y');
     // copy in xml
     fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
-    exec('%comspec% /c copy "'+ROOT+'"\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
+    exec('%comspec% /c copy "'+ROOT+'\\xml\\config.xml" "' + PROJECT_PATH + '\\res\\xml\\config.xml" /Y');
 }
 
 // copy cordova scripts
 fso.CreateFolder(PROJECT_PATH + '\\cordova');
+fso.CreateFolder(PROJECT_PATH + '\\cordova\\lib');
 createAppInfoJar();
-WScript.Echo("Copying cordova command tools...");
-exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\appinfo.jar ' + PROJECT_PATH + '\\cordova\\appinfo.jar /Y');
-exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /Y');
-exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.bat ' + PROJECT_PATH + '\\cordova\\cordova.bat /Y');
-exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\clean.bat ' + PROJECT_PATH + '\\cordova\\clean.bat /Y');
-exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\build.bat ' + PROJECT_PATH + '\\cordova\\build.bat /Y');
-exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\log.bat ' + PROJECT_PATH + '\\cordova\\log.bat /Y');
-exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\run.bat ' + PROJECT_PATH + '\\cordova\\run.bat /Y');
+Log("Copying cordova command tools...");
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\appinfo.jar" "' + PROJECT_PATH + '\\cordova\\appinfo.jar" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\cordova.js" "' + PROJECT_PATH + '\\cordova\\lib\\cordova.js" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\install-device.bat" "' + PROJECT_PATH + '\\cordova\\lib\\install-device.bat" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\install-emulator.bat" "' + PROJECT_PATH + '\\cordova\\lib\\install-emulator.bat" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-emulator-images.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-emulator-images.bat" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-devices.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-devices.bat" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\list-started-emulators.bat" "' + PROJECT_PATH + '\\cordova\\lib\\list-started-emulators.bat" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\lib\\start-emulator.bat" "' + PROJECT_PATH + '\\cordova\\lib\\start-emulator.bat" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\cordova.bat" "' + PROJECT_PATH + '\\cordova\\cordova.bat" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\clean.bat" "' + PROJECT_PATH + '\\cordova\\clean.bat" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\build.bat" "' + PROJECT_PATH + '\\cordova\\build.bat" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\log.bat" "' + PROJECT_PATH + '\\cordova\\log.bat" /Y');
+exec('%comspec% /c copy "'+ROOT+'\\bin\\templates\\cordova\\run.bat" "' + PROJECT_PATH + '\\cordova\\run.bat" /Y');
 
 // interpolate the activity name and package
-WScript.Echo("Updating AndroidManifest.xml and Main Activity...");
+Log("Updating AndroidManifest.xml and Main Activity...");
 replaceInFile(ACTIVITY_PATH, /__ACTIVITY__/, ACTIVITY);
 replaceInFile(ACTIVITY_PATH, /__ID__/, PACKAGE);
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/build
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/build b/lib/cordova-android/bin/templates/cordova/build
index e586e4d..3cbd9c1 100755
--- a/lib/cordova-android/bin/templates/cordova/build
+++ b/lib/cordova-android/bin/templates/cordova/build
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
 CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova build
+bash "$CORDOVA_PATH"/lib/cordova build "$@"

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/build.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/build.bat b/lib/cordova-android/bin/templates/cordova/build.bat
index 8e6ca9a..7aa7c75 100644
--- a/lib/cordova-android/bin/templates/cordova/build.bat
+++ b/lib/cordova-android/bin/templates/cordova/build.bat
@@ -1,18 +1,2 @@
-:: 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.
-
-%~dp0\cordova.bat build
+@ECHO OFF
+%~dp0\cordova.bat build %*
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/clean
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/clean b/lib/cordova-android/bin/templates/cordova/clean
index 53b7f9a..f52966a 100755
--- a/lib/cordova-android/bin/templates/cordova/clean
+++ b/lib/cordova-android/bin/templates/cordova/clean
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
 CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova clean
+bash "$CORDOVA_PATH"/lib/cordova clean "$@"

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/clean.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/clean.bat b/lib/cordova-android/bin/templates/cordova/clean.bat
index fe5c09f..b41bdc9 100644
--- a/lib/cordova-android/bin/templates/cordova/clean.bat
+++ b/lib/cordova-android/bin/templates/cordova/clean.bat
@@ -1,18 +1,2 @@
-:: 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.
-
-%~dp0\cordova.bat clean
+@ECHO OFF
+%~dp0\cordova.bat clean %*

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/cordova
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/cordova b/lib/cordova-android/bin/templates/cordova/cordova
deleted file mode 100755
index 1945a4c..0000000
--- a/lib/cordova-android/bin/templates/cordova/cordova
+++ /dev/null
@@ -1,159 +0,0 @@
-# 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.
-
-#!/bin/bash
-
-
-PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd )
-
-function check_devices {
-# FIXME
-    local devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep device`
-    if [ -z "$devices"  ] ; then
-        echo "1"
-    else
-        echo "0"
-    fi
-}
-
-function emulate {
-    declare -a avd_list=($(android list avd | grep "Name:" | cut -f 2 -d ":" | xargs))
-    # we need to start adb-server
-    adb start-server 1>/dev/null
-
-    # Do not launch an emulator if there is already one running or if a device is attached
-    if [ $(check_devices) == 0 ] ; then
-        return
-    fi
-
-    local avd_id="1000" #FIXME: hopefully user does not have 1000 AVDs
-    # User has no AVDs
-    if [ ${#avd_list[@]} == 0 ]
-    then
-        echo "You don't have any Android Virtual Devices. Please create at least one AVD."
-        echo "android"
-    fi
-    # User has only one AVD
-    if [ ${#avd_list[@]} == 1 ]
-    then
-        emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[0]} 1> /dev/null 2>&1 &
-    # User has more than 1 AVD
-    elif [ ${#avd_list[@]} -gt 1 ]
-    then
-        while [ -z ${avd_list[$avd_id]} ]
-        do
-            echo "Choose from one of the following Android Virtual Devices [0 to $((${#avd_list[@]}-1))]:"
-            for(( i = 0 ; i < ${#avd_list[@]} ; i++ ))
-            do
-                echo "$i) ${avd_list[$i]}"
-            done
-            read -t 5 -p "> " avd_id
-            # default value if input timeout
-            if [ $avd_id -eq 1000 ] ; then avd_id=0 ; fi
-        done
-        emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[$avd_id]} 1> /dev/null 2>&1 &
-    fi
-    
-}
-
-function clean {
-    ant clean
-}
-# has to be used independently and not in conjunction with other commands
-function log {
-    adb logcat
-}
-
-function run {
-    clean && emulate && wait_for_device && install && launch 
-}
-
-function install {
-    
-    declare -a devices=($(adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep device | cut -f 1))
-    local device_id="1000" #FIXME: hopefully user does not have 1000 AVDs
-    
-    if [ ${#devices[@]} == 0 ]
-    then
-        # should not reach here. Emulator should launch or device should be attached
-        echo "Emulator not running or device not attached. Could not install debug package"
-        exit 70
-    fi
-    
-    if [ ${#devices[@]} == 1 ]
-    then
-        export ANDROID_SERIAL=${devices[0]}
-    # User has more than 1 AVD
-    elif [ ${#devices[@]} -gt 1 ]
-    then
-        while [ -z ${devices[$device_id]} ]
-        do
-            echo "Choose from one of the following devices/emulators [0 to $((${#devices[@]}-1))]:"
-            for(( i = 0 ; i < ${#devices[@]} ; i++ ))
-            do
-                echo "$i) ${devices[$i]}"
-            done
-            read -t 5 -p "> " device_id
-            # default value if input timeout
-            if [ $device_id -eq 1000 ] ; then device_id=0 ; fi
-        done
-        export ANDROID_SERIAL=${devices[$device_id]}
-    fi
-
-    ant debug install
-}
-
-function build {
-    ant debug
-}
-
-function release {
-    ant release
-}
-
-function wait_for_device {
-    local i="0"
-    echo -n "Waiting for device..."
-
-    while [ $i -lt 300 ]
-    do
-        if [ $(check_devices) -eq 0 ]
-        then
-            break
-        else
-            sleep 1
-            i=$[i+1]
-            echo -n "."
-        fi
-    done
-    # Device timeout: emulator has not started in time or device not attached
-    if [ $i -eq 300 ]
-    then
-        echo "device timeout!"
-        exit 69
-    else
-        echo "connected!"
-    fi
-}
-
-function launch {
-    local launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
-    adb shell am start -n $launch_str 
-}
-
-# TODO parse arguments
-(cd "$PROJECT_PATH" && $1)

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/cordova.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/cordova.bat b/lib/cordova-android/bin/templates/cordova/cordova.bat
index 22c289a..9b56199 100644
--- a/lib/cordova-android/bin/templates/cordova/cordova.bat
+++ b/lib/cordova-android/bin/templates/cordova/cordova.bat
@@ -1,3 +1,5 @@
+@ECHO OFF
+GOTO BEGIN
 :: 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
@@ -14,14 +16,13 @@
 :: KIND, either express or implied.  See the License for the
 :: specific language governing permissions and limitations
 :: under the License.
-
-@ECHO OFF
+:BEGIN
 IF NOT DEFINED JAVA_HOME GOTO MISSING
 FOR %%X in (java.exe ant.bat android.bat) do (
     SET FOUND=%%~$PATH:X
     IF NOT DEFINED FOUND GOTO MISSING
 )
-cscript %~dp0\cordova.js %*
+cscript %~dp0\lib\cordova.js %* //nologo
 GOTO END
 :MISSING
 ECHO Missing one of the following:

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/cordova.js
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/cordova.js b/lib/cordova-android/bin/templates/cordova/cordova.js
deleted file mode 100644
index 51533cb..0000000
--- a/lib/cordova-android/bin/templates/cordova/cordova.js
+++ /dev/null
@@ -1,137 +0,0 @@
-// 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 ROOT = WScript.ScriptFullName.split('\\cordova\\cordova.js').join(''),
-    shell=WScript.CreateObject("WScript.Shell");
-
-function exec(command) {
-    var oExec=shell.Exec(command);
-    var output = new String();
-    while(oExec.Status == 0) {
-        if(!oExec.StdOut.AtEndOfStream) {
-            var line = oExec.StdOut.ReadLine();
-            // XXX: Change to verbose mode 
-            // WScript.StdOut.WriteLine(line);
-            output += line;
-        }
-        WScript.sleep(100);
-    }
-
-    return output;
-}
-
-function device_running() {
-    var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
-    if(local_devices.match(/\w+\tdevice/)) {
-        WScript.Echo("Yes");
-        return true;
-    }
-    WScript.Echo("No");
-    return false;
-}
-function emulate() {
-    // don't run emulator if a device is plugged in or if emulator is already running
-    if(device_running()) {
-        //WScript.Echo("Device or Emulator already running!");
-        return;
-    }
-    var oExec = shell.Exec("%comspec% /c android.bat list avd");
-    var avd_list = [];
-    var avd_id = -10;
-    while(!oExec.StdOut.AtEndOfStream) {
-        var output = oExec.StdOut.ReadLine();
-        if(output.match(/Name: (.)*/)) {
-            avd_list.push(output.replace(/ *Name:\s/, ""));
-        }
-    }
-    // user has no AVDs
-    if(avd_list.length == 0) {
-        WScript.Echo("You don't have any Android Virtual Devices. Please create at least one AVD.");
-        WScript.Echo("android");
-        WScript.Quit(1);
-    }
-    // user has only one AVD so we launch that one
-    if(avd_list.length == 1) {
-
-        shell.Run("emulator -cpu-delay 0 -no-boot-anim -cache %Temp%\cache -avd "+avd_list[0]);
-    }
-
-    // user has more than one avd so we ask them to choose
-    if(avd_list.length > 1) {
-        while(!avd_list[avd_id]) {
-            WScript.Echo("Choose from one of the following Android Virtual Devices [0 to "+(avd_list.length - 1)+"]:")
-            for(i = 0, j = avd_list.length ; i < j ; i++) {
-                WScript.Echo((i)+") "+avd_list[i]);
-            }
-            WScript.StdOut.Write("> ");
-            avd_id = new Number(WScript.StdIn.ReadLine());
-        }
-
-        shell.Run("emulator -cpu-delay 0 -no-boot-anim -cache %Temp%\\cache -avd "+avd_list[avd_id], 0, false);
-    }
-}
-
-function clean() {
-    WScript.Echo("Cleaning project...");
-    exec("%comspec% /c ant.bat clean -f "+ROOT+"\\build.xml 2>&1");
-}
-
-function build() {
-    WScript.Echo("Building project...");
-    exec("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
-}
-
-function install() {
-    WScript.Echo("Building/Installing project...");
-    exec("%comspec% /c ant.bat debug install -f "+ROOT+"\\build.xml 2>&1");
-}
-
-function log() {
-    shell.Run("%comspec% /c adb logcat");
-}
-
-function launch() {
-    WScript.Echo("Launching app...");
-    var launch_str=exec("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
-    //WScript.Echo(launch_str);
-    exec("%comspec% /c adb shell am start -n "+launch_str+" 2>&1");
-}
-
-function run() {
-    var i=0;
-   clean();
-   emulate();
-   WScript.Stdout.Write('Waiting for device...');
-   while(!device_running() && i < 300) {
-        WScript.Stdout.Write('.');
-        WScript.sleep(1000);
-        i += 1;
-   }
-   if(i == 300) {
-       WScript.Stderr.WriteLine("device/emulator timeout!"); 
-   } else {
-       WScript.Stdout.WriteLine("connected!");
-   }
-   install();
-   launch();
-}
-var args = WScript.Arguments;
-if(args.count() != 1) {
-    WScript.StdErr.Write("An error has occured!\n");
-    WScript.Quit(1);
-}
-eval(args(0)+"()");

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/cordova
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/cordova b/lib/cordova-android/bin/templates/cordova/lib/cordova
new file mode 100644
index 0000000..294df49
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/cordova
@@ -0,0 +1,386 @@
+#!/bin/bash
+#   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.
+
+PROJECT_PATH=$( cd "$( dirname "$0" )/../.." && pwd )
+
+function list_devices {
+    IFS=$'\n'
+    devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep -v 'emulator'`
+    device_list=($devices)
+    if [[ ${#device_list[@]} > 0 ]] ; then
+        for i in ${devices[@]}
+        do
+            # remove space and 'device'
+            echo ${i/[^a-zA-Z0-9._]device/}
+        done
+    else
+        echo "No devices found."
+        exit 2
+    fi
+}
+
+function list_started_emulators {
+    IFS=$'\n'
+    devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
+    emulator_list=($devices)
+    if [[ ${#emulator_list[@]} > 0 ]] ; then
+        for i in ${emulator_list[@]}
+        do
+            # remove space and 'device'
+            echo ${i/[^a-zA-Z0-9._]device/}
+        done
+    else
+        echo "No started emulators found, you can start an emulator by using the command"
+        echo " 'cordova/lib/start-emulator'"
+        exit 2
+    fi
+}
+
+function list_emulator_images {
+    emulator_images=`android list avds | grep "Name:" | cut -f 2 -d ":"`
+    emulator_list=($emulator_images)
+    if [[ ${#emulator_list[@]} > 0 ]] ; then
+        for i in ${emulator_list[@]}
+        do
+            echo ${i/[^a-zA-Z0-9._]/}
+        done
+    else
+        echo "No emulators found, if you would like to create an emulator follow the instructions"
+        echo " provided here : http://developer.android.com/tools/devices/index.html"
+        echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
+        exit 2
+    fi
+}
+
+function start_emulator {
+    emulator_images=`android list avds | grep "Name:" | cut -f 2 -d ":"`
+    # if target emulator is provided
+    if [[ "$#" -eq 1 ]] ; then
+        # check that it exists
+        if [[ $emulator_images =~ $1 ]] ; then
+            #xterm -e emulator -avd $1 &
+            emulator -avd $1 1> /dev/null 2>&1 &
+        else
+            echo "Could not find the provided emulator, make sure the emulator exists"
+            echo " by checking 'cordova/lib/list-emulator-images'"
+            exit 2
+        fi
+    else
+        # start first emulator
+        emulator_list=($emulator_images)
+        if [[ ${#emulator_list[@]} > 0 ]] ; then
+            #xterm -e emulator -avd ${emulator_list[0]} &
+            emulator -avd ${emulator_list[0]/[^a-zA-Z0-9._]/} 1> /dev/null 2>&1 &
+        else
+            echo "No emulators found, if you would like to create an emulator follow the instructions"
+            echo " provided here : http://developer.android.com/tools/devices/index.html"
+            echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
+            exit 2
+        fi
+    fi
+}
+
+function install_device {
+    IFS=$'\n'
+    devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep -v 'emulator'`
+    device_list=($devices)
+    if [[ ${#device_list[@]} > 0 ]] ; then
+        apks=`find $PROJECT_PATH/bin -type f -maxdepth 1 | egrep '\.apk$'`
+        apk_list=($apks)
+        if [[ ${#apk_list[@]} > 0 ]] ; then
+            local target
+            # handle target emulator
+            if [[ "$#" -eq 1 ]] ; then
+                # deploy to given target
+                target=${1/--target=/}
+            else
+                # delete trailing space and 'device' after device ID
+                target=${device_list[0]/[^a-zA-Z0-9._]device/}
+            fi
+            echo "Installing ${apk_list[0]} onto device $target..."
+            adb -s $target install -r ${apk_list[0]};
+            echo "Launching application..."
+            local launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
+            adb -s $target shell am start -W -a android.intent.action.MAIN -n $launch_str
+        else
+            echo "Application package not found, could not install to device"
+            echo " make sure your application is built before deploying."
+            exit 2
+        fi
+    else
+        echo "No devices found to deploy to. Please make sure your device is connected"
+        echo " and you can view it using the 'cordova/lib/list-devices' command."
+        exit 2
+    fi
+}
+
+function install_emulator {
+    IFS=$'\n'
+    # check that there is an emulator to deploy to
+    emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'emulator'`
+    emulator_list=($emulator_string)
+    if [[ ${#emulator_list[@]} > 0 ]] ; then
+        apks=`find $PROJECT_PATH/bin -type f -maxdepth 1 | egrep '\.apk$'`
+        apk_list=($apks)
+        if [[ ${#apk_list[@]} > 0 ]] ; then
+            local target
+            # handle target emulator
+            if [[ "$#" -eq 1 ]] ; then
+                # deploy to given target
+                target=${1/--target=/}
+            else
+                # delete trailing space and 'device' after emulator ID
+                target=${emulator_list[0]/[^a-zA-Z0-9._]device/}
+            fi
+            echo "Installing ${apk_list[0]} onto $target..."
+            adb -s $target install -r ${apk_list[0]};
+            echo "Launching application..."
+            local launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
+            adb -s $target shell am start -W -a android.intent.action.MAIN -n $launch_str
+
+        else
+            echo "Application package not found, could not install to device"
+            echo " make sure your application is built before deploying."
+            exit 2
+        fi
+    else
+        echo "No emulators found to deploy to. Please make sure your emulator is started"
+        echo " and you can view it using the 'cordova/lib/list-started-emulators' command."
+        exit 2
+    fi
+}
+
+# cleans the project
+function clean {
+    echo "Cleaning project..."
+    ant clean
+}
+
+# has to be used independently and not in conjunction with other commands
+function log {
+    # filter out nativeGetEnabledTags spam from latest sdk bug.
+    adb logcat | grep -v nativeGetEnabledTags
+}
+
+
+function build {
+    if [[ "$#" -eq 1 ]] ; then
+        if [[ $1 == "--debug" ]] ; then
+            clean
+            ant debug -f "$PROJECT_PATH"/build.xml
+        elif [[ $1 == "--release" ]] ; then
+            clean
+            ant release -f "$PROJECT_PATH"/build.xml
+        elif [[ $1 == "--nobuild" ]] ; then
+            echo "Skipping build..."
+        else
+            echo "Error : Build command '$1' not recognized."
+            exit 2
+        fi
+    else
+        echo "Warning : [ --debug | --release | --nobuild ] not specified, defaulting to --debug"
+        clean
+        ant debug -f "$PROJECT_PATH"/build.xml
+    fi
+}
+
+
+function wait_for_emulator {
+    emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
+    old_started=($emulator_string)
+    local new_started
+    local new_emulator_name
+    local i="0"
+    echo -n "Waiting for emulator..."
+    while [ $i -lt 300 ]
+    do
+        emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
+        new_started=($emulator_string)
+        if [[ ${#new_started[@]} > ${#old_started[@]} && -z "$new_emulator_name" ]] ; then
+            # get the name of the started emulator
+            local count="0"
+            if [[ ${#old_started[@]} == 0 ]] ; then
+                new_emulator_name=${new_started[$count]/[^a-zA-Z0-9._]device/}
+            else
+                for count in {0...${#old_started[@]}}
+                do
+                    if [[ ! ${new_started[$count]} == ${old_started[$count]} ]] ; then
+                        new_emulator_name=${new_started[$count]/[^a-zA-Z0-9._]device/}
+                    fi
+                done
+                if [[ -z "$new_emulator_name" ]] ; then
+                    count=$[count+1]
+                    new_emulator_name=${new_started[$count]/[^a-zA-Z0-9._]device/}
+                fi
+            fi
+        elif [[ "$new_emulator_name" ]] ; then
+            boot_anim=`adb -s $new_emulator_name shell getprop init.svc.bootanim`
+            if [[ $boot_anim =~ "stopped" ]] ; then
+                break
+            else
+                sleep 1
+                i=$[i+1]
+                echo -n "."
+            fi
+        else
+            sleep 1
+            i=$[i+1]
+            echo -n "."
+        fi
+    done
+    # Device timeout: emulator has not started in time
+    if [ $i -eq 300 ]
+    then
+        echo "emulator timeout!"
+        exit 69
+    else
+        echo "connected!"
+    fi
+}
+
+function run {
+    IFS=$'\n'
+    if [[ "$#" -eq 2 ]] ; then
+        build $2
+        if [[ $1 == "--device" ]] ; then
+            install_device
+        elif [[ $1 == "--emulator" ]] ; then
+            install_emulator
+        elif [[ $1 =~ "--target=" ]]; then
+            install_device $1
+        else
+            echo "Error : '$1' is not recognized as an install option"
+        fi
+    elif [[ "$#" -eq 1 ]] ; then
+        if [[ $1 == "--debug" || $1 == "--release" || $1 == "--nobuild" ]] ; then
+            build $1
+        elif [[ $1 == "--device" ]] ; then
+            install_device
+        elif [[ $1 == "--emulator" ]] ; then
+            install_emulator
+        elif [[ $1 =~ "--target=" ]]; then
+            install_device $1
+        else
+            echo "Error : '$1' is not recognized as an install option"
+        fi
+    else
+        echo "Warning : [ --device | --emulate | --target=<targetID> ] not specified, using defaults."
+        build
+        devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep -v 'emulator'`
+        device_list=($devices)
+        emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
+        emulator_list=($emulator_string)
+        if [[ ${#device_list[@]} > 0 ]] ; then
+            install_device
+        elif [[ ${#emulator_list[@]} > 0 ]] ; then
+            install_emulator
+        else
+            emulator_images=`android list avds | grep "Name:" | cut -f 2 -d ":"`
+            echo $emulator_images
+            emulator_image_list=($emulator_images)
+            if [[ ${#emulator_image_list[@]} > 0 ]] ; then
+                echo "Starting emulator : ${emulator_image_list[0]}" 
+                emulator -avd ${emulator_image_list[0]/[^.\w]/} 1> /dev/null 2>&1 &
+                wait_for_emulator
+                install_emulator
+            else
+                # TODO : look for emulator images and start one if it's availible
+                echo "Error : there are no availible devices or emulators to deploy to."
+                echo " create an emulator or connect your device to run this command."
+                echo "If you would like to create an emulator follow the instructions"
+                echo " provided here : http://developer.android.com/tools/devices/index.html"
+                echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
+                exit 2
+            fi
+        fi
+    fi
+}
+
+# parse command line arguments
+
+if [[ $# > 3 ]] ; then 
+    echo "Error :  too many arguments."
+    exit 2
+elif [[ $# == 3 ]] ; then
+    if [[ $1 == "run" ]] ; then
+        run $2 $3
+    else
+        echo "Error : too many arguments for '$1'"
+        exit 2
+    fi
+elif [[ $# == 2 ]] ; then
+    if [[ $1 == "run" ]] ; then
+        if [[ $2 == "--emulator" || $2 == "--device" || $2 =~ "--target=" ]] ; then
+            run $2 ''
+        elif [[ $2 == "--debug" || $2 == "--release" || $2 == "--nobuild" ]] ; then
+            run '' $2
+        else 
+            echo "Error : '$2' is not recognized as a run option."
+            exit 2
+        fi
+    elif [[ $1 == "build" ]] ; then
+        build $2
+    elif [[ $1 == "start-emulator" ]] ; then
+        start_emulator $2
+    elif [[ $1 == "install-device" ]] ; then
+        if [[ $2 =~ "--target=" ]] ; then
+            install_device $2
+        else
+            echo "Error : '$2' is not recognized as an install option"
+            exit 2
+        fi
+    elif [[ $1 == "install-emulator" ]] ; then
+        if [[ $2 =~ "--target=" ]] ; then
+            install_emulator $2
+        else
+            echo "Error : '$2' is not recognized as an install option"
+            exit 2
+        fi
+    else
+        echo "Error : '$1' is not recognized as an option that takes arguments"
+        exit 2
+    fi
+elif [[ $# == 1 ]] ; then
+    if [[ $1 == "run" ]] ; then
+        run
+    elif [[ $1 == "build" ]]; then
+        build
+    elif [[ $1 == "clean" ]]; then
+        clean
+    elif [[ $1 == "log" ]]; then
+        log
+    elif [[ $1 == "list-devices" ]]; then
+        list_devices
+    elif [[ $1 == "list-emulator-images" ]]; then
+        list_emulator_images
+    elif [[ $1 == "list-started-emulators" ]]; then
+        list_started_emulators
+    elif [[ $1 == "install-device" ]]; then
+        install_device
+    elif [[ $1 == "install-emulator" ]]; then
+        install_emulator
+    elif [[ $1 == "start-emulator" ]]; then
+        start_emulator
+    else
+        echo "Error : '$1' is not recognized as a tooling command."
+        exit 2
+    fi
+else
+    echo "Error : No command recieved, exiting..."
+    exit 2
+fi
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/cordova.js
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/cordova.js b/lib/cordova-android/bin/templates/cordova/lib/cordova.js
new file mode 100644
index 0000000..28f9b3e
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/cordova.js
@@ -0,0 +1,593 @@
+// 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 ROOT  = WScript.ScriptFullName.split('\\cordova\\lib\\cordova.js').join(''),
+    shell = WScript.CreateObject("WScript.Shell"),
+    fso   = WScript.CreateObject('Scripting.FileSystemObject');
+
+
+// log to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+} 
+
+// executes a commmand in the shell, returning stdout
+function exec(command) {
+    var oExec=shell.Exec(command);
+    var output = new String();
+    while (oExec.Status == 0) {
+        if (!oExec.StdOut.AtEndOfStream) {
+            var line = oExec.StdOut.ReadLine();
+            output += line;
+        }
+        WScript.sleep(100);
+    }
+    return output;
+}
+
+// executes a command in the shell, returns stdout or stderr if error
+function exec_out(command) {
+    var oExec=shell.Exec(command);
+    var output = new String();
+    while (oExec.Status == 0) {
+        if (!oExec.StdOut.AtEndOfStream) {
+            var line = oExec.StdOut.ReadLine();
+            // XXX: Change to verbose mode 
+            // WScript.StdOut.WriteLine(line);
+            output += line;
+        }
+        WScript.sleep(100);
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oExec.StdErr.AtEndOfStream) {
+        var line = oExec.StdErr.ReadAll();
+        return {'error' : true, 'output' : line};
+    }
+    return {'error' : false, 'output' : output};
+}
+
+// executes a commmand in the shell and outputs stdout and fails on stderr
+function exec_verbose(command) {
+    //Log("Command: " + command);
+    var oShell=shell.Exec(command);
+    while (oShell.Status == 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if (!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oShell.StdErr.AtEndOfStream) {
+        var line = oShell.StdErr.ReadAll();
+        Log(line, true);
+        WScript.Quit(2);
+    }
+}
+
+function get_devices() {
+    var device_list = []
+    var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
+    if (local_devices.match(/\w+\tdevice/)) {
+        devices = local_devices.split('\r\n');
+        //format (ID DESCRIPTION)
+        for (i in devices) {
+            if (devices[i].match(/\w+\tdevice/) && !devices[i].match(/emulator/)) {
+                device_list.push(devices[i].replace(/\t/, ' '));
+            }
+        }
+    }
+    return device_list
+}
+
+function list_devices() {
+    var devices = get_devices();
+    if (devices.length > 0) {
+        for (i in devices) {
+            Log(devices[i]);
+        }
+    }
+    else {
+        Log('No devices found, if your device is connected and not showing,');
+        Log(' then try and install the drivers for your device.');
+        Log(' http://developer.android.com/tools/extras/oem-usb.html');
+    }
+
+}
+
+function get_emulator_images() {
+    // discription contains all data recieved squashed onto one line
+    var add_description = true;
+    var avd_list = [];
+    var local_emulators = shell.Exec("%comspec% /c android list avds").StdOut.ReadAll();
+    if (local_emulators.match(/Name\:/)) {
+        emulators = local_emulators.split('\n');
+        //format (ID DESCRIPTION)
+        var count = 0;
+        var output = '';
+        for (i in emulators) {
+            if (emulators[i].match(/Name\:/)) {
+                var emulator_name = emulators[i].replace(/\s*Name\:\s/, '') + ' ';
+                if (add_description) {
+                    count = 1;
+                    output += emulator_name
+                }
+                else {
+                    avd_list.push(emulator_name);
+                }
+            }
+            // add description if indicated (all data squeezed onto one line)
+            if (count > 0) {
+                var emulator_description = emulators[i].replace(/\s*/g, '');
+                if (count > 4) {
+                    avd_list.push(output + emulator_description);
+                    count = 0;
+                    output = '';
+                }
+                else {
+                    count++;
+                    output += emulator_description + ' '
+                }
+            }
+        }
+    }
+    return avd_list;
+}
+
+function list_emulator_images() {
+    var images = get_emulator_images();
+    if (images.length > 0) {
+        for(i in images) {
+            Log(images[i]);
+        }
+    }
+    else {
+        Log('No emulators found, if you would like to create an emulator follow the instructions');
+        Log(' provided here : http://developer.android.com/tools/devices/index.html');
+        Log(' Or run \'android create avd --name <name> --target <targetID>\' in on the command line.');
+    }
+}
+
+function get_started_emulators() {
+    var started_emulators = [];
+    var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
+    if (local_devices.match(/emulator/)) {
+        devices = local_devices.split('\r\n');
+        //format (ID DESCRIPTION)
+        for (i in devices) {
+            if (devices[i].match(/\w+\tdevice/) && devices[i].match(/emulator/)) {
+                started_emulators.push(devices[i].replace(/\t/, ' '));
+            }
+        }
+    }
+    return started_emulators
+}
+
+function list_started_emulators() {
+    var images = get_started_emulators();
+    if (images.length > 0) {
+        for(i in images) {
+            Log(images[i]);
+        }
+    }
+    else {
+        Log('No started emulators found, if you would like to start an emulator call \'list-emulator-images\'');
+        Log(' to get the name of an emulator and then start the emulator with \'start-emulator <Name>\'');
+    }
+}
+
+function start_emulator(name) {  
+    var emulators = get_emulator_images();
+    var started_emulators = get_started_emulators();
+    var num_started = started_emulators.length;
+    var emulator_name;
+    var started = false;
+    if (name) {
+        for (i in emulators) {
+            if (emulators[i].substr(0,name.length) == name) {
+                Log("Starting emulator : " + name);
+                shell.Run("%comspec% /c start cmd /c emulator -avd " + name);
+                //shell.Run("%comspec% /c start cmd /c emulator -cpu-delay 0 -no-boot-anim -cache %Temp%\cache -avd " + name);
+                started = true;
+            }
+        }
+    }
+    else {
+        if (emulators.length > 0 && started_emulators < 1) {
+            emulator_name = emulators[0].split(' ', 1)[0];
+            start_emulator(emulator_name);
+            return;
+        } else if (started_emulators.length > 0) {
+            Log("Emulator already started : " + started_emulators[0].split(' ', 1));
+            return;
+        } else {
+            Log("Error : unable to start emulator, ensure you have emulators availible by checking \'list-emulator-images\'", true);
+            WScript.Quit(2);
+        }
+    }
+    if (!started) {
+        Log("Error : unable to start emulator, ensure you have emulators availible by checking \'list-emulator-images\'", true);
+        WScript.Quit(2);
+    }
+    else { // wait for emulator to boot before returning
+        WScript.Stdout.Write('Booting up emulator..');
+        var boot_anim = null;
+        var emulator_ID = null;
+        var new_started = get_started_emulators();
+        var i = 0;
+        // use boot animation property to tell when boot is complete.
+        while ((boot_anim == null || !boot_anim.output.match(/stopped/)) && i < 100) {
+            if (new_started.length > started_emulators.length && emulator_ID == null) {
+                // find new emulator that was just started to get it's ID
+                for(var i = 0; i < new_started.length; i++) {
+                    if (new_started[i] != started_emulators[i]) {
+                        emulator_ID = new_started[i].split(' ', 1)[0];
+                        boot_anim = exec_out('%comspec% /c adb -s ' + emulator_ID + ' shell getprop init.svc.bootanim');
+                        break;
+                    }
+                }
+            }
+            else if (boot_anim == null) {
+                new_started = get_started_emulators(); 
+            }
+            else {
+                boot_anim = exec_out('%comspec% /c adb -s ' + emulator_ID + ' shell getprop init.svc.bootanim'); 
+            }
+            i++;
+            WScript.Stdout.Write('.');
+            WScript.Sleep(2000);
+        }
+        if (i < 100) {
+            Log('\nBoot Complete!');
+        } else {
+             Log('\nEmulator boot timed out. Failed to load emulator');
+             WScript.Quit(2);
+        }
+    }
+}
+
+function install_device(target) {
+    var devices = get_devices();
+    var use_target = false;
+    if (devices.length < 1) {
+        Log("Error : No devices found to install to, make sure there are devices", true);
+        Log(" availible by checking \'<project_dir>\\cordova\\lib\\list-devices\'", true);
+        WScript.Quit(2);
+    }
+    if (target) {
+        var exists = false;
+        for (i in devices) {
+            if (devices[i].substr(0,target.length) == target)
+            {
+                exists = true;
+                break;
+            }
+        }
+        if (!exists) {
+            Log("Error : Unable to find target " + target, true);
+            Log("Please ensure the target exists by checking \'<project>\\cordova\\lib\\list-devices'");
+            WScript.Quit(2);
+        }
+        use_target = true;
+    }
+    // check if file .apk has been created
+    if (fso.FolderExists(ROOT + '\\bin')) {
+        var path_to_apk;
+        var out_folder = fso.GetFolder(ROOT + '\\bin');
+        var out_files = new Enumerator(out_folder.Files);
+        for (;!out_files.atEnd(); out_files.moveNext()) {
+            var path = out_files.item() + '';
+            if (fso.GetExtensionName(path) == 'apk' && !path.match(/unaligned/)) {
+                path_to_apk = out_files.item();
+                break;
+            }
+        }
+        if (path_to_apk) {
+            var launch_name = exec_out("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
+            if (launch_name.error) {
+                Log("Failed to get application name from appinfo.jar + AndroidManifest : ", true);
+                Log("Output : " + launch_name.output, true);
+                WScript.Quit(2);
+            }
+            // install on device (-d)
+            Log("Installing app on device...");
+            var cmd;
+            if (use_target) {
+                cmd = '%comspec% /c adb -s ' + target + ' install -r ' + path_to_apk;
+            } else {
+                cmd = '%comspec% /c adb -s ' + devices[0].split(' ', 1)[0] + ' install -r ' + path_to_apk;
+            }
+            var install = exec_out(cmd);
+            if ( install.error && install.output.match(/Failure/)) {
+                Log("Error : Could not install apk to device : ", true);
+                Log(install.output, true);
+                WScript.Quit(2);
+            }
+            else {
+                Log(install.output);
+            }
+            // run on device
+            Log("Launching application...");
+            cmd;
+            if (use_target) {
+                cmd = '%comspec% /c adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
+            } else {
+                cmd = '%comspec% /c adb -s ' + devices[0].split(' ', 1)[0] + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
+            }
+            exec_verbose(cmd);
+        }
+        else {
+            Log('Failed to find apk, make sure you project is built and there is an ', true);
+            Log(' apk in <project>\\bin\\.  To build your project use \'<project>\\cordova\\build\'', true);
+            WScript.Quit(2);
+        }
+    }
+}
+
+function install_emulator(target) {
+    var emulators = get_started_emulators();
+    var use_target = false;
+    if (emulators.length < 1) {
+        Log("Error : No emulators found to install to, make sure there are emulators", true);
+        Log(" availible by checking \'<project_dir>\\cordova\\lib\\list-started-emulators\'", true);
+        WScript.Quit(2);
+    }
+    if (target) {
+        var exists = false;
+        for (i in emulators) {
+            if (emulators[i].substr(0,target.length) == target)
+            {
+                exists = true;
+                break;
+            }
+        }
+        if (!exists) {
+            Log("Error : Unable to find target " + target, true);
+            Log("Please ensure the target exists by checking \'<project>\\cordova\\lib\\list-started-emulators'")
+        }
+        use_target = true;
+    } else {
+        target = emulators[0].split(' ', 1)[0];
+        Log("Deploying to emulator : " + target);
+    }
+    // check if file .apk has been created
+    if (fso.FolderExists(ROOT + '\\bin')) {
+        var path_to_apk;
+        var out_folder = fso.GetFolder(ROOT + '\\bin');
+        var out_files = new Enumerator(out_folder.Files);
+        for (;!out_files.atEnd(); out_files.moveNext()) {
+            var path = out_files.item() + '';
+            if (fso.GetExtensionName(path) == 'apk' && !path.match(/unaligned/)) {
+                path_to_apk = out_files.item();
+                break;
+            }
+        }
+        if (path_to_apk) {
+            var launch_name = exec_out("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
+            if (launch_name.error) {
+                Log("Failed to get application name from appinfo.jar + AndroidManifest : ", true);
+                Log("Output : " + launch_name.output, true);
+                WScript.Quit(2);
+            }
+            // install on emulator (-e)
+            Log("Installing app on emulator...");
+            var cmd = '%comspec% /c adb -s ' + target + ' install -r ' + path_to_apk;
+            var install = exec_out(cmd);
+            if ( install.error && install.output.match(/Failure/)) {
+                Log("Error : Could not install apk to emulator : ", true);
+                Log(install.output, true);
+                WScript.Quit(2);
+            }
+            else {
+                Log(install.output);
+            }
+            // run on emulator
+            Log("Launching application...");
+            cmd;
+            if (use_target) {
+                cmd = '%comspec% /c adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
+            } else {
+                cmd = '%comspec% /c adb -s ' + emulators[0].split(' ', 1)[0] + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output
+            }
+            exec_verbose(cmd);
+        }
+        else {
+            Log('Failed to find apk, make sure you project is built and there is an ', true);
+            Log(' apk in <project>\\bin\\.  To build your project use \'<project>\\cordova\\build\'', true);
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log('Failed to find apk, make sure you project is built and there is an ', true);
+        Log(' apk in <project>\\bin\\.  To build your project use \'<project>\\cordova\\build\'', true);
+        WScript.Quit(2);
+    }
+}
+
+function clean() {
+    Log("Cleaning project...");
+    exec("%comspec% /c ant.bat clean -f "+ROOT+"\\build.xml 2>&1");
+}
+
+function build(build_type) {
+    if (build_type) {
+        switch (build_type) {
+            case "--debug" :
+                clean();
+                Log("Building project...");
+                exec_verbose("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
+                break;
+            case "--release" :
+                clean();
+                Log("Building project...");
+                exec_verbose("%comspec% /c ant.bat release -f "+ROOT+"\\build.xml 2>&1");
+                break;
+            case "--nobuild" :
+                Log("Skipping build process.");
+                break;
+            default :
+                Log("Build option not recognized: " + build_type, true);
+                WScript.Quit(2);
+                break;
+        }
+    }
+    else {
+        Log("WARNING: [ --debug | --release | --nobuild ] not specified, defaulting to --debug.");
+        exec_verbose("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
+    }
+}
+
+function log() {
+    // filter out nativeGetEnabledTags spam from latest sdk bug.
+    shell.Run("%comspec% /c adb logcat | grep -v nativeGetEnabledTags");
+}
+
+function run(target, build_type) {
+    var use_target = false;
+    if (!target) {
+        Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, using defaults");
+    }
+    // build application
+    build(build_type);
+    // attempt to deploy to connected device 
+    var devices = get_devices();
+    if (devices.length > 0 || target == "--device") {
+        if (target) {
+            if (target.substr(0,9) == "--target=") {
+                install_device(target.split('--target=').join(''))
+            } else if (target == "--device") {
+                install_device();
+            } else {
+                Log("Did not regognize " + target + " as a run option.", true);
+                WScript.Quit(2);
+            }
+        }
+        else {
+            Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, using defaults");
+            install_device();
+        }
+    }
+    else {
+        var emulators = get_started_emulators();
+        if (emulators.length > 0) {
+            install_emulator();
+        }
+        else {
+            var emulator_images = get_emulator_images();
+            if (emulator_images.length < 1) {
+                Log('No emulators found, if you would like to create an emulator follow the instructions', true);
+                Log(' provided here : http://developer.android.com/tools/devices/index.html', true);
+                Log(' Or run \'android create avd --name <name> --target <targetID>\' in on the command line.', true);
+                WScript.Quit(2);
+            }
+            start_emulator(emulator_images[0].split(' ')[0]);
+            emulators = get_started_emulators();
+            if (emulators.length > 0) {
+                install_emulator();
+            }
+            else {
+                Log("Error : emulator failed to start.", true);
+                WScript.Quit(2);
+            }
+        }
+    }
+}
+
+var args = WScript.Arguments;
+if (args.count() == 0) {
+    Log("Error: no args provided.");
+    WScript.Quit(2);
+}
+else {
+    if (args(0) == "build") {
+        if (args.Count() > 1) {
+            build(args(1))
+        } else {
+            build();
+        }
+    } else if (args(0) == "clean") {
+        clean();
+    } else if (args(0) == "list-devices") {
+        list_devices();
+    } else if (args(0) == "list-emulator-images") {
+        list_emulator_images();
+    } else if (args(0) == "list-started-emulators") {
+        list_started_emulators();
+    } else if (args(0) == "start-emulator") {
+        if (args.Count() > 1) {
+            start_emulator(args(1))
+        } else {
+            start_emulator();
+        }
+    } else if (args(0) == "log") {
+        log();
+    } else if (args(0) == "install-emulator") {
+        if (args.Count() == 2) {
+            if (args(1).substr(0,9) == "--target=") {
+                install_emulator(args(1).split('--target=').join(''));
+            } else {
+                Log('Error: \"' + args(1) + '\" is not recognized as an install option', true);
+                WScript.Quit(2);
+            }
+        } else {
+            install_emulator();
+        }
+    } else if (args(0) == "install-device") {
+        if (args.Count() == 2) {
+            if (args(1).substr(0,9) == "--target=") {
+                install_device(args(1).split('--target=').join(''));
+            } else {
+                Log('Error: \"' + args(1) + '\" is not recognized as an install option', true);
+                WScript.Quit(2);
+            }
+        } else {
+            install_device();
+        }
+    } else if (args(0) == "run") {
+        if (args.Count() == 3) {
+            run(args(1), args(2));
+        }
+        else if (args.Count() == 2) {
+            if (args(1).substr(0,9) == "--target=" ||
+               args(1) == "--emulator" ||
+               args(1) == "--device") {
+                run(args(1));
+            } else if (args(1) == "--debug" ||
+                       args(1) == "--release" ||
+                       args(1) == "--nobuild") {
+                run(null, args(1))
+            } else {
+                Log('Error: \"' + args(1) + '\" is not recognized as a run option', true);
+                WScript.Quit(2);
+            }
+        }
+        else {
+            run();
+        }
+    } else {
+        Log('Error: \"' + args(0) + '\" is not recognized as a tooling command', true);
+        WScript.Quit(2);
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/install-device
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/install-device b/lib/cordova-android/bin/templates/cordova/lib/install-device
new file mode 100644
index 0000000..604b5ae
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/install-device
@@ -0,0 +1,23 @@
+#!/bin/bash
+# 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.
+
+set -e
+
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash "$CORDOVA_LIB_PATH"/cordova install-device "$@"
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/install-device.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/install-device.bat b/lib/cordova-android/bin/templates/cordova/lib/install-device.bat
new file mode 100644
index 0000000..52d9775
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/install-device.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" install-device %* //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/install-emulator
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/install-emulator b/lib/cordova-android/bin/templates/cordova/lib/install-emulator
new file mode 100644
index 0000000..105e2ee
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/install-emulator
@@ -0,0 +1,23 @@
+#!/bin/bash
+# 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.
+
+set -e
+
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash "$CORDOVA_LIB_PATH"/cordova install-emulator "$@"
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/install-emulator.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/install-emulator.bat b/lib/cordova-android/bin/templates/cordova/lib/install-emulator.bat
new file mode 100644
index 0000000..d11a7be
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/install-emulator.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" install-emulator %* //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/list-devices
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/list-devices b/lib/cordova-android/bin/templates/cordova/lib/list-devices
new file mode 100644
index 0000000..7a5b2f5
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-devices
@@ -0,0 +1,23 @@
+#!/bin/bash
+# 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.
+
+set -e
+
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash "$CORDOVA_LIB_PATH"/cordova list-devices
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/list-devices.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/list-devices.bat b/lib/cordova-android/bin/templates/cordova/lib/list-devices.bat
new file mode 100644
index 0000000..c146f10
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-devices.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" list-devices //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images b/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images
new file mode 100644
index 0000000..db8e563
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images
@@ -0,0 +1,23 @@
+#!/bin/bash
+# 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.
+
+set -e
+
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash "$CORDOVA_LIB_PATH"/cordova list-emulator-images
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images.bat b/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images.bat
new file mode 100644
index 0000000..172520b
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" list-emulator-images //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file


[40/50] git commit: [CB-3457] Fix Android to support dash in widget id.

Posted by br...@apache.org.
[CB-3457] Fix Android to support dash in widget id.


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

Branch: refs/heads/master2
Commit: 2597ee2b03b3e6b414ecd671e139af0e3d96bcb5
Parents: b981588
Author: Michael Brooks <mi...@michaelbrooks.ca>
Authored: Tue May 21 11:56:19 2013 -0700
Committer: Ian Clelland <ic...@chromium.org>
Committed: Thu May 23 16:04:02 2013 -0400

----------------------------------------------------------------------
 .../platform-script/android/android_parser.spec.js |   17 +++++++++++++++
 src/metadata/android_parser.js                     |    1 +
 2 files changed, 18 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/2597ee2b/spec/platform-script/android/android_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/android/android_parser.spec.js b/spec/platform-script/android/android_parser.spec.js
index 84c7903..03bf1a1 100644
--- a/spec/platform-script/android/android_parser.spec.js
+++ b/spec/platform-script/android/android_parser.spec.js
@@ -98,6 +98,23 @@ describe('android project parser', function() {
             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');

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/2597ee2b/src/metadata/android_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/android_parser.js b/src/metadata/android_parser.js
index 09b379f..ec6b7d0 100644
--- a/src/metadata/android_parser.js
+++ b/src/metadata/android_parser.js
@@ -73,6 +73,7 @@ module.exports.prototype = {
         // Update package name by changing the AndroidManifest id and moving the entry class around to the proper package directory
         var manifest = new et.ElementTree(et.XML(fs.readFileSync(this.manifest, 'utf-8')));
         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;
         fs.writeFileSync(this.manifest, manifest.write({indent: 4}), 'utf-8');


[44/50] git commit: Adding staging dir support to WP7+8.

Posted by br...@apache.org.
Adding staging dir support to WP7+8.


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

Branch: refs/heads/master2
Commit: 0584e1794744c5c65f6d1d5ad09797834d025782
Parents: a79365d
Author: Braden Shepherdson <br...@gmail.com>
Authored: Thu May 23 16:38:31 2013 -0400
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Thu May 23 16:38:31 2013 -0400

----------------------------------------------------------------------
 src/metadata/wp7_parser.js |   15 +++++++++++++++
 src/metadata/wp8_parser.js |   15 +++++++++++++++
 2 files changed, 30 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0584e179/src/metadata/wp7_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/wp7_parser.js b/src/metadata/wp7_parser.js
index 289eca0..3a4244f 100644
--- a/src/metadata/wp7_parser.js
+++ b/src/metadata/wp7_parser.js
@@ -144,12 +144,27 @@ module.exports.prototype = {
         var cordovajs_path = path.join(util.libDirectory, 'cordova-wp7', 'templates', 'standalone', 'www', 'cordova-' + VERSION + '.js');
         fs.writeFileSync(path.join(this.www_dir(), 'cordova.js'), fs.readFileSync(cordovajs_path, 'utf-8'), 'utf-8');
     },
+
+    staging_dir: function() {
+        return path.join(this.path, '.staging', 'www');
+    },
+
+    update_staging: function() {
+        var projectRoot = util.isCordova(this.path);
+        if (fs.existsSync(this.staging_dir())) {
+            var staging = path.join(this.staging_dir(), '*');
+            shell.cp('-rf', staging, this.www_dir());
+        }
+    },
+
     // calls the nessesary functions to update the wp7 project 
     update_project:function(cfg, callback) {
         //console.log("Updating wp7 project...");
 
         this.update_from_config(cfg);
         this.update_www();
+        // TODO: Add overrides support? Why is this missing?
+        this.update_staging();
         util.deleteSvnFolders(this.www_dir());
 
         //console.log("Done updating.");

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0584e179/src/metadata/wp8_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/wp8_parser.js b/src/metadata/wp8_parser.js
index 5ecb6dc..ddfccea 100644
--- a/src/metadata/wp8_parser.js
+++ b/src/metadata/wp8_parser.js
@@ -142,12 +142,27 @@ module.exports.prototype = {
         var cordovajs_path = path.join(util.libDirectory, 'cordova-wp8', 'templates', 'standalone', 'www', 'cordova-' + VERSION + '.js');
         fs.writeFileSync(path.join(this.www_dir(), 'cordova.js'), fs.readFileSync(cordovajs_path, 'utf-8'), 'utf-8');
     },
+
+    staging_dir: function() {
+        return path.join(this.path, '.staging', 'www');
+    },
+
+    update_staging: function() {
+        var projectRoot = util.isCordova(this.path);
+        if (fs.existsSync(this.staging_dir())) {
+            var staging = path.join(this.staging_dir(), '*');
+            shell.cp('-rf', staging, this.www_dir());
+        }
+    },
+
     // calls the nessesary functions to update the wp8 project 
     update_project:function(cfg, callback) {
         //console.log("Updating wp8 project...");
 
         this.update_from_config(cfg);
         this.update_www();
+        // TODO: Add overrides support? Why is this missing?
+        this.update_staging();
         util.deleteSvnFolders(this.www_dir());
 
         //console.log("Done updating.");


[46/50] Fix bootstrap, app/ dir is fully reverted now.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/spec/lib/jasmine-1.2.0/jasmine.js
----------------------------------------------------------------------
diff --git a/templates/www/spec/lib/jasmine-1.2.0/jasmine.js b/templates/www/spec/lib/jasmine-1.2.0/jasmine.js
new file mode 100644
index 0000000..03bf89a
--- /dev/null
+++ b/templates/www/spec/lib/jasmine-1.2.0/jasmine.js
@@ -0,0 +1,2529 @@
+var isCommonJS = typeof window == "undefined";
+
+/**
+ * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
+ *
+ * @namespace
+ */
+var jasmine = {};
+if (isCommonJS) exports.jasmine = jasmine;
+/**
+ * @private
+ */
+jasmine.unimplementedMethod_ = function() {
+  throw new Error("unimplemented method");
+};
+
+/**
+ * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
+ * a plain old variable and may be redefined by somebody else.
+ *
+ * @private
+ */
+jasmine.undefined = jasmine.___undefined___;
+
+/**
+ * Show diagnostic messages in the console if set to true
+ *
+ */
+jasmine.VERBOSE = false;
+
+/**
+ * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
+ *
+ */
+jasmine.DEFAULT_UPDATE_INTERVAL = 250;
+
+/**
+ * Default timeout interval in milliseconds for waitsFor() blocks.
+ */
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
+
+jasmine.getGlobal = function() {
+  function getGlobal() {
+    return this;
+  }
+
+  return getGlobal();
+};
+
+/**
+ * Allows for bound functions to be compared.  Internal use only.
+ *
+ * @ignore
+ * @private
+ * @param base {Object} bound 'this' for the function
+ * @param name {Function} function to find
+ */
+jasmine.bindOriginal_ = function(base, name) {
+  var original = base[name];
+  if (original.apply) {
+    return function() {
+      return original.apply(base, arguments);
+    };
+  } else {
+    // IE support
+    return jasmine.getGlobal()[name];
+  }
+};
+
+jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
+jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
+jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
+jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
+
+jasmine.MessageResult = function(values) {
+  this.type = 'log';
+  this.values = values;
+  this.trace = new Error(); // todo: test better
+};
+
+jasmine.MessageResult.prototype.toString = function() {
+  var text = "";
+  for (var i = 0; i < this.values.length; i++) {
+    if (i > 0) text += " ";
+    if (jasmine.isString_(this.values[i])) {
+      text += this.values[i];
+    } else {
+      text += jasmine.pp(this.values[i]);
+    }
+  }
+  return text;
+};
+
+jasmine.ExpectationResult = function(params) {
+  this.type = 'expect';
+  this.matcherName = params.matcherName;
+  this.passed_ = params.passed;
+  this.expected = params.expected;
+  this.actual = params.actual;
+  this.message = this.passed_ ? 'Passed.' : params.message;
+
+  var trace = (params.trace || new Error(this.message));
+  this.trace = this.passed_ ? '' : trace;
+};
+
+jasmine.ExpectationResult.prototype.toString = function () {
+  return this.message;
+};
+
+jasmine.ExpectationResult.prototype.passed = function () {
+  return this.passed_;
+};
+
+/**
+ * Getter for the Jasmine environment. Ensures one gets created
+ */
+jasmine.getEnv = function() {
+  var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
+  return env;
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isArray_ = function(value) {
+  return jasmine.isA_("Array", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isString_ = function(value) {
+  return jasmine.isA_("String", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isNumber_ = function(value) {
+  return jasmine.isA_("Number", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param {String} typeName
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isA_ = function(typeName, value) {
+  return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
+};
+
+/**
+ * Pretty printer for expecations.  Takes any object and turns it into a human-readable string.
+ *
+ * @param value {Object} an object to be outputted
+ * @returns {String}
+ */
+jasmine.pp = function(value) {
+  var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
+  stringPrettyPrinter.format(value);
+  return stringPrettyPrinter.string;
+};
+
+/**
+ * Returns true if the object is a DOM Node.
+ *
+ * @param {Object} obj object to check
+ * @returns {Boolean}
+ */
+jasmine.isDomNode = function(obj) {
+  return obj.nodeType > 0;
+};
+
+/**
+ * Returns a matchable 'generic' object of the class type.  For use in expecations of type when values don't matter.
+ *
+ * @example
+ * // don't care about which function is passed in, as long as it's a function
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
+ *
+ * @param {Class} clazz
+ * @returns matchable object of the type clazz
+ */
+jasmine.any = function(clazz) {
+  return new jasmine.Matchers.Any(clazz);
+};
+
+/**
+ * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
+ * attributes on the object.
+ *
+ * @example
+ * // don't care about any other attributes than foo.
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
+ *
+ * @param sample {Object} sample
+ * @returns matchable object for the sample
+ */
+jasmine.objectContaining = function (sample) {
+    return new jasmine.Matchers.ObjectContaining(sample);
+};
+
+/**
+ * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
+ *
+ * Spies should be created in test setup, before expectations.  They can then be checked, using the standard Jasmine
+ * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
+ *
+ * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
+ *
+ * Spies are torn down at the end of every spec.
+ *
+ * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
+ *
+ * @example
+ * // a stub
+ * var myStub = jasmine.createSpy('myStub');  // can be used anywhere
+ *
+ * // spy example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ *
+ * // actual foo.not will not be called, execution stops
+ * spyOn(foo, 'not');
+
+ // foo.not spied upon, execution will continue to implementation
+ * spyOn(foo, 'not').andCallThrough();
+ *
+ * // fake example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ *
+ * // foo.not(val) will return val
+ * spyOn(foo, 'not').andCallFake(function(value) {return value;});
+ *
+ * // mock example
+ * foo.not(7 == 7);
+ * expect(foo.not).toHaveBeenCalled();
+ * expect(foo.not).toHaveBeenCalledWith(true);
+ *
+ * @constructor
+ * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
+ * @param {String} name
+ */
+jasmine.Spy = function(name) {
+  /**
+   * The name of the spy, if provided.
+   */
+  this.identity = name || 'unknown';
+  /**
+   *  Is this Object a spy?
+   */
+  this.isSpy = true;
+  /**
+   * The actual function this spy stubs.
+   */
+  this.plan = function() {
+  };
+  /**
+   * Tracking of the most recent call to the spy.
+   * @example
+   * var mySpy = jasmine.createSpy('foo');
+   * mySpy(1, 2);
+   * mySpy.mostRecentCall.args = [1, 2];
+   */
+  this.mostRecentCall = {};
+
+  /**
+   * Holds arguments for each call to the spy, indexed by call count
+   * @example
+   * var mySpy = jasmine.createSpy('foo');
+   * mySpy(1, 2);
+   * mySpy(7, 8);
+   * mySpy.mostRecentCall.args = [7, 8];
+   * mySpy.argsForCall[0] = [1, 2];
+   * mySpy.argsForCall[1] = [7, 8];
+   */
+  this.argsForCall = [];
+  this.calls = [];
+};
+
+/**
+ * Tells a spy to call through to the actual implemenatation.
+ *
+ * @example
+ * var foo = {
+ *   bar: function() { // do some stuff }
+ * }
+ *
+ * // defining a spy on an existing property: foo.bar
+ * spyOn(foo, 'bar').andCallThrough();
+ */
+jasmine.Spy.prototype.andCallThrough = function() {
+  this.plan = this.originalValue;
+  return this;
+};
+
+/**
+ * For setting the return value of a spy.
+ *
+ * @example
+ * // defining a spy from scratch: foo() returns 'baz'
+ * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() returns 'baz'
+ * spyOn(foo, 'bar').andReturn('baz');
+ *
+ * @param {Object} value
+ */
+jasmine.Spy.prototype.andReturn = function(value) {
+  this.plan = function() {
+    return value;
+  };
+  return this;
+};
+
+/**
+ * For throwing an exception when a spy is called.
+ *
+ * @example
+ * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
+ * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
+ * spyOn(foo, 'bar').andThrow('baz');
+ *
+ * @param {String} exceptionMsg
+ */
+jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
+  this.plan = function() {
+    throw exceptionMsg;
+  };
+  return this;
+};
+
+/**
+ * Calls an alternate implementation when a spy is called.
+ *
+ * @example
+ * var baz = function() {
+ *   // do some stuff, return something
+ * }
+ * // defining a spy from scratch: foo() calls the function baz
+ * var foo = jasmine.createSpy('spy on foo').andCall(baz);
+ *
+ * // defining a spy on an existing property: foo.bar() calls an anonymnous function
+ * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
+ *
+ * @param {Function} fakeFunc
+ */
+jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
+  this.plan = fakeFunc;
+  return this;
+};
+
+/**
+ * Resets all of a spy's the tracking variables so that it can be used again.
+ *
+ * @example
+ * spyOn(foo, 'bar');
+ *
+ * foo.bar();
+ *
+ * expect(foo.bar.callCount).toEqual(1);
+ *
+ * foo.bar.reset();
+ *
+ * expect(foo.bar.callCount).toEqual(0);
+ */
+jasmine.Spy.prototype.reset = function() {
+  this.wasCalled = false;
+  this.callCount = 0;
+  this.argsForCall = [];
+  this.calls = [];
+  this.mostRecentCall = {};
+};
+
+jasmine.createSpy = function(name) {
+
+  var spyObj = function() {
+    spyObj.wasCalled = true;
+    spyObj.callCount++;
+    var args = jasmine.util.argsToArray(arguments);
+    spyObj.mostRecentCall.object = this;
+    spyObj.mostRecentCall.args = args;
+    spyObj.argsForCall.push(args);
+    spyObj.calls.push({object: this, args: args});
+    return spyObj.plan.apply(this, arguments);
+  };
+
+  var spy = new jasmine.Spy(name);
+
+  for (var prop in spy) {
+    spyObj[prop] = spy[prop];
+  }
+
+  spyObj.reset();
+
+  return spyObj;
+};
+
+/**
+ * Determines whether an object is a spy.
+ *
+ * @param {jasmine.Spy|Object} putativeSpy
+ * @returns {Boolean}
+ */
+jasmine.isSpy = function(putativeSpy) {
+  return putativeSpy && putativeSpy.isSpy;
+};
+
+/**
+ * Creates a more complicated spy: an Object that has every property a function that is a spy.  Used for stubbing something
+ * large in one call.
+ *
+ * @param {String} baseName name of spy class
+ * @param {Array} methodNames array of names of methods to make spies
+ */
+jasmine.createSpyObj = function(baseName, methodNames) {
+  if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
+    throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
+  }
+  var obj = {};
+  for (var i = 0; i < methodNames.length; i++) {
+    obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
+  }
+  return obj;
+};
+
+/**
+ * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
+ *
+ * Be careful not to leave calls to <code>jasmine.log</code> in production code.
+ */
+jasmine.log = function() {
+  var spec = jasmine.getEnv().currentSpec;
+  spec.log.apply(spec, arguments);
+};
+
+/**
+ * Function that installs a spy on an existing object's method name.  Used within a Spec to create a spy.
+ *
+ * @example
+ * // spy example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
+ *
+ * @see jasmine.createSpy
+ * @param obj
+ * @param methodName
+ * @returns a Jasmine spy that can be chained with all spy methods
+ */
+var spyOn = function(obj, methodName) {
+  return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
+};
+if (isCommonJS) exports.spyOn = spyOn;
+
+/**
+ * Creates a Jasmine spec that will be added to the current suite.
+ *
+ * // TODO: pending tests
+ *
+ * @example
+ * it('should be true', function() {
+ *   expect(true).toEqual(true);
+ * });
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var it = function(desc, func) {
+  return jasmine.getEnv().it(desc, func);
+};
+if (isCommonJS) exports.it = it;
+
+/**
+ * Creates a <em>disabled</em> Jasmine spec.
+ *
+ * A convenience method that allows existing specs to be disabled temporarily during development.
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var xit = function(desc, func) {
+  return jasmine.getEnv().xit(desc, func);
+};
+if (isCommonJS) exports.xit = xit;
+
+/**
+ * Starts a chain for a Jasmine expectation.
+ *
+ * It is passed an Object that is the actual value and should chain to one of the many
+ * jasmine.Matchers functions.
+ *
+ * @param {Object} actual Actual value to test against and expected value
+ */
+var expect = function(actual) {
+  return jasmine.getEnv().currentSpec.expect(actual);
+};
+if (isCommonJS) exports.expect = expect;
+
+/**
+ * Defines part of a jasmine spec.  Used in cominbination with waits or waitsFor in asynchrnous specs.
+ *
+ * @param {Function} func Function that defines part of a jasmine spec.
+ */
+var runs = function(func) {
+  jasmine.getEnv().currentSpec.runs(func);
+};
+if (isCommonJS) exports.runs = runs;
+
+/**
+ * Waits a fixed time period before moving to the next block.
+ *
+ * @deprecated Use waitsFor() instead
+ * @param {Number} timeout milliseconds to wait
+ */
+var waits = function(timeout) {
+  jasmine.getEnv().currentSpec.waits(timeout);
+};
+if (isCommonJS) exports.waits = waits;
+
+/**
+ * Waits for the latchFunction to return true before proceeding to the next block.
+ *
+ * @param {Function} latchFunction
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
+  jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
+};
+if (isCommonJS) exports.waitsFor = waitsFor;
+
+/**
+ * A function that is called before each spec in a suite.
+ *
+ * Used for spec setup, including validating assumptions.
+ *
+ * @param {Function} beforeEachFunction
+ */
+var beforeEach = function(beforeEachFunction) {
+  jasmine.getEnv().beforeEach(beforeEachFunction);
+};
+if (isCommonJS) exports.beforeEach = beforeEach;
+
+/**
+ * A function that is called after each spec in a suite.
+ *
+ * Used for restoring any state that is hijacked during spec execution.
+ *
+ * @param {Function} afterEachFunction
+ */
+var afterEach = function(afterEachFunction) {
+  jasmine.getEnv().afterEach(afterEachFunction);
+};
+if (isCommonJS) exports.afterEach = afterEach;
+
+/**
+ * Defines a suite of specifications.
+ *
+ * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
+ * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
+ * of setup in some tests.
+ *
+ * @example
+ * // TODO: a simple suite
+ *
+ * // TODO: a simple suite with a nested describe block
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var describe = function(description, specDefinitions) {
+  return jasmine.getEnv().describe(description, specDefinitions);
+};
+if (isCommonJS) exports.describe = describe;
+
+/**
+ * Disables a suite of specifications.  Used to disable some suites in a file, or files, temporarily during development.
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var xdescribe = function(description, specDefinitions) {
+  return jasmine.getEnv().xdescribe(description, specDefinitions);
+};
+if (isCommonJS) exports.xdescribe = xdescribe;
+
+
+// Provide the XMLHttpRequest class for IE 5.x-6.x:
+jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
+  function tryIt(f) {
+    try {
+      return f();
+    } catch(e) {
+    }
+    return null;
+  }
+
+  var xhr = tryIt(function() {
+    return new ActiveXObject("Msxml2.XMLHTTP.6.0");
+  }) ||
+    tryIt(function() {
+      return new ActiveXObject("Msxml2.XMLHTTP.3.0");
+    }) ||
+    tryIt(function() {
+      return new ActiveXObject("Msxml2.XMLHTTP");
+    }) ||
+    tryIt(function() {
+      return new ActiveXObject("Microsoft.XMLHTTP");
+    });
+
+  if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
+
+  return xhr;
+} : XMLHttpRequest;
+/**
+ * @namespace
+ */
+jasmine.util = {};
+
+/**
+ * Declare that a child class inherit it's prototype from the parent class.
+ *
+ * @private
+ * @param {Function} childClass
+ * @param {Function} parentClass
+ */
+jasmine.util.inherit = function(childClass, parentClass) {
+  /**
+   * @private
+   */
+  var subclass = function() {
+  };
+  subclass.prototype = parentClass.prototype;
+  childClass.prototype = new subclass();
+};
+
+jasmine.util.formatException = function(e) {
+  var lineNumber;
+  if (e.line) {
+    lineNumber = e.line;
+  }
+  else if (e.lineNumber) {
+    lineNumber = e.lineNumber;
+  }
+
+  var file;
+
+  if (e.sourceURL) {
+    file = e.sourceURL;
+  }
+  else if (e.fileName) {
+    file = e.fileName;
+  }
+
+  var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
+
+  if (file && lineNumber) {
+    message += ' in ' + file + ' (line ' + lineNumber + ')';
+  }
+
+  return message;
+};
+
+jasmine.util.htmlEscape = function(str) {
+  if (!str) return str;
+  return str.replace(/&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;');
+};
+
+jasmine.util.argsToArray = function(args) {
+  var arrayOfArgs = [];
+  for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
+  return arrayOfArgs;
+};
+
+jasmine.util.extend = function(destination, source) {
+  for (var property in source) destination[property] = source[property];
+  return destination;
+};
+
+/**
+ * Environment for Jasmine
+ *
+ * @constructor
+ */
+jasmine.Env = function() {
+  this.currentSpec = null;
+  this.currentSuite = null;
+  this.currentRunner_ = new jasmine.Runner(this);
+
+  this.reporter = new jasmine.MultiReporter();
+
+  this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
+  this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
+  this.lastUpdate = 0;
+  this.specFilter = function() {
+    return true;
+  };
+
+  this.nextSpecId_ = 0;
+  this.nextSuiteId_ = 0;
+  this.equalityTesters_ = [];
+
+  // wrap matchers
+  this.matchersClass = function() {
+    jasmine.Matchers.apply(this, arguments);
+  };
+  jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
+
+  jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
+};
+
+
+jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
+jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
+jasmine.Env.prototype.setInterval = jasmine.setInterval;
+jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
+
+/**
+ * @returns an object containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.version = function () {
+  if (jasmine.version_) {
+    return jasmine.version_;
+  } else {
+    throw new Error('Version not set');
+  }
+};
+
+/**
+ * @returns string containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.versionString = function() {
+  if (!jasmine.version_) {
+    return "version unknown";
+  }
+
+  var version = this.version();
+  var versionString = version.major + "." + version.minor + "." + version.build;
+  if (version.release_candidate) {
+    versionString += ".rc" + version.release_candidate;
+  }
+  versionString += " revision " + version.revision;
+  return versionString;
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSpecId = function () {
+  return this.nextSpecId_++;
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSuiteId = function () {
+  return this.nextSuiteId_++;
+};
+
+/**
+ * Register a reporter to receive status updates from Jasmine.
+ * @param {jasmine.Reporter} reporter An object which will receive status updates.
+ */
+jasmine.Env.prototype.addReporter = function(reporter) {
+  this.reporter.addReporter(reporter);
+};
+
+jasmine.Env.prototype.execute = function() {
+  this.currentRunner_.execute();
+};
+
+jasmine.Env.prototype.describe = function(description, specDefinitions) {
+  var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
+
+  var parentSuite = this.currentSuite;
+  if (parentSuite) {
+    parentSuite.add(suite);
+  } else {
+    this.currentRunner_.add(suite);
+  }
+
+  this.currentSuite = suite;
+
+  var declarationError = null;
+  try {
+    specDefinitions.call(suite);
+  } catch(e) {
+    declarationError = e;
+  }
+
+  if (declarationError) {
+    this.it("encountered a declaration exception", function() {
+      throw declarationError;
+    });
+  }
+
+  this.currentSuite = parentSuite;
+
+  return suite;
+};
+
+jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
+  if (this.currentSuite) {
+    this.currentSuite.beforeEach(beforeEachFunction);
+  } else {
+    this.currentRunner_.beforeEach(beforeEachFunction);
+  }
+};
+
+jasmine.Env.prototype.currentRunner = function () {
+  return this.currentRunner_;
+};
+
+jasmine.Env.prototype.afterEach = function(afterEachFunction) {
+  if (this.currentSuite) {
+    this.currentSuite.afterEach(afterEachFunction);
+  } else {
+    this.currentRunner_.afterEach(afterEachFunction);
+  }
+
+};
+
+jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
+  return {
+    execute: function() {
+    }
+  };
+};
+
+jasmine.Env.prototype.it = function(description, func) {
+  var spec = new jasmine.Spec(this, this.currentSuite, description);
+  this.currentSuite.add(spec);
+  this.currentSpec = spec;
+
+  if (func) {
+    spec.runs(func);
+  }
+
+  return spec;
+};
+
+jasmine.Env.prototype.xit = function(desc, func) {
+  return {
+    id: this.nextSpecId(),
+    runs: function() {
+    }
+  };
+};
+
+jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
+  if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
+    return true;
+  }
+
+  a.__Jasmine_been_here_before__ = b;
+  b.__Jasmine_been_here_before__ = a;
+
+  var hasKey = function(obj, keyName) {
+    return obj !== null && obj[keyName] !== jasmine.undefined;
+  };
+
+  for (var property in b) {
+    if (!hasKey(a, property) && hasKey(b, property)) {
+      mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+    }
+  }
+  for (property in a) {
+    if (!hasKey(b, property) && hasKey(a, property)) {
+      mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
+    }
+  }
+  for (property in b) {
+    if (property == '__Jasmine_been_here_before__') continue;
+    if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
+      mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
+    }
+  }
+
+  if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
+    mismatchValues.push("arrays were not the same length");
+  }
+
+  delete a.__Jasmine_been_here_before__;
+  delete b.__Jasmine_been_here_before__;
+  return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+};
+
+jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
+  mismatchKeys = mismatchKeys || [];
+  mismatchValues = mismatchValues || [];
+
+  for (var i = 0; i < this.equalityTesters_.length; i++) {
+    var equalityTester = this.equalityTesters_[i];
+    var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
+    if (result !== jasmine.undefined) return result;
+  }
+
+  if (a === b) return true;
+
+  if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
+    return (a == jasmine.undefined && b == jasmine.undefined);
+  }
+
+  if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
+    return a === b;
+  }
+
+  if (a instanceof Date && b instanceof Date) {
+    return a.getTime() == b.getTime();
+  }
+
+  if (a.jasmineMatches) {
+    return a.jasmineMatches(b);
+  }
+
+  if (b.jasmineMatches) {
+    return b.jasmineMatches(a);
+  }
+
+  if (a instanceof jasmine.Matchers.ObjectContaining) {
+    return a.matches(b);
+  }
+
+  if (b instanceof jasmine.Matchers.ObjectContaining) {
+    return b.matches(a);
+  }
+
+  if (jasmine.isString_(a) && jasmine.isString_(b)) {
+    return (a == b);
+  }
+
+  if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
+    return (a == b);
+  }
+
+  if (typeof a === "object" && typeof b === "object") {
+    return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
+  }
+
+  //Straight check
+  return (a === b);
+};
+
+jasmine.Env.prototype.contains_ = function(haystack, needle) {
+  if (jasmine.isArray_(haystack)) {
+    for (var i = 0; i < haystack.length; i++) {
+      if (this.equals_(haystack[i], needle)) return true;
+    }
+    return false;
+  }
+  return haystack.indexOf(needle) >= 0;
+};
+
+jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
+  this.equalityTesters_.push(equalityTester);
+};
+/** No-op base class for Jasmine reporters.
+ *
+ * @constructor
+ */
+jasmine.Reporter = function() {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecResults = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.log = function(str) {
+};
+
+/**
+ * Blocks are functions with executable code that make up a spec.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {Function} func
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Block = function(env, func, spec) {
+  this.env = env;
+  this.func = func;
+  this.spec = spec;
+};
+
+jasmine.Block.prototype.execute = function(onComplete) {  
+  try {
+    this.func.apply(this.spec);
+  } catch (e) {
+    this.spec.fail(e);
+  }
+  onComplete();
+};
+/** JavaScript API reporter.
+ *
+ * @constructor
+ */
+jasmine.JsApiReporter = function() {
+  this.started = false;
+  this.finished = false;
+  this.suites_ = [];
+  this.results_ = {};
+};
+
+jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
+  this.started = true;
+  var suites = runner.topLevelSuites();
+  for (var i = 0; i < suites.length; i++) {
+    var suite = suites[i];
+    this.suites_.push(this.summarize_(suite));
+  }
+};
+
+jasmine.JsApiReporter.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
+  var isSuite = suiteOrSpec instanceof jasmine.Suite;
+  var summary = {
+    id: suiteOrSpec.id,
+    name: suiteOrSpec.description,
+    type: isSuite ? 'suite' : 'spec',
+    children: []
+  };
+  
+  if (isSuite) {
+    var children = suiteOrSpec.children();
+    for (var i = 0; i < children.length; i++) {
+      summary.children.push(this.summarize_(children[i]));
+    }
+  }
+  return summary;
+};
+
+jasmine.JsApiReporter.prototype.results = function() {
+  return this.results_;
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
+  return this.results_[specId];
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
+  this.finished = true;
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
+  this.results_[spec.id] = {
+    messages: spec.results().getItems(),
+    result: spec.results().failedCount > 0 ? "failed" : "passed"
+  };
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.log = function(str) {
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
+  var results = {};
+  for (var i = 0; i < specIds.length; i++) {
+    var specId = specIds[i];
+    results[specId] = this.summarizeResult_(this.results_[specId]);
+  }
+  return results;
+};
+
+jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
+  var summaryMessages = [];
+  var messagesLength = result.messages.length;
+  for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
+    var resultMessage = result.messages[messageIndex];
+    summaryMessages.push({
+      text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
+      passed: resultMessage.passed ? resultMessage.passed() : true,
+      type: resultMessage.type,
+      message: resultMessage.message,
+      trace: {
+        stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
+      }
+    });
+  }
+
+  return {
+    result : result.result,
+    messages : summaryMessages
+  };
+};
+
+/**
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param actual
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Matchers = function(env, actual, spec, opt_isNot) {
+  this.env = env;
+  this.actual = actual;
+  this.spec = spec;
+  this.isNot = opt_isNot || false;
+  this.reportWasCalled_ = false;
+};
+
+// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
+jasmine.Matchers.pp = function(str) {
+  throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
+};
+
+// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
+jasmine.Matchers.prototype.report = function(result, failing_message, details) {
+  throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
+};
+
+jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
+  for (var methodName in prototype) {
+    if (methodName == 'report') continue;
+    var orig = prototype[methodName];
+    matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
+  }
+};
+
+jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
+  return function() {
+    var matcherArgs = jasmine.util.argsToArray(arguments);
+    var result = matcherFunction.apply(this, arguments);
+
+    if (this.isNot) {
+      result = !result;
+    }
+
+    if (this.reportWasCalled_) return result;
+
+    var message;
+    if (!result) {
+      if (this.message) {
+        message = this.message.apply(this, arguments);
+        if (jasmine.isArray_(message)) {
+          message = message[this.isNot ? 1 : 0];
+        }
+      } else {
+        var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
+        message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
+        if (matcherArgs.length > 0) {
+          for (var i = 0; i < matcherArgs.length; i++) {
+            if (i > 0) message += ",";
+            message += " " + jasmine.pp(matcherArgs[i]);
+          }
+        }
+        message += ".";
+      }
+    }
+    var expectationResult = new jasmine.ExpectationResult({
+      matcherName: matcherName,
+      passed: result,
+      expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
+      actual: this.actual,
+      message: message
+    });
+    this.spec.addMatcherResult(expectationResult);
+    return jasmine.undefined;
+  };
+};
+
+
+
+
+/**
+ * toBe: compares the actual to the expected using ===
+ * @param expected
+ */
+jasmine.Matchers.prototype.toBe = function(expected) {
+  return this.actual === expected;
+};
+
+/**
+ * toNotBe: compares the actual to the expected using !==
+ * @param expected
+ * @deprecated as of 1.0. Use not.toBe() instead.
+ */
+jasmine.Matchers.prototype.toNotBe = function(expected) {
+  return this.actual !== expected;
+};
+
+/**
+ * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toEqual = function(expected) {
+  return this.env.equals_(this.actual, expected);
+};
+
+/**
+ * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
+ * @param expected
+ * @deprecated as of 1.0. Use not.toEqual() instead.
+ */
+jasmine.Matchers.prototype.toNotEqual = function(expected) {
+  return !this.env.equals_(this.actual, expected);
+};
+
+/**
+ * Matcher that compares the actual to the expected using a regular expression.  Constructs a RegExp, so takes
+ * a pattern or a String.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toMatch = function(expected) {
+  return new RegExp(expected).test(this.actual);
+};
+
+/**
+ * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
+ * @param expected
+ * @deprecated as of 1.0. Use not.toMatch() instead.
+ */
+jasmine.Matchers.prototype.toNotMatch = function(expected) {
+  return !(new RegExp(expected).test(this.actual));
+};
+
+/**
+ * Matcher that compares the actual to jasmine.undefined.
+ */
+jasmine.Matchers.prototype.toBeDefined = function() {
+  return (this.actual !== jasmine.undefined);
+};
+
+/**
+ * Matcher that compares the actual to jasmine.undefined.
+ */
+jasmine.Matchers.prototype.toBeUndefined = function() {
+  return (this.actual === jasmine.undefined);
+};
+
+/**
+ * Matcher that compares the actual to null.
+ */
+jasmine.Matchers.prototype.toBeNull = function() {
+  return (this.actual === null);
+};
+
+/**
+ * Matcher that boolean not-nots the actual.
+ */
+jasmine.Matchers.prototype.toBeTruthy = function() {
+  return !!this.actual;
+};
+
+
+/**
+ * Matcher that boolean nots the actual.
+ */
+jasmine.Matchers.prototype.toBeFalsy = function() {
+  return !this.actual;
+};
+
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was called.
+ */
+jasmine.Matchers.prototype.toHaveBeenCalled = function() {
+  if (arguments.length > 0) {
+    throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
+  }
+
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy " + this.actual.identity + " to have been called.",
+      "Expected spy " + this.actual.identity + " not to have been called."
+    ];
+  };
+
+  return this.actual.wasCalled;
+};
+
+/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
+jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was not called.
+ *
+ * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
+ */
+jasmine.Matchers.prototype.wasNotCalled = function() {
+  if (arguments.length > 0) {
+    throw new Error('wasNotCalled does not take arguments');
+  }
+
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy " + this.actual.identity + " to not have been called.",
+      "Expected spy " + this.actual.identity + " to have been called."
+    ];
+  };
+
+  return !this.actual.wasCalled;
+};
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
+ *
+ * @example
+ *
+ */
+jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
+  var expectedArgs = jasmine.util.argsToArray(arguments);
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+  this.message = function() {
+    if (this.actual.callCount === 0) {
+      // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
+      return [
+        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
+        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
+      ];
+    } else {
+      return [
+        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
+        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
+      ];
+    }
+  };
+
+  return this.env.contains_(this.actual.argsForCall, expectedArgs);
+};
+
+/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
+jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
+
+/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
+jasmine.Matchers.prototype.wasNotCalledWith = function() {
+  var expectedArgs = jasmine.util.argsToArray(arguments);
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
+      "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
+    ];
+  };
+
+  return !this.env.contains_(this.actual.argsForCall, expectedArgs);
+};
+
+/**
+ * Matcher that checks that the expected item is an element in the actual Array.
+ *
+ * @param {Object} expected
+ */
+jasmine.Matchers.prototype.toContain = function(expected) {
+  return this.env.contains_(this.actual, expected);
+};
+
+/**
+ * Matcher that checks that the expected item is NOT an element in the actual Array.
+ *
+ * @param {Object} expected
+ * @deprecated as of 1.0. Use not.toContain() instead.
+ */
+jasmine.Matchers.prototype.toNotContain = function(expected) {
+  return !this.env.contains_(this.actual, expected);
+};
+
+jasmine.Matchers.prototype.toBeLessThan = function(expected) {
+  return this.actual < expected;
+};
+
+jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
+  return this.actual > expected;
+};
+
+/**
+ * Matcher that checks that the expected item is equal to the actual item
+ * up to a given level of decimal precision (default 2).
+ *
+ * @param {Number} expected
+ * @param {Number} precision
+ */
+jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
+  if (!(precision === 0)) {
+    precision = precision || 2;
+  }
+  var multiplier = Math.pow(10, precision);
+  var actual = Math.round(this.actual * multiplier);
+  expected = Math.round(expected * multiplier);
+  return expected == actual;
+};
+
+/**
+ * Matcher that checks that the expected exception was thrown by the actual.
+ *
+ * @param {String} expected
+ */
+jasmine.Matchers.prototype.toThrow = function(expected) {
+  var result = false;
+  var exception;
+  if (typeof this.actual != 'function') {
+    throw new Error('Actual is not a function');
+  }
+  try {
+    this.actual();
+  } catch (e) {
+    exception = e;
+  }
+  if (exception) {
+    result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
+  }
+
+  var not = this.isNot ? "not " : "";
+
+  this.message = function() {
+    if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
+      return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
+    } else {
+      return "Expected function to throw an exception.";
+    }
+  };
+
+  return result;
+};
+
+jasmine.Matchers.Any = function(expectedClass) {
+  this.expectedClass = expectedClass;
+};
+
+jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {
+  if (this.expectedClass == String) {
+    return typeof other == 'string' || other instanceof String;
+  }
+
+  if (this.expectedClass == Number) {
+    return typeof other == 'number' || other instanceof Number;
+  }
+
+  if (this.expectedClass == Function) {
+    return typeof other == 'function' || other instanceof Function;
+  }
+
+  if (this.expectedClass == Object) {
+    return typeof other == 'object';
+  }
+
+  return other instanceof this.expectedClass;
+};
+
+jasmine.Matchers.Any.prototype.jasmineToString = function() {
+  return '<jasmine.any(' + this.expectedClass + ')>';
+};
+
+jasmine.Matchers.ObjectContaining = function (sample) {
+  this.sample = sample;
+};
+
+jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
+  mismatchKeys = mismatchKeys || [];
+  mismatchValues = mismatchValues || [];
+
+  var env = jasmine.getEnv();
+
+  var hasKey = function(obj, keyName) {
+    return obj != null && obj[keyName] !== jasmine.undefined;
+  };
+
+  for (var property in this.sample) {
+    if (!hasKey(other, property) && hasKey(this.sample, property)) {
+      mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+    }
+    else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
+      mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
+    }
+  }
+
+  return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+};
+
+jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
+  return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>";
+};
+// Mock setTimeout, clearTimeout
+// Contributed by Pivotal Computer Systems, www.pivotalsf.com
+
+jasmine.FakeTimer = function() {
+  this.reset();
+
+  var self = this;
+  self.setTimeout = function(funcToCall, millis) {
+    self.timeoutsMade++;
+    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
+    return self.timeoutsMade;
+  };
+
+  self.setInterval = function(funcToCall, millis) {
+    self.timeoutsMade++;
+    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
+    return self.timeoutsMade;
+  };
+
+  self.clearTimeout = function(timeoutKey) {
+    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
+  };
+
+  self.clearInterval = function(timeoutKey) {
+    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
+  };
+
+};
+
+jasmine.FakeTimer.prototype.reset = function() {
+  this.timeoutsMade = 0;
+  this.scheduledFunctions = {};
+  this.nowMillis = 0;
+};
+
+jasmine.FakeTimer.prototype.tick = function(millis) {
+  var oldMillis = this.nowMillis;
+  var newMillis = oldMillis + millis;
+  this.runFunctionsWithinRange(oldMillis, newMillis);
+  this.nowMillis = newMillis;
+};
+
+jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
+  var scheduledFunc;
+  var funcsToRun = [];
+  for (var timeoutKey in this.scheduledFunctions) {
+    scheduledFunc = this.scheduledFunctions[timeoutKey];
+    if (scheduledFunc != jasmine.undefined &&
+        scheduledFunc.runAtMillis >= oldMillis &&
+        scheduledFunc.runAtMillis <= nowMillis) {
+      funcsToRun.push(scheduledFunc);
+      this.scheduledFunctions[timeoutKey] = jasmine.undefined;
+    }
+  }
+
+  if (funcsToRun.length > 0) {
+    funcsToRun.sort(function(a, b) {
+      return a.runAtMillis - b.runAtMillis;
+    });
+    for (var i = 0; i < funcsToRun.length; ++i) {
+      try {
+        var funcToRun = funcsToRun[i];
+        this.nowMillis = funcToRun.runAtMillis;
+        funcToRun.funcToCall();
+        if (funcToRun.recurring) {
+          this.scheduleFunction(funcToRun.timeoutKey,
+              funcToRun.funcToCall,
+              funcToRun.millis,
+              true);
+        }
+      } catch(e) {
+      }
+    }
+    this.runFunctionsWithinRange(oldMillis, nowMillis);
+  }
+};
+
+jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
+  this.scheduledFunctions[timeoutKey] = {
+    runAtMillis: this.nowMillis + millis,
+    funcToCall: funcToCall,
+    recurring: recurring,
+    timeoutKey: timeoutKey,
+    millis: millis
+  };
+};
+
+/**
+ * @namespace
+ */
+jasmine.Clock = {
+  defaultFakeTimer: new jasmine.FakeTimer(),
+
+  reset: function() {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.defaultFakeTimer.reset();
+  },
+
+  tick: function(millis) {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.defaultFakeTimer.tick(millis);
+  },
+
+  runFunctionsWithinRange: function(oldMillis, nowMillis) {
+    jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
+  },
+
+  scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
+    jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
+  },
+
+  useMock: function() {
+    if (!jasmine.Clock.isInstalled()) {
+      var spec = jasmine.getEnv().currentSpec;
+      spec.after(jasmine.Clock.uninstallMock);
+
+      jasmine.Clock.installMock();
+    }
+  },
+
+  installMock: function() {
+    jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
+  },
+
+  uninstallMock: function() {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.installed = jasmine.Clock.real;
+  },
+
+  real: {
+    setTimeout: jasmine.getGlobal().setTimeout,
+    clearTimeout: jasmine.getGlobal().clearTimeout,
+    setInterval: jasmine.getGlobal().setInterval,
+    clearInterval: jasmine.getGlobal().clearInterval
+  },
+
+  assertInstalled: function() {
+    if (!jasmine.Clock.isInstalled()) {
+      throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
+    }
+  },
+
+  isInstalled: function() {
+    return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
+  },
+
+  installed: null
+};
+jasmine.Clock.installed = jasmine.Clock.real;
+
+//else for IE support
+jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
+  if (jasmine.Clock.installed.setTimeout.apply) {
+    return jasmine.Clock.installed.setTimeout.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.setTimeout(funcToCall, millis);
+  }
+};
+
+jasmine.getGlobal().setInterval = function(funcToCall, millis) {
+  if (jasmine.Clock.installed.setInterval.apply) {
+    return jasmine.Clock.installed.setInterval.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.setInterval(funcToCall, millis);
+  }
+};
+
+jasmine.getGlobal().clearTimeout = function(timeoutKey) {
+  if (jasmine.Clock.installed.clearTimeout.apply) {
+    return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.clearTimeout(timeoutKey);
+  }
+};
+
+jasmine.getGlobal().clearInterval = function(timeoutKey) {
+  if (jasmine.Clock.installed.clearTimeout.apply) {
+    return jasmine.Clock.installed.clearInterval.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.clearInterval(timeoutKey);
+  }
+};
+
+/**
+ * @constructor
+ */
+jasmine.MultiReporter = function() {
+  this.subReporters_ = [];
+};
+jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
+
+jasmine.MultiReporter.prototype.addReporter = function(reporter) {
+  this.subReporters_.push(reporter);
+};
+
+(function() {
+  var functionNames = [
+    "reportRunnerStarting",
+    "reportRunnerResults",
+    "reportSuiteResults",
+    "reportSpecStarting",
+    "reportSpecResults",
+    "log"
+  ];
+  for (var i = 0; i < functionNames.length; i++) {
+    var functionName = functionNames[i];
+    jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
+      return function() {
+        for (var j = 0; j < this.subReporters_.length; j++) {
+          var subReporter = this.subReporters_[j];
+          if (subReporter[functionName]) {
+            subReporter[functionName].apply(subReporter, arguments);
+          }
+        }
+      };
+    })(functionName);
+  }
+})();
+/**
+ * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
+ *
+ * @constructor
+ */
+jasmine.NestedResults = function() {
+  /**
+   * The total count of results
+   */
+  this.totalCount = 0;
+  /**
+   * Number of passed results
+   */
+  this.passedCount = 0;
+  /**
+   * Number of failed results
+   */
+  this.failedCount = 0;
+  /**
+   * Was this suite/spec skipped?
+   */
+  this.skipped = false;
+  /**
+   * @ignore
+   */
+  this.items_ = [];
+};
+
+/**
+ * Roll up the result counts.
+ *
+ * @param result
+ */
+jasmine.NestedResults.prototype.rollupCounts = function(result) {
+  this.totalCount += result.totalCount;
+  this.passedCount += result.passedCount;
+  this.failedCount += result.failedCount;
+};
+
+/**
+ * Adds a log message.
+ * @param values Array of message parts which will be concatenated later.
+ */
+jasmine.NestedResults.prototype.log = function(values) {
+  this.items_.push(new jasmine.MessageResult(values));
+};
+
+/**
+ * Getter for the results: message & results.
+ */
+jasmine.NestedResults.prototype.getItems = function() {
+  return this.items_;
+};
+
+/**
+ * Adds a result, tracking counts (total, passed, & failed)
+ * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
+ */
+jasmine.NestedResults.prototype.addResult = function(result) {
+  if (result.type != 'log') {
+    if (result.items_) {
+      this.rollupCounts(result);
+    } else {
+      this.totalCount++;
+      if (result.passed()) {
+        this.passedCount++;
+      } else {
+        this.failedCount++;
+      }
+    }
+  }
+  this.items_.push(result);
+};
+
+/**
+ * @returns {Boolean} True if <b>everything</b> below passed
+ */
+jasmine.NestedResults.prototype.passed = function() {
+  return this.passedCount === this.totalCount;
+};
+/**
+ * Base class for pretty printing for expectation results.
+ */
+jasmine.PrettyPrinter = function() {
+  this.ppNestLevel_ = 0;
+};
+
+/**
+ * Formats a value in a nice, human-readable string.
+ *
+ * @param value
+ */
+jasmine.PrettyPrinter.prototype.format = function(value) {
+  if (this.ppNestLevel_ > 40) {
+    throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
+  }
+
+  this.ppNestLevel_++;
+  try {
+    if (value === jasmine.undefined) {
+      this.emitScalar('undefined');
+    } else if (value === null) {
+      this.emitScalar('null');
+    } else if (value === jasmine.getGlobal()) {
+      this.emitScalar('<global>');
+    } else if (value.jasmineToString) {
+      this.emitScalar(value.jasmineToString());
+    } else if (typeof value === 'string') {
+      this.emitString(value);
+    } else if (jasmine.isSpy(value)) {
+      this.emitScalar("spy on " + value.identity);
+    } else if (value instanceof RegExp) {
+      this.emitScalar(value.toString());
+    } else if (typeof value === 'function') {
+      this.emitScalar('Function');
+    } else if (typeof value.nodeType === 'number') {
+      this.emitScalar('HTMLNode');
+    } else if (value instanceof Date) {
+      this.emitScalar('Date(' + value + ')');
+    } else if (value.__Jasmine_been_here_before__) {
+      this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
+    } else if (jasmine.isArray_(value) || typeof value == 'object') {
+      value.__Jasmine_been_here_before__ = true;
+      if (jasmine.isArray_(value)) {
+        this.emitArray(value);
+      } else {
+        this.emitObject(value);
+      }
+      delete value.__Jasmine_been_here_before__;
+    } else {
+      this.emitScalar(value.toString());
+    }
+  } finally {
+    this.ppNestLevel_--;
+  }
+};
+
+jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
+  for (var property in obj) {
+    if (property == '__Jasmine_been_here_before__') continue;
+    fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && 
+                                         obj.__lookupGetter__(property) !== null) : false);
+  }
+};
+
+jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
+
+jasmine.StringPrettyPrinter = function() {
+  jasmine.PrettyPrinter.call(this);
+
+  this.string = '';
+};
+jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
+
+jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
+  this.append(value);
+};
+
+jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
+  this.append("'" + value + "'");
+};
+
+jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
+  this.append('[ ');
+  for (var i = 0; i < array.length; i++) {
+    if (i > 0) {
+      this.append(', ');
+    }
+    this.format(array[i]);
+  }
+  this.append(' ]');
+};
+
+jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
+  var self = this;
+  this.append('{ ');
+  var first = true;
+
+  this.iterateObject(obj, function(property, isGetter) {
+    if (first) {
+      first = false;
+    } else {
+      self.append(', ');
+    }
+
+    self.append(property);
+    self.append(' : ');
+    if (isGetter) {
+      self.append('<getter>');
+    } else {
+      self.format(obj[property]);
+    }
+  });
+
+  this.append(' }');
+};
+
+jasmine.StringPrettyPrinter.prototype.append = function(value) {
+  this.string += value;
+};
+jasmine.Queue = function(env) {
+  this.env = env;
+  this.blocks = [];
+  this.running = false;
+  this.index = 0;
+  this.offset = 0;
+  this.abort = false;
+};
+
+jasmine.Queue.prototype.addBefore = function(block) {
+  this.blocks.unshift(block);
+};
+
+jasmine.Queue.prototype.add = function(block) {
+  this.blocks.push(block);
+};
+
+jasmine.Queue.prototype.insertNext = function(block) {
+  this.blocks.splice((this.index + this.offset + 1), 0, block);
+  this.offset++;
+};
+
+jasmine.Queue.prototype.start = function(onComplete) {
+  this.running = true;
+  this.onComplete = onComplete;
+  this.next_();
+};
+
+jasmine.Queue.prototype.isRunning = function() {
+  return this.running;
+};
+
+jasmine.Queue.LOOP_DONT_RECURSE = true;
+
+jasmine.Queue.prototype.next_ = function() {
+  var self = this;
+  var goAgain = true;
+
+  while (goAgain) {
+    goAgain = false;
+    
+    if (self.index < self.blocks.length && !this.abort) {
+      var calledSynchronously = true;
+      var completedSynchronously = false;
+
+      var onComplete = function () {
+        if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
+          completedSynchronously = true;
+          return;
+        }
+
+        if (self.blocks[self.index].abort) {
+          self.abort = true;
+        }
+
+        self.offset = 0;
+        self.index++;
+
+        var now = new Date().getTime();
+        if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
+          self.env.lastUpdate = now;
+          self.env.setTimeout(function() {
+            self.next_();
+          }, 0);
+        } else {
+          if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
+            goAgain = true;
+          } else {
+            self.next_();
+          }
+        }
+      };
+      self.blocks[self.index].execute(onComplete);
+
+      calledSynchronously = false;
+      if (completedSynchronously) {
+        onComplete();
+      }
+      
+    } else {
+      self.running = false;
+      if (self.onComplete) {
+        self.onComplete();
+      }
+    }
+  }
+};
+
+jasmine.Queue.prototype.results = function() {
+  var results = new jasmine.NestedResults();
+  for (var i = 0; i < this.blocks.length; i++) {
+    if (this.blocks[i].results) {
+      results.addResult(this.blocks[i].results());
+    }
+  }
+  return results;
+};
+
+
+/**
+ * Runner
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ */
+jasmine.Runner = function(env) {
+  var self = this;
+  self.env = env;
+  self.queue = new jasmine.Queue(env);
+  self.before_ = [];
+  self.after_ = [];
+  self.suites_ = [];
+};
+
+jasmine.Runner.prototype.execute = function() {
+  var self = this;
+  if (self.env.reporter.reportRunnerStarting) {
+    self.env.reporter.reportRunnerStarting(this);
+  }
+  self.queue.start(function () {
+    self.finishCallback();
+  });
+};
+
+jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
+  beforeEachFunction.typeName = 'beforeEach';
+  this.before_.splice(0,0,beforeEachFunction);
+};
+
+jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
+  afterEachFunction.typeName = 'afterEach';
+  this.after_.splice(0,0,afterEachFunction);
+};
+
+
+jasmine.Runner.prototype.finishCallback = function() {
+  this.env.reporter.reportRunnerResults(this);
+};
+
+jasmine.Runner.prototype.addSuite = function(suite) {
+  this.suites_.push(suite);
+};
+
+jasmine.Runner.prototype.add = function(block) {
+  if (block instanceof jasmine.Suite) {
+    this.addSuite(block);
+  }
+  this.queue.add(block);
+};
+
+jasmine.Runner.prototype.specs = function () {
+  var suites = this.suites();
+  var specs = [];
+  for (var i = 0; i < suites.length; i++) {
+    specs = specs.concat(suites[i].specs());
+  }
+  return specs;
+};
+
+jasmine.Runner.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.Runner.prototype.topLevelSuites = function() {
+  var topLevelSuites = [];
+  for (var i = 0; i < this.suites_.length; i++) {
+    if (!this.suites_[i].parentSuite) {
+      topLevelSuites.push(this.suites_[i]);
+    }
+  }
+  return topLevelSuites;
+};
+
+jasmine.Runner.prototype.results = function() {
+  return this.queue.results();
+};
+/**
+ * Internal representation of a Jasmine specification, or test.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {jasmine.Suite} suite
+ * @param {String} description
+ */
+jasmine.Spec = function(env, suite, description) {
+  if (!env) {
+    throw new Error('jasmine.Env() required');
+  }
+  if (!suite) {
+    throw new Error('jasmine.Suite() required');
+  }
+  var spec = this;
+  spec.id = env.nextSpecId ? env.nextSpecId() : null;
+  spec.env = env;
+  spec.suite = suite;
+  spec.description = description;
+  spec.queue = new jasmine.Queue(env);
+
+  spec.afterCallbacks = [];
+  spec.spies_ = [];
+
+  spec.results_ = new jasmine.NestedResults();
+  spec.results_.description = description;
+  spec.matchersClass = null;
+};
+
+jasmine.Spec.prototype.getFullName = function() {
+  return this.suite.getFullName() + ' ' + this.description + '.';
+};
+
+
+jasmine.Spec.prototype.results = function() {
+  return this.results_;
+};
+
+/**
+ * All parameters are pretty-printed and concatenated together, then written to the spec's output.
+ *
+ * Be careful not to leave calls to <code>jasmine.log</code> in production code.
+ */
+jasmine.Spec.prototype.log = function() {
+  return this.results_.log(arguments);
+};
+
+jasmine.Spec.prototype.runs = function (func) {
+  var block = new jasmine.Block(this.env, func, this);
+  this.addToQueue(block);
+  return this;
+};
+
+jasmine.Spec.prototype.addToQueue = function (block) {
+  if (this.queue.isRunning()) {
+    this.queue.insertNext(block);
+  } else {
+    this.queue.add(block);
+  }
+};
+
+/**
+ * @param {jasmine.ExpectationResult} result
+ */
+jasmine.Spec.prototype.addMatcherResult = function(result) {
+  this.results_.addResult(result);
+};
+
+jasmine.Spec.prototype.expect = function(actual) {
+  var positive = new (this.getMatchersClass_())(this.env, actual, this);
+  positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
+  return positive;
+};
+
+/**
+ * Waits a fixed time period before moving to the next block.
+ *
+ * @deprecated Use waitsFor() instead
+ * @param {Number} timeout milliseconds to wait
+ */
+jasmine.Spec.prototype.waits = function(timeout) {
+  var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
+  this.addToQueue(waitsFunc);
+  return this;
+};
+
+/**
+ * Waits for the latchFunction to return true before proceeding to the next block.
+ *
+ * @param {Function} latchFunction
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
+  var latchFunction_ = null;
+  var optional_timeoutMessage_ = null;
+  var optional_timeout_ = null;
+
+  for (var i = 0; i < arguments.length; i++) {
+    var arg = arguments[i];
+    switch (typeof arg) {
+      case 'function':
+        latchFunction_ = arg;
+        break;
+      case 'string':
+        optional_timeoutMessage_ = arg;
+        break;
+      case 'number':
+        optional_timeout_ = arg;
+        break;
+    }
+  }
+
+  var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
+  this.addToQueue(waitsForFunc);
+  return this;
+};
+
+jasmine.Spec.prototype.fail = function (e) {
+  var expectationResult = new jasmine.ExpectationResult({
+    passed: false,
+    message: e ? jasmine.util.formatException(e) : 'Exception',
+    trace: { stack: e.stack }
+  });
+  this.results_.addResult(expectationResult);
+};
+
+jasmine.Spec.prototype.getMatchersClass_ = function() {
+  return this.matchersClass || this.env.matchersClass;
+};
+
+jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
+  var parent = this.getMatchersClass_();
+  var newMatchersClass = function() {
+    parent.apply(this, arguments);
+  };
+  jasmine.util.inherit(newMatchersClass, parent);
+  jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
+  this.matchersClass = newMatchersClass;
+};
+
+jasmine.Spec.prototype.finishCallback = function() {
+  this.env.reporter.reportSpecResults(this);
+};
+
+jasmine.Spec.prototype.finish = function(onComplete) {
+  this.removeAllSpies();
+  this.finishCallback();
+  if (onComplete) {
+    onComplete();
+  }
+};
+
+jasmine.Spec.prototype.after = function(doAfter) {
+  if (this.queue.isRunning()) {
+    this.queue.add(new jasmine.Block(this.env, doAfter, this));
+  } else {
+    this.afterCallbacks.unshift(doAfter);
+  }
+};
+
+jasmine.Spec.prototype.execute = function(onComplete) {
+  var spec = this;
+  if (!spec.env.specFilter(spec)) {
+    spec.results_.skipped = true;
+    spec.finish(onComplete);
+    return;
+  }
+
+  this.env.reporter.reportSpecStarting(this);
+
+  spec.env.currentSpec = spec;
+
+  spec.addBeforesAndAftersToQueue();
+
+  spec.queue.start(function () {
+    spec.finish(onComplete);
+  });
+};
+
+jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
+  var runner = this.env.currentRunner();
+  var i;
+
+  for (var suite = this.suite; suite; suite = suite.parentSuite) {
+    for (i = 0; i < suite.before_.length; i++) {
+      this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
+    }
+  }
+  for (i = 0; i < runner.before_.length; i++) {
+    this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
+  }
+  for (i = 0; i < this.afterCallbacks.length; i++) {
+    this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
+  }
+  for (suite = this.suite; suite; suite = suite.parentSuite) {
+    for (i = 0; i < suite.after_.length; i++) {
+      this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
+    }
+  }
+  for (i = 0; i < runner.after_.length; i++) {
+    this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
+  }
+};
+
+jasmine.Spec.prototype.explodes = function() {
+  throw 'explodes function should not have been called';
+};
+
+jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
+  if (obj == jasmine.undefined) {
+    throw "spyOn could not find an object to spy upon for " + methodName + "()";
+  }
+
+  if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
+    throw methodName + '() method does not exist';
+  }
+
+  if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
+    throw new Error(methodName + ' has already been spied upon');
+  }
+
+  var spyObj = jasmine.createSpy(methodName);
+
+  this.spies_.push(spyObj);
+  spyObj.baseObj = obj;
+  spyObj.methodName = methodName;
+  spyObj.originalValue = obj[methodName];
+
+  obj[methodName] = spyObj;
+
+  return spyObj;
+};
+
+jasmine.Spec.prototype.removeAllSpies = function() {
+  for (var i = 0; i < this.spies_.length; i++) {
+    var spy = this.spies_[i];
+    spy.baseObj[spy.methodName] = spy.originalValue;
+  }
+  this.spies_ = [];
+};
+
+/**
+ * Internal representation of a Jasmine suite.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {String} description
+ * @param {Function} specDefinitions
+ * @param {jasmine.Suite} parentSuite
+ */
+jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
+  var self = this;
+  self.id = env.nextSuiteId ? env.nextSuiteId() : null;
+  self.description = description;
+  self.queue = new jasmine.Queue(env);
+  self.parentSuite = parentSuite;
+  self.env = env;
+  self.before_ = [];
+  self.after_ = [];
+  self.children_ = [];
+  self.suites_ = [];
+  self.specs_ = [];
+};
+
+jasmine.Suite.prototype.getFullName = function() {
+  var fullName = this.description;
+  for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
+    fullName = parentSuite.description + ' ' + fullName;
+  }
+  return fullName;
+};
+
+jasmine.Suite.prototype.finish = function(onComplete) {
+  this.env.reporter.reportSuiteResults(this);
+  this.finished = true;
+  if (typeof(onComplete) == 'function') {
+    onComplete();
+  }
+};
+
+jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
+  beforeEachFunction.typeName = 'beforeEach';
+  this.before_.unshift(beforeEachFunction);
+};
+
+jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
+  afterEachFunction.typeName = 'afterEach';
+  this.after_.unshift(afterEachFunction);
+};
+
+jasmine.Suite.prototype.results = function() {
+  return this.queue.results();
+};
+
+jasmine.Suite.prototype.add = function(suiteOrSpec) {
+  this.children_.push(suiteOrSpec);
+  if (suiteOrSpec instanceof jasmine.Suite) {
+    this.suites_.push(suiteOrSpec);
+    this.env.currentRunner().addSuite(suiteOrSpec);
+  } else {
+    this.specs_.push(suiteOrSpec);
+  }
+  this.queue.add(suiteOrSpec);
+};
+
+jasmine.Suite.prototype.specs = function() {
+  return this.specs_;
+};
+
+jasmine.Suite.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.Suite.prototype.children = function() {
+  return this.children_;
+};
+
+jasmine.Suite.prototype.execute = function(onComplete) {
+  var self = this;
+  this.queue.start(function () {
+    self.finish(onComplete);
+  });
+};
+jasmine.WaitsBlock = function(env, timeout, spec) {
+  this.timeout = timeout;
+  jasmine.Block.call(this, env, null, spec);
+};
+
+jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
+
+jasmine.WaitsBlock.prototype.execute = function (onComplete) {
+  if (jasmine.VERBOSE) {
+    this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
+  }
+  this.env.setTimeout(function () {
+    onComplete();
+  }, this.timeout);
+};
+/**
+ * A block which waits for some condition to become true, with timeout.
+ *
+ * @constructor
+ * @extends jasmine.Block
+ * @param {jasmine.Env} env The Jasmine environment.
+ * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
+ * @param {Function} latchFunction A function which returns true when the desired condition has been met.
+ * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
+ * @param {jasmine.Spec} spec The Jasmine spec.
+ */
+jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
+  this.timeout = timeout || env.defaultTimeoutInterval;
+  this.latchFunction = latchFunction;
+  this.message = message;
+  this.totalTimeSpentWaitingForLatch = 0;
+  jasmine.Block.call(this, env, null, spec);
+};
+jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
+
+jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
+
+jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
+  if (jasmine.VERBOSE) {
+    this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
+  }
+  var latchFunctionResult;
+  try {
+    latchFunctionResult = this.latchFunction.apply(this.spec);
+  } catch (e) {
+    this.spec.fail(e);
+    onComplete();
+    return;
+  }
+
+  if (latchFunctionResult) {
+    onComplete();
+  } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
+    var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
+    this.spec.fail({
+      name: 'timeout',
+      message: message
+    });
+
+    this.abort = true;
+    onComplete();
+  } else {
+    this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
+    var self = this;
+    this.env.setTimeout(function() {
+      self.execute(onComplete);
+    }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
+  }
+};
+
+jasmine.version_= {
+  "major": 1,
+  "minor": 2,
+  "build": 0,
+  "revision": 1337005947
+};


[32/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/Compass.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/Compass.cs b/lib/cordova-wp7/templates/standalone/Plugins/Compass.cs
new file mode 100644
index 0000000..c9e1c4d
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/Compass.cs
@@ -0,0 +1,362 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using DeviceCompass = Microsoft.Devices.Sensors.Compass;
+using System.Windows.Threading;
+using System.Runtime.Serialization;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Threading;
+using Microsoft.Devices.Sensors;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+
+    public class Compass : BaseCommand
+    {
+        #region Static members
+
+        /// <summary>
+        /// Status of listener
+        /// </summary>
+        private static int currentStatus;
+
+        /// <summary>
+        /// Id for get getCompass method
+        /// </summary>
+        private static string getCompassId = "getCompassId";
+
+        /// <summary>
+        /// Compass
+        /// </summary>
+        private static DeviceCompass compass = new DeviceCompass();
+
+        /// <summary>
+        /// Listeners for callbacks
+        /// </summary>
+        private static Dictionary<string, Compass> watchers = new Dictionary<string, Compass>();
+
+        #endregion
+
+        #region Status codes
+
+        public const int Stopped = 0;
+        public const int Starting = 1;
+        public const int Running = 2;
+        public const int ErrorFailedToStart = 4;
+        public const int Not_Supported = 20;
+
+        /*
+         *   // Capture error codes
+            CompassError.COMPASS_INTERNAL_ERR = 0;
+            CompassError.COMPASS_NOT_SUPPORTED = 20;
+         * */
+
+        #endregion
+
+        #region CompassOptions class
+        /// <summary>
+        /// Represents Accelerometer options.
+        /// </summary>
+        [DataContract]
+        public class CompassOptions
+        {
+            /// <summary>
+            /// How often to retrieve the Acceleration in milliseconds
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "frequency")]
+            public int Frequency { get; set; }
+
+            /// <summary>
+            /// The change in degrees required to initiate a watchHeadingFilter success callback.
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "filter")]
+            public int Filter { get; set; }
+
+            /// <summary>
+            /// Watcher id
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "id")]
+            public string Id { get; set; }
+
+        }
+        #endregion
+
+
+        /// <summary>
+        /// Time the value was last changed
+        /// </summary>
+        //private DateTime lastValueChangedTime;
+
+        /// <summary>
+        /// Accelerometer options
+        /// </summary>
+        private CompassOptions compassOptions;
+
+        //bool isDataValid;
+
+        //bool calibrating = false;
+
+        public Compass()
+        {
+
+        }
+
+        /// <summary>
+        /// Formats current coordinates into JSON format
+        /// </summary>
+        /// <returns>Coordinates in JSON format</returns>
+        private string GetHeadingFormatted(CompassReading reading)
+        {   
+            // NOTE: timestamp is generated on the JS side, to avoid issues with format conversions
+            string result = String.Format("\"magneticHeading\":{0},\"headingAccuracy\":{1},\"trueHeading\":{2}",
+                            reading.MagneticHeading.ToString("0.0", CultureInfo.InvariantCulture),
+                            reading.HeadingAccuracy.ToString("0.0", CultureInfo.InvariantCulture),
+                            reading.TrueHeading.ToString("0.0", CultureInfo.InvariantCulture));
+            return "{" + result + "}";
+        }
+
+        public void getHeading(string options)
+        {
+            if (!DeviceCompass.IsSupported)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "{code:" + Not_Supported + "}"));
+            }
+            else
+            {
+                //if (compass == null)
+                //{
+                //    // Instantiate the compass.
+                //    compass = new DeviceCompass();
+                //    compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(40);
+                //    compass.CurrentValueChanged += new EventHandler<Microsoft.Devices.Sensors.SensorReadingEventArgs<Microsoft.Devices.Sensors.CompassReading>>(compass_CurrentValueChanged);
+                //    compass.Calibrate += new EventHandler<Microsoft.Devices.Sensors.CalibrationEventArgs>(compass_Calibrate);
+                //}
+
+
+                //compass.Start();
+
+            }
+
+            try
+            {
+                if (currentStatus != Running)
+                {
+                    lock (compass)
+                    {
+                        compass.CurrentValueChanged += compass_SingleHeadingValueChanged;
+                        compass.Start();
+                        this.SetStatus(Starting);
+                    }
+
+                    long timeout = 2000;
+                    while ((currentStatus == Starting) && (timeout > 0))
+                    {
+                        timeout = timeout - 100;
+                        Thread.Sleep(100);
+                    }
+
+                    if (currentStatus != Running)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, ErrorFailedToStart));
+                        return;
+                    }
+                }
+                lock (compass)
+                {
+                    compass.CurrentValueChanged -= compass_SingleHeadingValueChanged;
+                    if (watchers.Count < 1)
+                    {
+                        compass.Stop();
+                        this.SetStatus(Stopped);
+                    }
+                }
+            }
+            catch (UnauthorizedAccessException)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION, ErrorFailedToStart));
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ErrorFailedToStart));
+            }
+        }
+
+        void compass_SingleHeadingValueChanged(object sender, Microsoft.Devices.Sensors.SensorReadingEventArgs<CompassReading> e)
+        {
+            this.SetStatus(Running);
+            if (compass.IsDataValid)
+            {
+                // trueHeading :: The heading in degrees from 0 - 359.99 at a single moment in time.
+                //  magneticHeading:: The heading relative to the geographic North Pole in degrees 0 - 359.99 at a single moment in time. 
+                //  A negative value indicates that the true heading could not be determined.
+                // headingAccuracy :: The deviation in degrees between the reported heading and the true heading.
+                //rawMagnetometerReading = e.SensorReading.MagnetometerReading;
+
+                //Debug.WriteLine("Compass Result :: " + GetHeadingFormatted(e.SensorReading));
+
+                PluginResult result = new PluginResult(PluginResult.Status.OK, GetHeadingFormatted(e.SensorReading));
+
+                DispatchCommandResult(result);
+            }
+        }
+
+        /// <summary>
+        /// Starts listening for compass sensor
+        /// </summary>
+        /// <returns>status of listener</returns>
+        private int start()
+        {
+            if ((currentStatus == Running) || (currentStatus == Starting))
+            {
+                return currentStatus;
+            }
+            try
+            {
+                lock (compass)
+                {
+                    watchers.Add(getCompassId, this);
+                    compass.CurrentValueChanged += watchers[getCompassId].compass_CurrentValueChanged;
+                    compass.Start();
+                    this.SetStatus(Starting);
+                }
+            }
+            catch (Exception)
+            {
+                this.SetStatus(ErrorFailedToStart);
+            }
+            return currentStatus;
+        }
+
+        public void startWatch(string options)
+        {
+            if (!DeviceCompass.IsSupported)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, Not_Supported));
+            }
+
+            try
+            {
+                compassOptions = JSON.JsonHelper.Deserialize<CompassOptions>(options);
+            }
+            catch (Exception ex)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                return;
+            }
+
+            if (string.IsNullOrEmpty(compassOptions.Id))
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            try
+            {
+                lock (compass)
+                {
+                    watchers.Add(compassOptions.Id, this);
+                    compass.CurrentValueChanged += watchers[compassOptions.Id].compass_CurrentValueChanged;
+                    compass.Start();
+                    this.SetStatus(Starting);
+                }
+            }
+            catch (Exception)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ErrorFailedToStart));
+                return;
+            }
+        }
+
+        public void stopWatch(string options)
+        {
+            try
+            {
+                compassOptions = JSON.JsonHelper.Deserialize<CompassOptions>(options);
+            }
+            catch (Exception ex)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                return;
+            }
+
+            if (string.IsNullOrEmpty(compassOptions.Id))
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+
+            if (currentStatus != Stopped)
+            {
+                lock (compass)
+                {
+                    Compass watcher = watchers[compassOptions.Id];
+                    compass.CurrentValueChanged -= watcher.compass_CurrentValueChanged;
+                    watchers.Remove(compassOptions.Id);
+                    watcher.Dispose();
+                }
+            }
+            this.SetStatus(Stopped);
+
+            this.DispatchCommandResult();
+        }
+
+        void compass_Calibrate(object sender, Microsoft.Devices.Sensors.CalibrationEventArgs e)
+        {
+            //throw new NotImplementedException();
+            // TODO: pass calibration error to JS
+        }
+
+        void compass_CurrentValueChanged(object sender, Microsoft.Devices.Sensors.SensorReadingEventArgs<CompassReading> e)
+        {
+            this.SetStatus(Running);
+            if (compass.IsDataValid)
+            {
+                // trueHeading :: The heading in degrees from 0 - 359.99 at a single moment in time.
+                //  magneticHeading:: The heading relative to the geographic North Pole in degrees 0 - 359.99 at a single moment in time. 
+                //  A negative value indicates that the true heading could not be determined.
+                // headingAccuracy :: The deviation in degrees between the reported heading and the true heading.
+                //rawMagnetometerReading = e.SensorReading.MagnetometerReading;
+
+                //Debug.WriteLine("Compass Result :: " + GetHeadingFormatted(e.SensorReading));
+
+                PluginResult result = new PluginResult(PluginResult.Status.OK, GetHeadingFormatted(e.SensorReading));
+                result.KeepCallback = true;
+
+                DispatchCommandResult(result);
+            }
+        }
+
+        /// <summary>
+        /// Sets current status
+        /// </summary>
+        /// <param name="status">current status</param>
+        private void SetStatus(int status)
+        {
+            currentStatus = status;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/Contacts.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/Contacts.cs b/lib/cordova-wp7/templates/standalone/Plugins/Contacts.cs
new file mode 100644
index 0000000..6789bb8
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/Contacts.cs
@@ -0,0 +1,664 @@
+/*  
+	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.
+*/
+
+using Microsoft.Phone.Tasks;
+using Microsoft.Phone.UserData;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Windows;
+using DeviceContacts = Microsoft.Phone.UserData.Contacts;
+
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    [DataContract]
+    public class SearchOptions
+    {
+        [DataMember]
+        public string filter { get; set; }
+        [DataMember]
+        public bool multiple { get; set; }
+    }
+
+    [DataContract]
+    public class ContactSearchParams
+    {
+        [DataMember]
+        public string[] fields { get; set; }
+        [DataMember]
+        public SearchOptions options { get; set; }
+    }
+
+    [DataContract]
+    public class JSONContactAddress
+    {
+        [DataMember]
+        public string formatted { get; set; }
+        [DataMember]
+        public string type { get; set; }
+        [DataMember]
+        public string streetAddress { get; set; }
+        [DataMember]
+        public string locality { get; set; }
+        [DataMember]
+        public string region { get; set; }
+        [DataMember]
+        public string postalCode { get; set; }
+        [DataMember]
+        public string country { get; set; }
+        [DataMember]
+        public bool pref { get; set; }
+    }
+
+    [DataContract]
+    public class JSONContactName
+    {
+        [DataMember]
+        public string formatted { get; set; }
+        [DataMember]
+        public string familyName { get; set; }
+        [DataMember]
+        public string givenName { get; set; }
+        [DataMember]
+        public string middleName { get; set; }
+        [DataMember]
+        public string honorificPrefix { get; set; }
+        [DataMember]
+        public string honorificSuffix { get; set; }
+    }
+
+    [DataContract]
+    public class JSONContactField
+    {
+        [DataMember]
+        public string type { get; set; }
+        [DataMember]
+        public string value { get; set; }
+        [DataMember]
+        public bool pref { get; set; }
+    }
+
+    [DataContract]
+    public class JSONContactOrganization
+    {
+        [DataMember]
+        public string type { get; set; }
+        [DataMember]
+        public string name { get; set; }
+        [DataMember]
+        public bool pref { get; set; }
+        [DataMember]
+        public string department { get; set; }
+        [DataMember]
+        public string title { get; set; }
+    }
+
+    [DataContract]
+    public class JSONContact
+    {
+        [DataMember]
+        public string id { get; set; }
+        [DataMember]
+        public string rawId { get; set; }
+        [DataMember]
+        public string displayName { get; set; }
+        [DataMember]
+        public string nickname { get; set; }
+        [DataMember]
+        public string note { get; set; }
+
+        [DataMember]
+        public JSONContactName name { get; set; }
+
+        [DataMember]
+        public JSONContactField[] emails { get; set; }
+
+        [DataMember]
+        public JSONContactField[] phoneNumbers { get; set; }
+
+        [DataMember]
+        public JSONContactField[] ims { get; set; }
+
+        [DataMember]
+        public JSONContactField[] photos { get; set; }
+
+        [DataMember]
+        public JSONContactField[] categories { get; set; }
+
+        [DataMember]
+        public JSONContactField[] urls { get; set; }
+
+        [DataMember]
+        public JSONContactOrganization[] organizations { get; set; }
+
+        [DataMember]
+        public JSONContactAddress[] addresses { get; set; }
+    }
+
+
+    public class Contacts : BaseCommand
+    {
+
+        public const int UNKNOWN_ERROR = 0;
+        public const int INVALID_ARGUMENT_ERROR = 1;
+        public const int TIMEOUT_ERROR = 2;
+        public const int PENDING_OPERATION_ERROR = 3;
+        public const int IO_ERROR = 4;
+        public const int NOT_SUPPORTED_ERROR = 5;
+        public const int PERMISSION_DENIED_ERROR = 20;
+        public const int SYNTAX_ERR = 8;
+
+        public Contacts()
+        {
+
+        }
+
+        // refer here for contact properties we can access: http://msdn.microsoft.com/en-us/library/microsoft.phone.tasks.savecontacttask_members%28v=VS.92%29.aspx
+        public void save(string jsonContact)
+        {
+
+            // jsonContact is actually an array of 1 {contact}
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(jsonContact);
+
+
+            JSONContact contact = JSON.JsonHelper.Deserialize<JSONContact>(args[0]);
+
+            SaveContactTask contactTask = new SaveContactTask();
+
+            if (contact.nickname != null)
+            {
+                contactTask.Nickname = contact.nickname;
+            }
+            if (contact.urls != null && contact.urls.Length > 0)
+            {
+                contactTask.Website = contact.urls[0].value;
+            }
+            if (contact.note != null)
+            {
+                contactTask.Notes = contact.note;
+            }
+
+            #region contact.name
+            if (contact.name != null)
+            {
+                if (contact.name.givenName != null)
+                    contactTask.FirstName = contact.name.givenName;
+                if (contact.name.familyName != null)
+                    contactTask.LastName = contact.name.familyName;
+                if (contact.name.middleName != null)
+                    contactTask.MiddleName = contact.name.middleName;
+                if (contact.name.honorificSuffix != null)
+                    contactTask.Suffix = contact.name.honorificSuffix;
+                if (contact.name.honorificPrefix != null)
+                    contactTask.Title = contact.name.honorificPrefix;
+            }
+            #endregion
+
+            #region contact.org
+            if (contact.organizations != null && contact.organizations.Count() > 0)
+            {
+                contactTask.Company = contact.organizations[0].name;
+                contactTask.JobTitle = contact.organizations[0].title;
+            }
+            #endregion
+
+            #region contact.phoneNumbers
+            if (contact.phoneNumbers != null && contact.phoneNumbers.Length > 0)
+            {
+                foreach (JSONContactField field in contact.phoneNumbers)
+                {
+                    string fieldType = field.type.ToLower();
+                    if (fieldType == "work")
+                    {
+                        contactTask.WorkPhone = field.value;
+                    }
+                    else if (fieldType == "home")
+                    {
+                        contactTask.HomePhone = field.value;
+                    }
+                    else if (fieldType == "mobile")
+                    {
+                        contactTask.MobilePhone = field.value;
+                    }
+                }
+            }
+            #endregion
+
+            #region contact.emails
+
+            if (contact.emails != null && contact.emails.Length > 0)
+            {
+
+                // set up different email types if they are not explicitly defined
+                foreach (string type in new string[] { "personal", "work", "other" })
+                {
+                    foreach (JSONContactField field in contact.emails)
+                    {
+                        if (field != null && String.IsNullOrEmpty(field.type))
+                        {
+                            field.type = type;
+                            break;
+                        }
+                    }
+                }
+
+                foreach (JSONContactField field in contact.emails)
+                {
+                    if (field != null)
+                    {
+                        if (field.type != null && field.type != "other")
+                        {
+                            string fieldType = field.type.ToLower();
+                            if (fieldType == "work")
+                            {
+                                contactTask.WorkEmail = field.value;
+                            }
+                            else if (fieldType == "home" || fieldType == "personal")
+                            {
+                                contactTask.PersonalEmail = field.value;
+                            }
+                        }
+                        else
+                        {
+                            contactTask.OtherEmail = field.value;
+                        }
+                    }
+
+                }
+            }
+            #endregion
+
+            if (contact.note != null && contact.note.Length > 0)
+            {
+                contactTask.Notes = contact.note;
+            }
+
+            #region contact.addresses
+            if (contact.addresses != null && contact.addresses.Length > 0)
+            {
+                foreach (JSONContactAddress address in contact.addresses)
+                {
+                    if (address.type == null)
+                    {
+                        address.type = "home"; // set a default
+                    }
+                    string fieldType = address.type.ToLower();
+                    if (fieldType == "work")
+                    {
+                        contactTask.WorkAddressCity = address.locality;
+                        contactTask.WorkAddressCountry = address.country;
+                        contactTask.WorkAddressState = address.region;
+                        contactTask.WorkAddressStreet = address.streetAddress;
+                        contactTask.WorkAddressZipCode = address.postalCode;
+                    }
+                    else if (fieldType == "home" || fieldType == "personal")
+                    {
+                        contactTask.HomeAddressCity = address.locality;
+                        contactTask.HomeAddressCountry = address.country;
+                        contactTask.HomeAddressState = address.region;
+                        contactTask.HomeAddressStreet = address.streetAddress;
+                        contactTask.HomeAddressZipCode = address.postalCode;
+                    }
+                    else
+                    {
+                        // no other address fields available ...
+                        Debug.WriteLine("Creating contact with unsupported address type :: " + address.type);
+                    }
+                }
+            }
+            #endregion
+
+
+            contactTask.Completed += new EventHandler<SaveContactResult>(ContactSaveTaskCompleted);
+            contactTask.Show();
+        }
+
+        void ContactSaveTaskCompleted(object sender, SaveContactResult e)
+        {
+            SaveContactTask task = sender as SaveContactTask;
+
+            if (e.TaskResult == TaskResult.OK)
+            {
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    DeviceContacts deviceContacts = new DeviceContacts();
+                    deviceContacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(postAdd_SearchCompleted);
+
+                    string displayName = String.Format("{0}{2}{1}", task.FirstName, task.LastName, String.IsNullOrEmpty(task.FirstName) ? "" : " ");
+
+                    deviceContacts.SearchAsync(displayName, FilterKind.DisplayName, task);
+                });
+
+
+            }
+            else if (e.TaskResult == TaskResult.Cancel)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Operation cancelled."));
+            }
+        }
+
+        void postAdd_SearchCompleted(object sender, ContactsSearchEventArgs e)
+        {
+            if (e.Results.Count() > 0)
+            {
+                List<Contact> foundContacts = new List<Contact>();
+
+                int n = (from Contact contact in e.Results select contact.GetHashCode()).Max();
+                Contact newContact = (from Contact contact in e.Results
+                                      where contact.GetHashCode() == n
+                                      select contact).First();
+
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, FormatJSONContact(newContact, null)));
+            }
+            else
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
+            }
+        }
+
+
+
+        public void remove(string id)
+        {
+            // note id is wrapped in [] and always has exactly one string ...
+            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "{\"code\":" + NOT_SUPPORTED_ERROR + "}"));
+        }
+
+        public void search(string searchCriteria)
+        {
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(searchCriteria);
+
+            ContactSearchParams searchParams = new ContactSearchParams();
+            try
+            {
+                searchParams.fields = JSON.JsonHelper.Deserialize<string[]>(args[0]);
+                searchParams.options = JSON.JsonHelper.Deserialize<SearchOptions>(args[1]);
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_ARGUMENT_ERROR));
+                return;
+            }
+
+            if (searchParams.options == null)
+            {
+                searchParams.options = new SearchOptions();
+                searchParams.options.filter = "";
+                searchParams.options.multiple = true;
+            }
+
+            DeviceContacts deviceContacts = new DeviceContacts();
+            deviceContacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(contacts_SearchCompleted);
+
+            // default is to search all fields
+            FilterKind filterKind = FilterKind.None;
+            // if only one field is specified, we will try the 3 available DeviceContact search filters
+            if (searchParams.fields.Count() == 1)
+            {
+                if (searchParams.fields.Contains("name"))
+                {
+                    filterKind = FilterKind.DisplayName;
+                }
+                else if (searchParams.fields.Contains("emails"))
+                {
+                    filterKind = FilterKind.EmailAddress;
+                }
+                else if (searchParams.fields.Contains("phoneNumbers"))
+                {
+                    filterKind = FilterKind.PhoneNumber;
+                }
+            }
+
+            try
+            {
+
+                deviceContacts.SearchAsync(searchParams.options.filter, filterKind, searchParams);
+            }
+            catch (Exception ex)
+            {
+                Debug.WriteLine("search contacts exception :: " + ex.Message);
+            }
+        }
+
+        private void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
+        {
+            ContactSearchParams searchParams = (ContactSearchParams)e.State;
+
+            List<Contact> foundContacts = null;
+
+            // if we have multiple search fields
+            if (searchParams.options.filter.Length > 0 && searchParams.fields.Count() > 1)
+            {
+                foundContacts = new List<Contact>();
+                if (searchParams.fields.Contains("emails"))
+                {
+                    foundContacts.AddRange(from Contact con in e.Results
+                                           from ContactEmailAddress a in con.EmailAddresses
+                                           where a.EmailAddress.Contains(searchParams.options.filter)
+                                           select con);
+                }
+                if (searchParams.fields.Contains("displayName"))
+                {
+                    foundContacts.AddRange(from Contact con in e.Results
+                                           where con.DisplayName.Contains(searchParams.options.filter)
+                                           select con);
+                }
+                if (searchParams.fields.Contains("name"))
+                {
+                    foundContacts.AddRange(from Contact con in e.Results
+                                           where con.CompleteName != null && con.CompleteName.ToString().Contains(searchParams.options.filter)
+                                           select con);
+                }
+                if (searchParams.fields.Contains("phoneNumbers"))
+                {
+                    foundContacts.AddRange(from Contact con in e.Results
+                                           from ContactPhoneNumber a in con.PhoneNumbers
+                                           where a.PhoneNumber.Contains(searchParams.options.filter)
+                                           select con);
+                }
+                if (searchParams.fields.Contains("urls"))
+                {
+                    foundContacts.AddRange(from Contact con in e.Results
+                                           from string a in con.Websites
+                                           where a.Contains(searchParams.options.filter)
+                                           select con);
+                }
+            }
+            else
+            {
+                foundContacts = new List<Contact>(e.Results);
+            }
+
+            //List<string> contactList = new List<string>();
+
+            string strResult = "";
+
+            IEnumerable<Contact> distinctContacts = foundContacts.Distinct();
+
+            foreach (Contact contact in distinctContacts)
+            {
+                strResult += FormatJSONContact(contact, null) + ",";
+                //contactList.Add(FormatJSONContact(contact, null));
+                if (!searchParams.options.multiple)
+                {
+                    break; // just return the first item
+                }
+            }
+            PluginResult result = new PluginResult(PluginResult.Status.OK);
+            result.Message = "[" + strResult.TrimEnd(',') + "]";
+            DispatchCommandResult(result);
+
+        }
+
+        private string FormatJSONPhoneNumbers(Contact con)
+        {
+            string retVal = "";
+            string contactFieldFormat = "\"type\":\"{0}\",\"value\":\"{1}\",\"pref\":\"false\"";
+            foreach (ContactPhoneNumber number in con.PhoneNumbers)
+            {
+
+                string contactField = string.Format(contactFieldFormat,
+                                                    number.Kind.ToString(),
+                                                    number.PhoneNumber);
+
+                retVal += "{" + contactField + "},";
+            }
+            return retVal.TrimEnd(',');
+        }
+
+        private string FormatJSONEmails(Contact con)
+        {
+            string retVal = "";
+            string contactFieldFormat = "\"type\":\"{0}\",\"value\":\"{1}\",\"pref\":\"false\"";
+            foreach (ContactEmailAddress address in con.EmailAddresses)
+            {
+                string contactField = string.Format(contactFieldFormat,
+                                                    address.Kind.ToString(),
+                                                    address.EmailAddress);
+
+                retVal += "{" + contactField + "},";
+            }
+            return retVal.TrimEnd(',');
+        }
+
+        private string getFormattedJSONAddress(ContactAddress address, bool isPreferred)
+        {
+
+            string addressFormatString = "\"pref\":{0}," + // bool
+                          "\"type\":\"{1}\"," +
+                          "\"formatted\":\"{2}\"," +
+                          "\"streetAddress\":\"{3}\"," +
+                          "\"locality\":\"{4}\"," +
+                          "\"region\":\"{5}\"," +
+                          "\"postalCode\":\"{6}\"," +
+                          "\"country\":\"{7}\"";
+
+            string formattedAddress = address.PhysicalAddress.AddressLine1 + " "
+                                    + address.PhysicalAddress.AddressLine2 + " "
+                                    + address.PhysicalAddress.City + " "
+                                    + address.PhysicalAddress.StateProvince + " "
+                                    + address.PhysicalAddress.CountryRegion + " "
+                                    + address.PhysicalAddress.PostalCode;
+
+            string jsonAddress = string.Format(addressFormatString,
+                                               isPreferred ? "\"true\"" : "\"false\"",
+                                               address.Kind.ToString(),
+                                               formattedAddress,
+                                               address.PhysicalAddress.AddressLine1 + " " + address.PhysicalAddress.AddressLine2,
+                                               address.PhysicalAddress.City,
+                                               address.PhysicalAddress.StateProvince,
+                                               address.PhysicalAddress.PostalCode,
+                                               address.PhysicalAddress.CountryRegion);
+
+            //Debug.WriteLine("getFormattedJSONAddress returning :: " + jsonAddress);
+
+            return "{" + jsonAddress + "}";
+        }
+
+        private string FormatJSONAddresses(Contact con)
+        {
+            string retVal = "";
+            foreach (ContactAddress address in con.Addresses)
+            {
+                retVal += this.getFormattedJSONAddress(address, false) + ",";
+            }
+
+            //Debug.WriteLine("FormatJSONAddresses returning :: " + retVal);
+            return retVal.TrimEnd(',');
+        }
+
+        private string FormatJSONWebsites(Contact con)
+        {
+            string retVal = "";
+            foreach (string website in con.Websites)
+            {
+                retVal += "\"" + website + "\",";
+            }
+            return retVal.TrimEnd(',');
+        }
+
+        /*
+         *  formatted: The complete name of the contact. (DOMString)
+            familyName: The contacts family name. (DOMString)
+            givenName: The contacts given name. (DOMString)
+            middleName: The contacts middle name. (DOMString)
+            honorificPrefix: The contacts prefix (example Mr. or Dr.) (DOMString)
+            honorificSuffix: The contacts suffix (example Esq.). (DOMString)
+         */
+        private string FormatJSONName(Contact con)
+        {
+            string retVal = "";
+            string formatStr = "\"formatted\":\"{0}\"," +
+                                "\"familyName\":\"{1}\"," +
+                                "\"givenName\":\"{2}\"," +
+                                "\"middleName\":\"{3}\"," +
+                                "\"honorificPrefix\":\"{4}\"," +
+                                "\"honorificSuffix\":\"{5}\"";
+
+            if (con.CompleteName != null)
+            {
+                retVal = string.Format(formatStr,
+                                   con.CompleteName.FirstName + " " + con.CompleteName.LastName, // TODO: does this need suffix? middlename?
+                                   con.CompleteName.LastName,
+                                   con.CompleteName.FirstName,
+                                   con.CompleteName.MiddleName,
+                                   con.CompleteName.Title,
+                                   con.CompleteName.Suffix);
+            }
+            else
+            {
+                retVal = string.Format(formatStr,"","","","","","");
+            }
+
+            return "{" + retVal + "}";
+        }
+
+        private string FormatJSONContact(Contact con, string[] fields)
+        {
+
+            string contactFormatStr = "\"id\":\"{0}\"," +
+                                      "\"displayName\":\"{1}\"," +
+                                      "\"nickname\":\"{2}\"," +
+                                      "\"phoneNumbers\":[{3}]," +
+                                      "\"emails\":[{4}]," +
+                                      "\"addresses\":[{5}]," +
+                                      "\"urls\":[{6}]," +
+                                      "\"name\":{7}," +
+                                      "\"note\":\"{8}\"," +
+                                      "\"birthday\":\"{9}\"";
+
+
+            string jsonContact = String.Format(contactFormatStr,
+                                               con.GetHashCode(),
+                                               con.DisplayName,
+                                               con.CompleteName != null ? con.CompleteName.Nickname : "",
+                                               FormatJSONPhoneNumbers(con),
+                                               FormatJSONEmails(con),
+                                               FormatJSONAddresses(con),
+                                               FormatJSONWebsites(con),
+                                               FormatJSONName(con),
+                                               con.Notes.FirstOrDefault(),
+                                               con.Birthdays.FirstOrDefault());
+
+            //Debug.WriteLine("jsonContact = " + jsonContact);
+            // JSON requires new line characters be escaped
+            return "{" + jsonContact.Replace("\n", "\\n") + "}";
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/DebugConsole.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/DebugConsole.cs b/lib/cordova-wp7/templates/standalone/Plugins/DebugConsole.cs
new file mode 100644
index 0000000..fa9863a
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/DebugConsole.cs
@@ -0,0 +1,49 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+
+    public class DebugConsole : BaseCommand
+    {
+        // warn, error
+        public void log(string msg)
+        {
+            Debug.WriteLine("Log:" + msg);
+        }
+
+        public void error(string msg)
+        {
+            Debug.WriteLine("Error:" + msg);
+        }
+
+        public void warn(string msg)
+        {
+            Debug.WriteLine("Warn:" + msg);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/Device.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/Device.cs b/lib/cordova-wp7/templates/standalone/Plugins/Device.cs
new file mode 100644
index 0000000..07100ae
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/Device.cs
@@ -0,0 +1,135 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Info;
+using System.IO.IsolatedStorage;
+using System.Windows.Resources;
+using System.IO;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public class Device : BaseCommand
+    {
+        public void getDeviceInfo(string notused)
+        {
+
+            string res = String.Format("\"name\":\"{0}\",\"cordova\":\"{1}\",\"platform\":\"{2}\",\"uuid\":\"{3}\",\"version\":\"{4}\",\"model\":\"{5}\"",
+                                        this.name,
+                                        this.cordova,
+                                        this.platform,
+                                        this.uuid,
+                                        this.version,
+                                        this.model);
+
+
+
+            res = "{" + res + "}";
+            //Debug.WriteLine("Result::" + res);
+            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, res));
+        }
+
+        public string model
+        {
+            get
+            {
+                return DeviceStatus.DeviceName;
+                //return String.Format("{0},{1},{2}", DeviceStatus.DeviceManufacturer, DeviceStatus.DeviceHardwareVersion, DeviceStatus.DeviceFirmwareVersion); 
+            }
+        }
+
+        public string name
+        {
+            get
+            {
+                return DeviceStatus.DeviceName;
+                
+            }
+        }
+
+        public string cordova
+        {
+            get
+            {
+                // TODO: should be able to dynamically read the Cordova version from somewhere...
+                return "2.7.0";
+            }
+        }
+
+        public string platform
+        {
+            get
+            {
+                return Environment.OSVersion.Platform.ToString();
+            }
+        }
+
+        public string uuid
+        {
+            get
+            {
+                string returnVal = "";
+                object id;
+                UserExtendedProperties.TryGetValue("ANID", out id);
+
+                if (id != null)
+                {
+                    returnVal = id.ToString().Substring(2, 32);
+                }
+                else
+                {
+                    returnVal = "???unknown???";
+
+                    using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        try
+                        {
+                            IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Open, FileAccess.Read, appStorage);
+
+                            using (StreamReader reader = new StreamReader(fileStream))
+                            {
+                                returnVal = reader.ReadLine();
+                            }
+                        }
+                        catch (Exception /*ex*/)
+                        {
+
+                        }
+                    }
+                }
+
+                return returnVal;
+            }
+        }
+
+        public string version
+        {
+            get
+            {
+                return Environment.OSVersion.Version.ToString();
+            }
+        }
+
+    }
+}


[20/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/Media.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/Media.cs b/lib/cordova-wp8/templates/standalone/Plugins/Media.cs
new file mode 100644
index 0000000..5de4884
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/Media.cs
@@ -0,0 +1,547 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Windows;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Provides the ability to record and play back audio files on a device. 
+    /// </summary>
+    public class Media : BaseCommand
+    {
+        /// <summary>
+        /// Audio player objects
+        /// </summary>
+        private static Dictionary<string, AudioPlayer> players = new Dictionary<string, AudioPlayer>();
+
+        /// <summary>
+        /// Represents Media action options.
+        /// </summary>
+        [DataContract]
+        public class MediaOptions
+        {
+            /// <summary>
+            /// Audio id
+            /// </summary>
+            [DataMember(Name = "id", IsRequired = true)]
+            public string Id { get; set; }
+
+            /// <summary>
+            /// Path to audio file
+            /// </summary>
+            [DataMember(Name = "src")]
+            public string Src { get; set; }
+
+            /// <summary>
+            /// New track position
+            /// </summary>
+            [DataMember(Name = "milliseconds")]
+            public int Milliseconds { get; set; }
+        }
+
+        /// <summary>
+        /// Releases the audio player instance to save memory.
+        /// </summary>  
+        public void release(string options)
+        {
+            try
+            {
+                MediaOptions mediaOptions;
+
+                try
+                {
+                    string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaOptions = new MediaOptions();
+                    mediaOptions.Id = optionsString[0];
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                if (!Media.players.ContainsKey(mediaOptions.Id))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, false));
+                    return;
+                }
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        AudioPlayer audio = Media.players[mediaOptions.Id];
+                        Media.players.Remove(mediaOptions.Id);
+                        audio.Dispose();
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, true));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Starts recording and save the specified file 
+        /// </summary>
+        public void startRecordingAudio(string options)
+        {
+            try
+            {
+                MediaOptions mediaOptions;
+
+                try
+                {
+                    string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaOptions = new MediaOptions();
+                    mediaOptions.Id = optionsString[0];
+                    mediaOptions.Src = optionsString[1];
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                if (mediaOptions != null)
+                {
+
+                    Deployment.Current.Dispatcher.BeginInvoke(() =>
+                    {
+                        try
+                        {
+                            AudioPlayer audio;
+                            if (!Media.players.ContainsKey(mediaOptions.Id))
+                            {
+                                audio = new AudioPlayer(this, mediaOptions.Id);
+                                Media.players.Add(mediaOptions.Id, audio);
+                            }
+                            else
+                            {
+                                audio = Media.players[mediaOptions.Id];
+                            }
+
+                            if (audio != null)
+                            {
+                                audio.startRecording(mediaOptions.Src);
+                                DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                            }
+                            else
+                            {
+                                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error accessing AudioPlayer for key " + mediaOptions.Id));
+                            }
+                            
+                            
+                        }
+                        catch (Exception e)
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                        }
+
+                    });
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                }
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Stops recording and save to the file specified when recording started 
+        /// </summary>
+        public void stopRecordingAudio(string options)
+        {
+            try
+            {
+                string mediaId = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        if (Media.players.ContainsKey(mediaId))
+                        {
+                            AudioPlayer audio = Media.players[mediaId];
+                            audio.stopRecording();
+                            Media.players.Remove(mediaId);
+                        }
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+            }
+        }
+
+        public void setVolume(string options) // id,volume
+        {
+            try
+            {
+                string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                string id = optionsString[0];
+                double volume = double.Parse(optionsString[1]);
+
+                if (Media.players.ContainsKey(id))
+                {
+                    Deployment.Current.Dispatcher.BeginInvoke(() =>
+                    {
+                        try
+                        {
+                            AudioPlayer player = Media.players[id];
+                            player.setVolume(volume);
+                        }
+                        catch (Exception e)
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                        }
+                    });
+                }
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Error parsing options into setVolume method"));
+                return;
+            }
+        }
+
+        // Some Audio Notes:
+        // In the Windows Phone Emulator, playback of video or audio content using the MediaElement control is not supported.
+        // While playing, a MediaElement stops all other media playback on the phone.
+        // Multiple MediaElement controls are NOT supported
+
+        // Called when you create a new Media('blah') object in JS.
+        public void create(string options)
+        {
+            // Debug.WriteLine("Creating Audio :: " + options);
+            try
+            {
+                MediaOptions mediaOptions;
+                try
+                {
+                    string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaOptions = new MediaOptions();
+                    mediaOptions.Id = optionsString[0];
+                    mediaOptions.Src = optionsString[1];
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Error parsing options into create method"));
+                    return;
+                }
+
+                AudioPlayer audio = new AudioPlayer(this, mediaOptions.Id);
+                Media.players.Add(mediaOptions.Id, audio);
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Starts or resume playing audio file 
+        /// </summary>
+        public void startPlayingAudio(string options)
+        {
+            try
+            {
+                MediaOptions mediaOptions;
+                try
+                {
+                    string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaOptions = new MediaOptions();
+                    mediaOptions.Id = optionsString[0];
+                    mediaOptions.Src = optionsString[1];
+                    if (optionsString.Length > 2 && optionsString[2] != null)
+                    {
+                        mediaOptions.Milliseconds = int.Parse(optionsString[2]);
+                    }
+
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                AudioPlayer audio;
+
+                if (!Media.players.ContainsKey(mediaOptions.Id))
+                {
+                    audio = new AudioPlayer(this, mediaOptions.Id);
+                    Media.players.Add(mediaOptions.Id, audio);
+                }
+                else
+                {
+                    //Debug.WriteLine("INFO: startPlayingAudio FOUND mediaPlayer for " + mediaOptions.Id);
+                    audio = Media.players[mediaOptions.Id];
+                }
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        audio.startPlaying(mediaOptions.Src);
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+
+        /// <summary>
+        /// Seeks to a location
+        /// </summary>
+        public void seekToAudio(string options)
+        {
+            try
+            {
+                MediaOptions mediaOptions;
+
+                try
+                {
+                    string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaOptions = new MediaOptions();
+                    mediaOptions.Id = optionsString[0];
+                    if (optionsString.Length > 1 && optionsString[1] != null)
+                    {
+                        mediaOptions.Milliseconds = int.Parse(optionsString[1]);
+                    }
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        if (Media.players.ContainsKey(mediaOptions.Id))
+                        {
+                            AudioPlayer audio = Media.players[mediaOptions.Id];
+                            audio.seekToPlaying(mediaOptions.Milliseconds);
+                        }
+                        else
+                        {
+                            Debug.WriteLine("ERROR: seekToAudio could not find mediaPlayer for " + mediaOptions.Id);
+                        }
+
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Pauses playing 
+        /// </summary>
+        public void pausePlayingAudio(string options)
+        {
+
+            try
+            {
+                string mediaId = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        if (Media.players.ContainsKey(mediaId))
+                        {
+                            AudioPlayer audio = Media.players[mediaId];
+                            audio.pausePlaying();
+                        }
+                        else
+                        {
+                            Debug.WriteLine("ERROR: pausePlayingAudio could not find mediaPlayer for " + mediaId);
+                        }
+
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+
+
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+            }
+
+
+        }
+
+
+        /// <summary>
+        /// Stops playing the audio file
+        /// </summary>
+        public void stopPlayingAudio(String options)
+        {
+            try
+            {
+                string mediaId = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        if (Media.players.ContainsKey(mediaId))
+                        {
+                            AudioPlayer audio = Media.players[mediaId];
+                            audio.stopPlaying();
+                        }
+                        else
+                        {
+                            Debug.WriteLine("stopPlaying could not find mediaPlayer for " + mediaId);
+                        }
+
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+            }
+        }
+
+        /// <summary>
+        /// Gets current position of playback
+        /// </summary>
+        public void getCurrentPositionAudio(string options)
+        {
+            try
+            {
+                string mediaId = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    try
+                    {
+                        if (Media.players.ContainsKey(mediaId))
+                        {
+                            AudioPlayer audio = Media.players[mediaId];
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, audio.getCurrentPosition()));
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, -1));
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+                    }
+                });
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                return;
+            }
+        }
+
+
+        /// <summary>
+        /// Gets the duration of the audio file
+        /// </summary>
+        
+        [Obsolete("This method will be removed shortly")]
+        public void getDurationAudio(string options)
+        {
+            try
+            {
+                MediaOptions mediaOptions;
+
+                try
+                {
+                    mediaOptions = JSON.JsonHelper.Deserialize<MediaOptions>(options);
+                }
+                catch (Exception)
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                    return;
+                }
+
+                AudioPlayer audio;
+                if (Media.players.ContainsKey(mediaOptions.Id))
+                {
+                    audio = Media.players[mediaOptions.Id];
+                }
+                else
+                {
+                    Debug.WriteLine("ERROR: getDurationAudio could not find mediaPlayer for " + mediaOptions.Id);
+                    audio = new AudioPlayer(this, mediaOptions.Id);
+                    Media.players.Add(mediaOptions.Id, audio);
+                }
+
+                Deployment.Current.Dispatcher.BeginInvoke(() =>
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.OK, audio.getDuration(mediaOptions.Src)));
+                });
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/MimeTypeMapper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/MimeTypeMapper.cs b/lib/cordova-wp8/templates/standalone/Plugins/MimeTypeMapper.cs
new file mode 100644
index 0000000..a2794f5
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/MimeTypeMapper.cs
@@ -0,0 +1,101 @@
+/*  
+	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.
+*/
+
+using System.Collections.Generic;
+using System.IO;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Represents file extension to mime type mapper.
+    /// </summary>
+    public static class MimeTypeMapper
+    {
+        /// <summary>
+        /// For unknown type it is recommended to use 'application/octet-stream'
+        /// http://stackoverflow.com/questions/1176022/unknown-file-type-mime
+        /// </summary>
+        private static string DefaultMimeType = "application/octet-stream";
+
+        /// <summary>
+        /// Stores mime type for all necessary extension
+        /// </summary>
+        private static readonly Dictionary<string, string> MIMETypesDictionary = new Dictionary<string, string>
+                                                                             {                                                                                
+                                                                                 {"avi", "video/x-msvideo"},
+                                                                                 {"bmp", "image/bmp"},                                                                                                                                                                
+                                                                                 {"gif", "image/gif"},
+                                                                                 {"html","text/html"},                                                                              
+                                                                                 {"jpe", "image/jpeg"},
+                                                                                 {"jpeg", "image/jpeg"},
+                                                                                 {"jpg", "image/jpeg"},     
+                                                                                 {"js","text/javascript"},                                                                         
+                                                                                 {"mov", "video/quicktime"},
+                                                                                 {"mp2", "audio/mpeg"},
+                                                                                 {"mp3", "audio/mpeg"},
+                                                                                 {"mp4", "video/mp4"},
+                                                                                 {"mpe", "video/mpeg"},
+                                                                                 {"mpeg", "video/mpeg"},
+                                                                                 {"mpg", "video/mpeg"},
+                                                                                 {"mpga", "audio/mpeg"},                                                                                
+                                                                                 {"pbm", "image/x-portable-bitmap"},
+                                                                                 {"pcm", "audio/x-pcm"},
+                                                                                 {"pct", "image/pict"},
+                                                                                 {"pgm", "image/x-portable-graymap"},
+                                                                                 {"pic", "image/pict"},
+                                                                                 {"pict", "image/pict"},
+                                                                                 {"png", "image/png"},
+                                                                                 {"pnm", "image/x-portable-anymap"},
+                                                                                 {"pnt", "image/x-macpaint"},
+                                                                                 {"pntg", "image/x-macpaint"},
+                                                                                 {"ppm", "image/x-portable-pixmap"},
+                                                                                 {"qt", "video/quicktime"},
+                                                                                 {"ra", "audio/x-pn-realaudio"},
+                                                                                 {"ram", "audio/x-pn-realaudio"},
+                                                                                 {"ras", "image/x-cmu-raster"},
+                                                                                 {"rgb", "image/x-rgb"},
+                                                                                 {"snd", "audio/basic"},
+                                                                                 {"txt", "text/plain"},
+                                                                                 {"tif", "image/tiff"},
+                                                                                 {"tiff", "image/tiff"},
+                                                                                 {"wav", "audio/x-wav"},
+                                                                                 {"wbmp", "image/vnd.wap.wbmp"},
+
+                                                                             };
+        /// <summary>
+        /// Gets mime type by file extension
+        /// </summary>
+        /// <param name="fileName">file name to extract extension</param>
+        /// <returns>mime type</returns>
+        public static string GetMimeType(string fileName)
+        {
+            string ext = Path.GetExtension(fileName);
+
+            // invalid extension
+            if (string.IsNullOrEmpty(ext) || !ext.StartsWith("."))
+            {
+                return DefaultMimeType;
+            }
+
+            ext = ext.Remove(0, 1);
+
+            if (MIMETypesDictionary.ContainsKey(ext))
+            {
+                return MIMETypesDictionary[ext];
+            }
+
+            return DefaultMimeType;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/NetworkStatus.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/NetworkStatus.cs b/lib/cordova-wp8/templates/standalone/Plugins/NetworkStatus.cs
new file mode 100644
index 0000000..12eb061
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/NetworkStatus.cs
@@ -0,0 +1,129 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Diagnostics;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Net.NetworkInformation;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+
+    // http://msdn.microsoft.com/en-us/library/microsoft.phone.net.networkinformation(v=VS.92).aspx
+    // http://msdn.microsoft.com/en-us/library/microsoft.phone.net.networkinformation.devicenetworkinformation(v=VS.92).aspx
+
+    public class NetworkStatus : BaseCommand
+    {
+        const string UNKNOWN = "unknown";
+        const string ETHERNET = "ethernet";
+        const string WIFI = "wifi";
+        const string CELL_2G = "2g";
+        const string CELL_3G = "3g";
+        const string CELL_4G = "4g";
+        const string NONE = "none";
+        const string CELL = "cellular";
+
+        private bool HasCallback = false;
+
+        public NetworkStatus()
+        {
+            DeviceNetworkInformation.NetworkAvailabilityChanged += new EventHandler<NetworkNotificationEventArgs>(ChangeDetected);
+        }
+
+        public override void OnResume(object sender, Microsoft.Phone.Shell.ActivatedEventArgs e)
+        {
+            this.getConnectionInfo("");
+        }
+
+        public void getConnectionInfo(string empty)
+        {
+            HasCallback = true;
+            updateConnectionType(checkConnectionType());
+        }
+
+        private string checkConnectionType()
+        {
+            if (DeviceNetworkInformation.IsNetworkAvailable)
+            {
+                if (DeviceNetworkInformation.IsWiFiEnabled)
+                {
+                    return WIFI;
+                }
+                else
+                {
+                    return DeviceNetworkInformation.IsCellularDataEnabled ? CELL : UNKNOWN;
+                }
+            }
+            return NONE;
+        }
+
+        private string checkConnectionType(NetworkInterfaceSubType type)
+        {
+            switch (type)
+            {
+                case NetworkInterfaceSubType.Cellular_1XRTT: //cell
+                case NetworkInterfaceSubType.Cellular_GPRS: //cell
+                    return CELL;
+                case NetworkInterfaceSubType.Cellular_EDGE: //2
+                    return CELL_2G;
+                case NetworkInterfaceSubType.Cellular_3G:
+                case NetworkInterfaceSubType.Cellular_EVDO: //3
+                case NetworkInterfaceSubType.Cellular_EVDV: //3 
+                case NetworkInterfaceSubType.Cellular_HSPA: //3
+                    return CELL_3G;
+                case NetworkInterfaceSubType.WiFi:
+                    return WIFI;
+                case NetworkInterfaceSubType.Unknown:
+                case NetworkInterfaceSubType.Desktop_PassThru:
+                default:
+                    return UNKNOWN;
+            }
+        }
+
+        void ChangeDetected(object sender, NetworkNotificationEventArgs e)
+        {
+            switch (e.NotificationType)
+            {
+                case NetworkNotificationType.InterfaceConnected:
+                    updateConnectionType(checkConnectionType(e.NetworkInterface.InterfaceSubtype));
+                    break;
+                case NetworkNotificationType.InterfaceDisconnected:
+                    updateConnectionType(NONE);
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void updateConnectionType(string type)
+        {
+            // This should also implicitly fire offline/online events as that is handled on the JS side
+            if (this.HasCallback)
+            {
+                PluginResult result = new PluginResult(PluginResult.Status.OK, type);
+                result.KeepCallback = true;
+                DispatchCommandResult(result);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/Notification.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/Notification.cs b/lib/cordova-wp8/templates/standalone/Plugins/Notification.cs
new file mode 100644
index 0000000..0759c72
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/Notification.cs
@@ -0,0 +1,361 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Devices;
+using System.Runtime.Serialization;
+using System.Threading;
+using System.Windows.Resources;
+using Microsoft.Phone.Controls;
+using Microsoft.Xna.Framework.Audio;
+using WPCordovaClassLib.Cordova.UI;
+using System.Diagnostics;
+
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public class Notification : BaseCommand
+    {
+        static ProgressBar progressBar = null;
+        const int DEFAULT_DURATION = 5;
+
+        private NotificationBox notifyBox;
+
+        private PhoneApplicationPage Page
+        {
+            get
+            {
+                PhoneApplicationPage page = null;
+                PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                if (frame != null)
+                {
+                    page = frame.Content as PhoneApplicationPage;
+                }
+                return page;
+            }
+        }
+
+        // blink api - doesn't look like there is an equivalent api we can use...
+
+        [DataContract]
+        public class AlertOptions
+        {
+            [OnDeserializing]
+            public void OnDeserializing(StreamingContext context)
+            {
+                // set defaults
+                this.message = "message";
+                this.title = "Alert";
+                this.buttonLabel = "ok";
+            }
+
+            /// <summary>
+            /// message to display in the alert box
+            /// </summary>
+            [DataMember]
+            public string message;
+
+            /// <summary>
+            /// title displayed on the alert window
+            /// </summary>
+            [DataMember]
+            public string title;
+
+            /// <summary>
+            /// text to display on the button
+            /// </summary>
+            [DataMember]
+            public string buttonLabel;
+        }
+
+        public void alert(string options)
+        {
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+            AlertOptions alertOpts = new AlertOptions();
+            alertOpts.message = args[0];
+            alertOpts.title = args[1];
+            alertOpts.buttonLabel = args[2];
+            string aliasCurrentCommandCallbackId = args[3];
+
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                PhoneApplicationPage page = Page;
+                if (page != null)
+                {
+                    Grid grid = page.FindName("LayoutRoot") as Grid;
+                    if (grid != null)
+                    {
+                        var previous = notifyBox;
+                        notifyBox = new NotificationBox();
+                        notifyBox.Tag = new { previous = previous, callbackId = aliasCurrentCommandCallbackId };
+                        notifyBox.PageTitle.Text = alertOpts.title;
+                        notifyBox.SubTitle.Text = alertOpts.message;
+                        Button btnOK = new Button();
+                        btnOK.Content = alertOpts.buttonLabel;
+                        btnOK.Click += new RoutedEventHandler(btnOK_Click);
+                        btnOK.Tag = 1;
+                        notifyBox.ButtonPanel.Children.Add(btnOK);
+                        grid.Children.Add(notifyBox);
+
+                        if (previous == null)
+                        {
+                            page.BackKeyPress += page_BackKeyPress;
+                        }
+                    }
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.INSTANTIATION_EXCEPTION));
+                }
+            });
+        }
+
+        public void confirm(string options)
+        {
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+            AlertOptions alertOpts = new AlertOptions();
+            alertOpts.message = args[0];
+            alertOpts.title = args[1];
+            alertOpts.buttonLabel = args[2];
+            string aliasCurrentCommandCallbackId = args[3];
+
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                PhoneApplicationPage page = Page;
+                if (page != null)
+                {
+                    Grid grid = page.FindName("LayoutRoot") as Grid;
+                    if (grid != null)
+                    {
+                        var previous = notifyBox;
+                        notifyBox = new NotificationBox();
+                        notifyBox.Tag = new { previous = previous, callbackId = aliasCurrentCommandCallbackId };
+                        notifyBox.PageTitle.Text = alertOpts.title;
+                        notifyBox.SubTitle.Text = alertOpts.message;
+
+                        string[] labels = JSON.JsonHelper.Deserialize<string[]>(alertOpts.buttonLabel);
+
+                        if (labels == null)
+                        {
+                            labels = alertOpts.buttonLabel.Split(',');
+                        }
+
+                        for (int n = 0; n < labels.Length; n++)
+                        {
+                            Button btn = new Button();
+                            btn.Content = labels[n];
+                            btn.Tag = n;
+                            btn.Click += new RoutedEventHandler(btnOK_Click);
+                            notifyBox.ButtonPanel.Children.Add(btn);
+                        }
+
+                        grid.Children.Add(notifyBox);
+                        if (previous == null)
+                        {
+                            page.BackKeyPress += page_BackKeyPress;
+                        }
+                    }
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.INSTANTIATION_EXCEPTION));
+                }
+            });
+        }
+
+        void page_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
+        {
+            PhoneApplicationPage page = sender as PhoneApplicationPage;
+            string callbackId = "";
+            if (page != null && notifyBox != null)
+            {
+                Grid grid = page.FindName("LayoutRoot") as Grid;
+                if (grid != null)
+                {
+                    grid.Children.Remove(notifyBox);
+                    dynamic notifBoxData = notifyBox.Tag;
+                    notifyBox = notifBoxData.previous as NotificationBox;
+                    callbackId = notifBoxData.callbackId as string;
+                }
+                if (notifyBox == null)
+                {
+                    page.BackKeyPress -= page_BackKeyPress;
+                }
+                e.Cancel = true;
+            }
+
+            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, 0), callbackId);
+        }
+
+        void btnOK_Click(object sender, RoutedEventArgs e)
+        {
+            Button btn = sender as Button;
+            FrameworkElement notifBoxParent = null;
+            int retVal = 0;
+            string callbackId = "";
+            if (btn != null)
+            {
+                retVal = (int)btn.Tag + 1;
+
+                notifBoxParent = btn.Parent as FrameworkElement;
+                while ((notifBoxParent = notifBoxParent.Parent as FrameworkElement) != null &&
+                       !(notifBoxParent is NotificationBox)) ;
+            }
+            if (notifBoxParent != null)
+            {
+                PhoneApplicationPage page = Page;
+                if (page != null)
+                {
+                    Grid grid = page.FindName("LayoutRoot") as Grid;
+                    if (grid != null)
+                    {
+                        grid.Children.Remove(notifBoxParent);
+                    }
+                    
+                    dynamic notifBoxData = notifBoxParent.Tag;
+                    notifyBox = notifBoxData.previous as NotificationBox;
+                    callbackId = notifBoxData.callbackId as string;
+
+                    if (notifyBox == null)
+                    {
+                        page.BackKeyPress -= page_BackKeyPress;
+                    }
+                }
+
+            }
+            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, retVal),callbackId);
+        }
+
+
+
+        public void beep(string options)
+        {
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+            int times = int.Parse(args[0]);
+
+            string resourcePath = BaseCommand.GetBaseURL() + "resources/notification-beep.wav";
+
+            StreamResourceInfo sri = Application.GetResourceStream(new Uri(resourcePath, UriKind.Relative));
+
+            if (sri != null)
+            {
+                SoundEffect effect = SoundEffect.FromStream(sri.Stream);
+                SoundEffectInstance inst = effect.CreateInstance();
+                ThreadPool.QueueUserWorkItem((o) =>
+                {
+                    // cannot interact with UI !!
+                    do
+                    {
+                        inst.Play();
+                        Thread.Sleep(effect.Duration + TimeSpan.FromMilliseconds(100));
+                    }
+                    while (--times > 0);
+
+                });
+
+            }
+
+            // TODO: may need a listener to trigger DispatchCommandResult after the alarm has finished executing...
+            DispatchCommandResult();
+        }
+
+        // Display an indeterminate progress indicator
+        public void activityStart(string unused)
+        {
+
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+
+                if (frame != null)
+                {
+                    PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+
+                    if (page != null)
+                    {
+                        var temp = page.FindName("LayoutRoot");
+                        Grid grid = temp as Grid;
+                        if (grid != null)
+                        {
+                            if (progressBar != null)
+                            {
+                                grid.Children.Remove(progressBar);
+                            }
+                            progressBar = new ProgressBar();
+                            progressBar.IsIndeterminate = true;
+                            progressBar.IsEnabled = true;
+
+                            grid.Children.Add(progressBar);
+                        }
+                    }
+                }
+            });
+        }
+
+
+        // Remove our indeterminate progress indicator
+        public void activityStop(string unused)
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                if (progressBar != null)
+                {
+                    progressBar.IsEnabled = false;
+                    PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                    if (frame != null)
+                    {
+                        PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+                        if (page != null)
+                        {
+                            Grid grid = page.FindName("LayoutRoot") as Grid;
+                            if (grid != null)
+                            {
+                                grid.Children.Remove(progressBar);
+                            }
+                        }
+                    }
+                    progressBar = null;
+                }
+            });
+        }
+
+        public void vibrate(string vibrateDuration)
+        {
+
+            int msecs = 200; // set default
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(vibrateDuration);
+
+                msecs = int.Parse(args[0]);
+                if (msecs < 1)
+                {
+                    msecs = 1;
+                }
+            }
+            catch (FormatException)
+            {
+
+            }
+
+            VibrateController.Default.Start(TimeSpan.FromMilliseconds(msecs));
+
+            // TODO: may need to add listener to trigger DispatchCommandResult when the vibration ends...
+            DispatchCommandResult();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioCaptureTask.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioCaptureTask.cs b/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioCaptureTask.cs
new file mode 100644
index 0000000..3d7d60f
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioCaptureTask.cs
@@ -0,0 +1,107 @@
+/*  
+	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.
+*/
+
+using System;
+using System.IO;
+using System.Windows;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    /// <summary>
+    /// Allows an application to launch the Audio Recording application. 
+    /// Use this to allow users to record audio from your application.
+    /// </summary>
+    public class AudioCaptureTask
+    {
+        /// <summary>
+        /// Represents recorded audio returned from a call to the Show method of
+        /// a WPCordovaClassLib.Cordova.Controls.AudioCaptureTask object
+        /// </summary>
+        public class AudioResult : TaskEventArgs
+        {
+            /// <summary>
+            /// Initializes a new instance of the AudioResult class.
+            /// </summary>
+            public AudioResult()
+            { }
+
+            /// <summary>
+            /// Initializes a new instance of the AudioResult class
+            /// with the specified Microsoft.Phone.Tasks.TaskResult.
+            /// </summary>
+            /// <param name="taskResult">Associated Microsoft.Phone.Tasks.TaskResult</param>
+            public AudioResult(TaskResult taskResult)
+                : base(taskResult)
+            { }
+
+            /// <summary>
+            ///  Gets the file name of the recorded audio.
+            /// </summary>
+            public Stream AudioFile { get; internal set; }
+
+            /// <summary>
+            /// Gets the stream containing the data for the recorded audio.
+            /// </summary>
+            public string AudioFileName { get; internal set; }
+        }
+
+        /// <summary>
+        /// Occurs when a audio recording task is completed.
+        /// </summary>
+        public event EventHandler<AudioResult> Completed;
+
+        /// <summary>
+        /// Shows Audio Recording application
+        /// </summary>
+        public void Show()
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                var root = Application.Current.RootVisual as PhoneApplicationFrame;
+
+                root.Navigated += new System.Windows.Navigation.NavigatedEventHandler(NavigationService_Navigated);
+
+                string baseUrl = WPCordovaClassLib.Cordova.Commands.BaseCommand.GetBaseURL();
+                // dummy parameter is used to always open a fresh version
+                root.Navigate(new System.Uri( baseUrl + "CordovaLib/UI/AudioRecorder.xaml?dummy=" + Guid.NewGuid().ToString(), UriKind.Relative));
+
+            });
+        }
+
+        /// <summary>
+        /// Performs additional configuration of the recording application.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void NavigationService_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            if (!(e.Content is AudioRecorder)) return;
+
+            (Application.Current.RootVisual as PhoneApplicationFrame).Navigated -= NavigationService_Navigated;
+
+            AudioRecorder audioRecorder = (AudioRecorder)e.Content;
+
+            if (audioRecorder != null)
+            {
+                audioRecorder.Completed += this.Completed;
+            }
+            else if (this.Completed != null)
+            {
+                this.Completed(this, new AudioResult(TaskResult.Cancel));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioRecorder.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioRecorder.xaml b/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioRecorder.xaml
new file mode 100644
index 0000000..0fd26ab
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioRecorder.xaml
@@ -0,0 +1,66 @@
+<!--
+ 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. 
+-->
+<phone:PhoneApplicationPage 
+    x:Class="WPCordovaClassLib.Cordova.UI.AudioRecorder"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    SupportedOrientations="Portrait" Orientation="Portrait"
+    mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
+    shell:SystemTray.IsVisible="True">
+
+    <!--LayoutRoot is the root grid where all page content is placed-->
+    <Grid x:Name="LayoutRoot" Background="Transparent">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+
+        <!--TitlePanel contains the name of the application and page title-->
+        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="0,17,0,28">
+            <TextBlock x:Name="PageTitle" Text="Audio recorder" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
+        </StackPanel>
+
+        <!--ContentPanel - place additional content here-->
+        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
+            <Button Name="btnStartStop" Content="Start" Height="72" HorizontalAlignment="Left" Margin="156,96,0,0"  VerticalAlignment="Top" Width="160" Click="btnStartStop_Click" />
+            <Button Name="btnTake" Content="Take" IsEnabled="False" Height="72" HorizontalAlignment="Left" Margin="155,182,0,0" VerticalAlignment="Top" Width="160" Click="btnTake_Click" />
+            <TextBlock Height="30" HorizontalAlignment="Left" Margin="168,60,0,0" Name="txtDuration" Text="Duration: 00:00" VerticalAlignment="Top" />
+        </Grid>
+    </Grid>
+ 
+    <!--Sample code showing usage of ApplicationBar-->
+    <!--<phone:PhoneApplicationPage.ApplicationBar>
+        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
+            <shell:ApplicationBar.MenuItems>
+                <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
+                <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
+            </shell:ApplicationBar.MenuItems>
+        </shell:ApplicationBar>
+    </phone:PhoneApplicationPage.ApplicationBar>-->
+
+</phone:PhoneApplicationPage>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioRecorder.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioRecorder.xaml.cs b/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioRecorder.xaml.cs
new file mode 100644
index 0000000..01a0832
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/UI/AudioRecorder.xaml.cs
@@ -0,0 +1,307 @@
+/*  
+	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.
+*/
+
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using System;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Windows;
+using System.Windows.Threading;
+using WPCordovaClassLib.Cordova.Commands;
+using AudioResult = WPCordovaClassLib.Cordova.UI.AudioCaptureTask.AudioResult;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    /// <summary>
+    /// Implements Audio Recording application
+    /// </summary>
+    public partial class AudioRecorder : PhoneApplicationPage
+    {
+
+        #region Constants
+
+        private const string RecordingStartCaption = "Start";
+        private const string RecordingStopCaption = "Stop";
+
+        private const string LocalFolderName = "AudioCache";
+        private const string FileNameFormat = "Audio-{0}.wav";
+
+        #endregion
+
+        #region Callbacks
+
+        /// <summary>
+        /// Occurs when a audio recording task is completed.
+        /// </summary>
+        public event EventHandler<AudioResult> Completed;
+
+        #endregion
+
+        #region Fields
+
+        /// <summary>
+        /// Audio source
+        /// </summary>
+        private Microphone microphone;
+
+        /// <summary>
+        /// Temporary buffer to store audio chunk
+        /// </summary>
+        private byte[] buffer;
+
+        /// <summary>
+        /// Recording duration
+        /// </summary>
+        private TimeSpan duration;
+
+        /// <summary>
+        /// Output buffer
+        /// </summary>
+        private MemoryStream memoryStream;
+
+        /// <summary>
+        /// Xna game loop dispatcher
+        /// </summary>
+        DispatcherTimer dtXna;
+
+        /// <summary>
+        /// Recording result, dispatched back when recording page is closed
+        /// </summary>
+        private AudioResult result = new AudioResult(TaskResult.Cancel);
+
+        /// <summary>
+        /// Whether we are recording audio now
+        /// </summary>
+        private bool IsRecording
+        {
+            get
+            {
+                return (this.microphone != null && this.microphone.State == MicrophoneState.Started);
+            }
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Creates new instance of the AudioRecorder class.
+        /// </summary>
+        public AudioRecorder()
+        {
+
+            this.InitializeXnaGameLoop();
+
+            // microphone requires special XNA initialization to work
+            InitializeComponent();
+        }
+
+        /// <summary>
+        /// Starts recording, data is stored in memory
+        /// </summary>
+        private void StartRecording()
+        {
+            this.microphone = Microphone.Default;
+            this.microphone.BufferDuration = TimeSpan.FromMilliseconds(500);
+
+            this.btnTake.IsEnabled = false;
+            this.btnStartStop.Content = RecordingStopCaption;
+
+            this.buffer = new byte[microphone.GetSampleSizeInBytes(this.microphone.BufferDuration)];
+            this.microphone.BufferReady += new EventHandler<EventArgs>(MicrophoneBufferReady);
+
+            this.memoryStream = new MemoryStream();
+            this.memoryStream.InitializeWavStream(this.microphone.SampleRate);
+
+            this.duration = new TimeSpan(0);
+
+            this.microphone.Start();
+        }
+
+        /// <summary>
+        /// Stops recording
+        /// </summary>
+        private void StopRecording()
+        {
+            this.microphone.Stop();
+
+            this.microphone.BufferReady -= MicrophoneBufferReady;
+
+            this.microphone = null;
+
+            btnStartStop.Content = RecordingStartCaption;
+
+            // check there is some data
+            this.btnTake.IsEnabled = true;
+        }
+
+        /// <summary>
+        /// Handles Start/Stop events
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnStartStop_Click(object sender, RoutedEventArgs e)
+        {
+
+            if (this.IsRecording)
+            {
+                this.StopRecording();
+            }
+            else
+            {
+                this.StartRecording();
+            }
+        }
+
+        /// <summary>
+        /// Handles Take button click
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnTake_Click(object sender, RoutedEventArgs e)
+        {
+            this.result = this.SaveAudioClipToLocalStorage();
+
+            if (Completed != null)
+            {
+                Completed(this, result);
+            }
+
+            if (this.NavigationService.CanGoBack)
+            {
+                this.NavigationService.GoBack();
+            }
+        }
+
+        /// <summary>
+        /// Handles page closing event, stops recording if needed and dispatches results.
+        /// </summary>
+        /// <param name="e"></param>
+        protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
+        {
+            if (IsRecording)
+            {
+                StopRecording();
+            }
+
+            this.FinalizeXnaGameLoop();
+
+            base.OnNavigatedFrom(e);
+        }
+
+        /// <summary>
+        /// Copies data from microphone to memory storages and updates recording state
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void MicrophoneBufferReady(object sender, EventArgs e)
+        {
+            this.microphone.GetData(this.buffer);
+            this.memoryStream.Write(this.buffer, 0, this.buffer.Length);
+            TimeSpan bufferDuration = this.microphone.BufferDuration;
+
+            this.Dispatcher.BeginInvoke(() =>
+            {
+                this.duration += bufferDuration;
+
+                this.txtDuration.Text = "Duration: " +
+                    this.duration.Minutes.ToString().PadLeft(2, '0') + ":" +
+                    this.duration.Seconds.ToString().PadLeft(2, '0');
+            });
+
+        }
+
+        /// <summary>
+        /// Writes audio data from memory to isolated storage
+        /// </summary>
+        /// <returns></returns>
+        private AudioResult SaveAudioClipToLocalStorage()
+        {
+            if (this.memoryStream == null || this.memoryStream.Length <= 0)
+            {
+                return new AudioResult(TaskResult.Cancel);
+            }
+
+            this.memoryStream.UpdateWavStream();
+
+            // save audio data to local isolated storage
+
+            string filename = String.Format(FileNameFormat, Guid.NewGuid().ToString());
+
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+
+                    if (!isoFile.DirectoryExists(LocalFolderName))
+                    {
+                        isoFile.CreateDirectory(LocalFolderName);
+                    }
+
+                    string filePath = System.IO.Path.Combine("/" + LocalFolderName + "/", filename);
+
+                    this.memoryStream.Seek(0, SeekOrigin.Begin);
+
+                    using (IsolatedStorageFileStream fileStream = isoFile.CreateFile(filePath))
+                    {
+
+                        this.memoryStream.CopyTo(fileStream);
+                    }
+
+                    AudioResult result = new AudioResult(TaskResult.OK);
+                    result.AudioFileName = filePath;
+
+                    result.AudioFile = this.memoryStream;
+                    result.AudioFile.Seek(0, SeekOrigin.Begin);
+
+                    return result;
+                }
+
+
+
+            }
+            catch (Exception)
+            {
+                //TODO: log or do something else
+                throw;
+            }
+        }
+
+        /// <summary>
+        /// Special initialization required for the microphone: XNA game loop
+        /// </summary>
+        private void InitializeXnaGameLoop()
+        {
+            // Timer to simulate the XNA game loop (Microphone is from XNA)
+            this.dtXna = new DispatcherTimer();
+            this.dtXna.Interval = TimeSpan.FromMilliseconds(33);
+            this.dtXna.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
+            this.dtXna.Start();
+        }
+        /// <summary>
+        /// Finalizes XNA game loop for microphone
+        /// </summary>
+        private void FinalizeXnaGameLoop()
+        {
+            // Timer to simulate the XNA game loop (Microphone is from XNA)
+            if (dtXna != null)
+            {
+                dtXna.Stop();
+                dtXna = null;
+            }
+        }
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/UI/ImageCapture.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/UI/ImageCapture.xaml b/lib/cordova-wp8/templates/standalone/Plugins/UI/ImageCapture.xaml
new file mode 100644
index 0000000..a7eee21
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/UI/ImageCapture.xaml
@@ -0,0 +1,26 @@
+<phone:PhoneApplicationPage 
+    x:Class="WPCordovaClassLib.Cordova.UI.ImageCapture"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
+    mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
+    shell:SystemTray.IsVisible="True">
+
+    <!--LayoutRoot is the root grid where all page content is placed-->
+    <Grid x:Name="LayoutRoot" Background="Yellow">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+
+    </Grid>
+ 
+
+</phone:PhoneApplicationPage>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/UI/ImageCapture.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/UI/ImageCapture.xaml.cs b/lib/cordova-wp8/templates/standalone/Plugins/UI/ImageCapture.xaml.cs
new file mode 100644
index 0000000..234b444
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/UI/ImageCapture.xaml.cs
@@ -0,0 +1,109 @@
+/*  
+	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.
+*/
+
+
+using System;
+using System.IO;
+using System.Windows;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    public partial class ImageCapture : PhoneApplicationPage
+    {
+        public ImageCapture()
+        {
+            InitializeComponent();
+        }
+    }
+
+    public class ImageCaptureTask
+    {
+        /// <summary>
+        /// Represents an image returned from a call to the Show method of
+        /// a WPCordovaClassLib.Cordova.Controls.ImageCaptureTask object
+        /// </summary>
+        //public class AudioResult : TaskEventArgs
+        //{
+        //    /// <summary>
+        //    /// Initializes a new instance of the AudioResult class.
+        //    /// </summary>
+        //    public AudioResult()
+        //    { }
+
+        //    /// <summary>
+        //    /// Initializes a new instance of the AudioResult class
+        //    /// with the specified Microsoft.Phone.Tasks.TaskResult.
+        //    /// </summary>
+        //    /// <param name="taskResult">Associated Microsoft.Phone.Tasks.TaskResult</param>
+        //    public AudioResult(TaskResult taskResult)
+        //        : base(taskResult)
+        //    { }
+
+        //    /// <summary>
+        //    ///  Gets the file name of the recorded audio.
+        //    /// </summary>
+        //    public Stream AudioFile { get; internal set; }
+
+        //    /// <summary>
+        //    /// Gets the stream containing the data for the recorded audio.
+        //    /// </summary>
+        //    public string AudioFileName { get; internal set; }
+        //}
+
+        ///// <summary>
+        ///// Occurs when a audio recording task is completed.
+        ///// </summary>
+        //public event EventHandler<AudioResult> Completed;
+
+        /// <summary>
+        /// Shows Audio Recording application
+        /// </summary>
+        public void Show()
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                var root = Application.Current.RootVisual as PhoneApplicationFrame;
+
+                root.Navigated += new System.Windows.Navigation.NavigatedEventHandler(NavigationService_Navigated);
+
+                string baseUrl = WPCordovaClassLib.Cordova.Commands.BaseCommand.GetBaseURL();
+
+                // dummy parameter is used to always open a fresh version
+                root.Navigate(new System.Uri(baseUrl + "Cordova/UI/ImageCapture.xaml?dummy=" + Guid.NewGuid().ToString(), UriKind.Relative));
+            });
+        }
+
+        /// <summary>
+        /// Performs additional configuration of the recording application.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void NavigationService_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            ImageCapture imageCapture = e.Content as ImageCapture;
+            if (imageCapture != null)
+            {
+                (Application.Current.RootVisual as PhoneApplicationFrame).Navigated -= NavigationService_Navigated;
+
+                //imageCapture.Completed += this.Completed;
+                //else if (this.Completed != null)
+                //{
+                //    this.Completed(this, new AudioResult(TaskResult.Cancel));
+                //}
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/UI/NotificationBox.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/UI/NotificationBox.xaml b/lib/cordova-wp8/templates/standalone/Plugins/UI/NotificationBox.xaml
new file mode 100644
index 0000000..1ca5d5f
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/UI/NotificationBox.xaml
@@ -0,0 +1,62 @@
+<!--
+ 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. 
+-->
+<UserControl x:Class="WPCordovaClassLib.Cordova.UI.NotificationBox"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    d:DesignHeight="800" d:DesignWidth="480" VerticalAlignment="Stretch">
+
+    <Grid x:Name="LayoutRoot" 
+          Background="{StaticResource PhoneSemitransparentBrush}" VerticalAlignment="Stretch">
+        
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+        
+
+        <!--TitlePanel contains the name of the application and page title-->
+        <StackPanel x:Name="TitlePanel" 
+                    Grid.Row="0" 
+                    Background="{StaticResource PhoneSemitransparentBrush}">
+            <TextBlock x:Name="PageTitle" 
+                       Text="Title" 
+                       Margin="10,10" 
+                       Style="{StaticResource PhoneTextTitle2Style}"/>
+            
+            <TextBlock x:Name="SubTitle" 
+                       Text="Subtitle" 
+                       TextWrapping="Wrap"
+                       Margin="10,10"
+                       Style="{StaticResource PhoneTextTitle3Style}"/>
+            
+            <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">       
+            <StackPanel x:Name="ButtonPanel"
+                        Margin="10,10"
+                        Orientation="Horizontal"/>
+            </ScrollViewer>
+
+        </StackPanel>
+    </Grid>
+</UserControl>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/UI/NotificationBox.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/UI/NotificationBox.xaml.cs b/lib/cordova-wp8/templates/standalone/Plugins/UI/NotificationBox.xaml.cs
new file mode 100644
index 0000000..50b2f2a
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/UI/NotificationBox.xaml.cs
@@ -0,0 +1,41 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    public partial class NotificationBox : UserControl
+    {
+        public NotificationBox()
+        {
+            InitializeComponent();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoCaptureTask.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoCaptureTask.cs b/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoCaptureTask.cs
new file mode 100644
index 0000000..958c05c
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoCaptureTask.cs
@@ -0,0 +1,105 @@
+/*  
+	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.
+*/
+
+using System;
+using System.IO;
+using System.Windows;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    /// <summary>
+    /// Allows an application to launch the Video Recording application. 
+    /// Use this to allow users to record video from your application.
+    /// </summary>
+    public class VideoCaptureTask
+    {
+        /// <summary>
+        /// Represents recorded video returned from a call to the Show method of
+        /// a WPCordovaClassLib.Cordova.Controls.VideoCaptureTask object
+        /// </summary>
+        public class VideoResult : TaskEventArgs
+        {
+            /// <summary>
+            /// Initializes a new instance of the VideoResult class.
+            /// </summary>
+            public VideoResult()
+            { }
+
+            /// <summary>
+            /// Initializes a new instance of the VideoResult class
+            /// with the specified Microsoft.Phone.Tasks.TaskResult.
+            /// </summary>
+            /// <param name="taskResult">Associated Microsoft.Phone.Tasks.TaskResult</param>
+            public VideoResult(TaskResult taskResult)
+                : base(taskResult)
+            { }
+
+            /// <summary>
+            ///  Gets the file name of the recorded Video.
+            /// </summary>
+            public Stream VideoFile { get; internal set; }
+
+            /// <summary>
+            /// Gets the stream containing the data for the recorded Video.
+            /// </summary>
+            public string VideoFileName { get; internal set; }
+        }
+
+        /// <summary>
+        /// Occurs when a Video recording task is completed.
+        /// </summary>
+        public event EventHandler<VideoResult> Completed;
+
+        /// <summary>
+        /// Shows Video Recording application
+        /// </summary>
+        public void Show()
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                var root = Application.Current.RootVisual as PhoneApplicationFrame;
+
+                root.Navigated += new System.Windows.Navigation.NavigatedEventHandler(NavigationService_Navigated);
+
+                string baseUrl = WPCordovaClassLib.Cordova.Commands.BaseCommand.GetBaseURL();
+                // dummy parameter is used to always open a fresh version
+                root.Navigate(new System.Uri( baseUrl + "CordovaLib/UI/VideoRecorder.xaml?dummy=" + Guid.NewGuid().ToString(), UriKind.Relative));
+            });
+        }
+
+        /// <summary>
+        /// Performs additional configuration of the recording application.
+        /// </summary>
+        private void NavigationService_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            if (!(e.Content is VideoRecorder)) return;
+
+            (Application.Current.RootVisual as PhoneApplicationFrame).Navigated -= NavigationService_Navigated;
+
+            VideoRecorder VideoRecorder = (VideoRecorder)e.Content;
+
+            if (VideoRecorder != null)
+            {
+                VideoRecorder.Completed += this.Completed;
+            }
+            else if (this.Completed != null)
+            {
+                this.Completed(this, new VideoResult(TaskResult.Cancel));
+            }
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoRecorder.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoRecorder.xaml b/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoRecorder.xaml
new file mode 100644
index 0000000..c78fdb0
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/UI/VideoRecorder.xaml
@@ -0,0 +1,52 @@
+<!--
+ 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. 
+-->
+<phone:PhoneApplicationPage 
+    x:Class="WPCordovaClassLib.Cordova.UI.VideoRecorder"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="480"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    SupportedOrientations="Landscape" Orientation="LandscapeLeft"
+    shell:SystemTray.IsVisible="False">
+   
+    <Canvas x:Name="LayoutRoot" Background="Transparent" Grid.ColumnSpan="1" Grid.Column="0">
+
+        <Rectangle 
+            x:Name="viewfinderRectangle"
+            Width="640" 
+            Height="480" 
+            HorizontalAlignment="Left" 
+            Canvas.Left="80"/>
+        
+    </Canvas>
+
+    <phone:PhoneApplicationPage.ApplicationBar>
+        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" x:Name="PhoneAppBar" Opacity="0.0">
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar.feature.video.rest.png" Text="Record"  x:Name="btnStartRecording" Click="StartRecording_Click" />
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar.save.rest.png" Text="Take" x:Name="btnTakeVideo" Click="TakeVideo_Click"/>            
+        </shell:ApplicationBar>
+    </phone:PhoneApplicationPage.ApplicationBar>
+
+</phone:PhoneApplicationPage>


[04/50] git commit: Added missing bin scripts to windows phone

Posted by br...@apache.org.
Added missing bin scripts to windows phone


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

Branch: refs/heads/master2
Commit: f283c8ab007a8e66c2210656324ae5361874a69f
Parents: c7253d6
Author: Benn Mapes <be...@gmail.com>
Authored: Thu May 23 11:01:11 2013 -0700
Committer: Ian Clelland <ic...@chromium.org>
Committed: Thu May 23 16:04:02 2013 -0400

----------------------------------------------------------------------
 lib/cordova-wp7/bin/check_reqs     |   24 ++
 lib/cordova-wp7/bin/check_reqs.bat |    9 +
 lib/cordova-wp7/bin/check_reqs.js  |  111 ++++++++++
 lib/cordova-wp7/bin/create.bat     |    9 +
 lib/cordova-wp7/bin/create.js      |    7 +-
 lib/cordova-wp8/bin/check_reqs     |   24 ++
 lib/cordova-wp8/bin/check_reqs.bat |    9 +
 lib/cordova-wp8/bin/check_reqs.js  |  111 ++++++++++
 lib/cordova-wp8/bin/create.bat     |    9 +
 lib/cordova-wp8/bin/create.js      |  268 +++++++++++++++++++++++
 lib/cordova-wp8/bin/update.bat     |    9 +
 lib/cordova-wp8/bin/update.js      |  353 +++++++++++++++++++++++++++++++
 12 files changed, 939 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp7/bin/check_reqs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/bin/check_reqs b/lib/cordova-wp7/bin/check_reqs
new file mode 100644
index 0000000..d1b9bc0
--- /dev/null
+++ b/lib/cordova-wp7/bin/check_reqs
@@ -0,0 +1,24 @@
+#! /bin/sh
+
+#
+# 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.
+#
+
+echo "ERROR: Cordova tooling for Windows Phone requires a Windows OS with the 'msbuild' command "
+echo " in the PATH environment variable as well as having .NET Framework 4.0 (from WP SDK's)"
+exit 1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp7/bin/check_reqs.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/bin/check_reqs.bat b/lib/cordova-wp7/bin/check_reqs.bat
new file mode 100644
index 0000000..26e7393
--- /dev/null
+++ b/lib/cordova-wp7/bin/check_reqs.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%check_reqs.js (
+        cscript "%full_path%check_reqs.js" %* //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'check_reqs.js' in 'bin' folder, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp7/bin/check_reqs.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/bin/check_reqs.js b/lib/cordova-wp7/bin/check_reqs.js
new file mode 100644
index 0000000..3dc1496
--- /dev/null
+++ b/lib/cordova-wp7/bin/check_reqs.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.
+*/
+
+
+var args = WScript.Arguments;
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var REQUIRE_GIT = false;
+
+function Usage() {
+    Log("Usage: [ check_reqs | cscript check_reqs.js ]");
+    Log("examples:");
+    Log("    cscript C:\\Users\\anonymous\\cordova-wp7\\bin\\check_reqs.js");
+    Log("    CordovaWindowsPhone\\bin\\check_reqs");
+
+}
+
+// log to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// gets the output from a command, failing with the given error message
+function check_command(cmd, fail_msg) {
+    var out = wscript_shell.Exec(cmd);
+    while (out.Status == 0) {
+        WScript.Sleep(100);
+    }
+
+    //Check that command executed 
+    if (!out.StdErr.AtEndOfStream) {
+        var line = out.StdErr.ReadLine();
+        Log(fail_msg, true);
+        Log('Output : ' + line, true);
+        WScript.Quit(1);
+    }
+
+    if (!out.StdOut.AtEndOfStream) {
+        var line = out.StdOut.ReadAll();
+        return line;
+    }
+    else {
+         Log('Unable to get output from command "' + cmd + '"', true);
+         WScript.Quit(1);
+    }
+}
+
+/* The tooling for cordova windows phone requires these commands
+ *  in the environment PATH variable.
+ * - msbuild (C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319)
+ * - git? (for dynamic cli loading of projects?)
+ */
+function SystemRequiermentsMet() {
+    var cmd = 'msbuild -version'
+    var fail_msg = 'The command `msbuild` failed. Make sure you have the latest Windows Phone SDKs installed, and the `msbuild.exe` command (inside C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319) is added to your path.'
+    var output = check_command(cmd, fail_msg);
+    var msversion = output.match(/\.NET\sFramework\,\sversion\s4\.0/);
+    if (!msversion) {
+        Log('Please install the .NET Framwork v4.0.30319 (in the latest windows phone SDK\'s).', true);
+        Log('Make sure the "msbuild" command in your path is pointing to  v4.0.30319 of msbuild as well (inside C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319).', true);
+        WScript.Quit(1);
+    }
+
+    if(REQUIRE_GIT) {
+        cmd = 'git --version';
+        fail_msg = 'The command `git` failed. Make sure you have git installed as well ad in your PATH environment so the tool can use it';
+        output = check_command(cmd, fail_msg);
+        var gitVersion = output.match(/git\sversion\s1\./);
+        if (!gitVersion) {
+            Log('Please ensure you have at least git v1 installed and added to you PATH so this tool can use it to get the latest codova.');
+        }
+    }
+}
+
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help" || args(0) == "-h") {
+        Usage();
+        WScript.Quit(1);
+    }
+    else {
+        Log('Error : Did not recognize argument ' + args(0), true)
+        Usage();
+        WScript.Quit(1);
+    }
+}
+
+SystemRequiermentsMet();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp7/bin/create.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/bin/create.bat b/lib/cordova-wp7/bin/create.bat
new file mode 100644
index 0000000..329048e
--- /dev/null
+++ b/lib/cordova-wp7/bin/create.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%create.js (
+        cscript "%full_path%create.js" %* //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'create.js' in 'bin' folder, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp7/bin/create.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/bin/create.js b/lib/cordova-wp7/bin/create.js
index dac48de..a789e20 100644
--- a/lib/cordova-wp7/bin/create.js
+++ b/lib/cordova-wp7/bin/create.js
@@ -172,11 +172,10 @@ function create(path, namespace, name) {
     replaceInFile(path + "\\MainPage.xaml.cs",/\$safeprojectname\$/g,namespace);
     replaceInFile(path + "\\CordovaAppProj.csproj",/\$safeprojectname\$/g,namespace);
     if (NAME != "CordovaAppProj") {
-        var valid_name = NAME.replace(/(\.\s|\s\.|\s+|\.+)/g, '_');
-        replaceInFile(path + "\\CordovaSolution.sln", /CordovaAppProj/g, valid_name);
+        replaceInFile(path + "\\CordovaSolution.sln",/CordovaAppProj/g,NAME);
         // rename project and solution
-        exec('%comspec% /c ren ' + path + "\\CordovaSolution.sln " + valid_name + '.sln');
-        exec('%comspec% /c ren ' + path + "\\CordovaAppProj.csproj " + valid_name + '.csproj');
+        exec('%comspec% /c ren ' + path + "\\CordovaSolution.sln " + NAME + '.sln');
+        exec('%comspec% /c ren ' + path + "\\CordovaAppProj.csproj " + NAME + '.csproj');
     }
 
     //copy .dll if necessary

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp8/bin/check_reqs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/bin/check_reqs b/lib/cordova-wp8/bin/check_reqs
new file mode 100644
index 0000000..d1b9bc0
--- /dev/null
+++ b/lib/cordova-wp8/bin/check_reqs
@@ -0,0 +1,24 @@
+#! /bin/sh
+
+#
+# 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.
+#
+
+echo "ERROR: Cordova tooling for Windows Phone requires a Windows OS with the 'msbuild' command "
+echo " in the PATH environment variable as well as having .NET Framework 4.0 (from WP SDK's)"
+exit 1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp8/bin/check_reqs.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/bin/check_reqs.bat b/lib/cordova-wp8/bin/check_reqs.bat
new file mode 100644
index 0000000..26e7393
--- /dev/null
+++ b/lib/cordova-wp8/bin/check_reqs.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%check_reqs.js (
+        cscript "%full_path%check_reqs.js" %* //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'check_reqs.js' in 'bin' folder, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp8/bin/check_reqs.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/bin/check_reqs.js b/lib/cordova-wp8/bin/check_reqs.js
new file mode 100644
index 0000000..492b13f
--- /dev/null
+++ b/lib/cordova-wp8/bin/check_reqs.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.
+*/
+
+
+var args = WScript.Arguments;
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+
+var REQUIRE_GIT = false;
+
+function Usage() {
+    Log("Usage: [ check_reqs | cscript check_reqs.js ]");
+    Log("examples:");
+    Log("    cscript C:\\Users\\anonymous\\cordova-wp8\\bin\\check_reqs.js");
+    Log("    CordovaWindowsPhone\\bin\\check_reqs");
+
+}
+
+// log to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// gets the output from a command, failing with the given error message
+function check_command(cmd, fail_msg) {
+    var out = wscript_shell.Exec(cmd);
+    while (out.Status == 0) {
+        WScript.Sleep(100);
+    }
+
+    //Check that command executed 
+    if (!out.StdErr.AtEndOfStream) {
+        var line = out.StdErr.ReadLine();
+        Log(fail_msg, true);
+        Log('Output : ' + line, true);
+        WScript.Quit(1);
+    }
+
+    if (!out.StdOut.AtEndOfStream) {
+        var line = out.StdOut.ReadAll();
+        return line;
+    }
+    else {
+         Log('Unable to get output from command "' + cmd + '"', true);
+         WScript.Quit(1);
+    }
+}
+
+/* The tooling for cordova windows phone requires these commands
+ *  in the environment PATH variable.
+ * - msbuild (C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319)
+ * - git? (for dynamic cli loading of projects?)
+ */
+function SystemRequiermentsMet() {
+    var cmd = 'msbuild -version'
+    var fail_msg = 'The command `msbuild` failed. Make sure you have the latest Windows Phone SDKs installed, and the `msbuild.exe` command (inside C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319) is added to your path.'
+    var output = check_command(cmd, fail_msg);
+    var msversion = output.match(/\.NET\sFramework\,\sversion\s4\.0/);
+    if (!msversion) {
+        Log('Please install the .NET Framwork v4.0.30319 (in the latest windows phone SDK\'s).', true);
+        Log('Make sure the "msbuild" command in your path is pointing to  v4.0.30319 of msbuild as well (inside C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319).', true);
+        WScript.Quit(1);
+    }
+
+    if(REQUIRE_GIT) {
+        cmd = 'git --version';
+        fail_msg = 'The command `git` failed. Make sure you have git installed as well ad in your PATH environment so the tool can use it';
+        output = check_command(cmd, fail_msg);
+        var gitVersion = output.match(/git\sversion\s1\./);
+        if (!gitVersion) {
+            Log('Please ensure you have at least git v1 installed and added to you PATH so this tool can use it to get the latest codova.');
+        }
+    }
+}
+
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help" || args(0) == "-h") {
+        Usage();
+        WScript.Quit(1);
+    }
+    else {
+        Log('Error : Did not recognize argument ' + args(0), true)
+        Usage();
+        WScript.Quit(1);
+    }
+}
+
+SystemRequiermentsMet();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp8/bin/create.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/bin/create.bat b/lib/cordova-wp8/bin/create.bat
new file mode 100644
index 0000000..329048e
--- /dev/null
+++ b/lib/cordova-wp8/bin/create.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%create.js (
+        cscript "%full_path%create.js" %* //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'create.js' in 'bin' folder, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp8/bin/create.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/bin/create.js b/lib/cordova-wp8/bin/create.js
new file mode 100644
index 0000000..d5e7c46
--- /dev/null
+++ b/lib/cordova-wp8/bin/create.js
@@ -0,0 +1,268 @@
+/*
+       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.
+*/
+
+/*
+ * create a cordova/wp8 project
+ *
+ * USAGE
+ *  ./create [path package activity]
+
+    ./bin/create.bat C:\Users\Me\MyTestProj "test.proj" "TestProject"
+ */
+
+
+var fso=WScript.CreateObject("Scripting.FileSystemObject");
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\bin\\create.js').join('');
+
+var args = WScript.Arguments,
+    FRAMEWORK_PATH = '\\framework',
+    TOOLING_PATH = '\\tooling',
+    TEMPLATES_PATH = '\\templates',
+    // sub folder for standalone project
+    STANDALONE_PATH = TEMPLATES_PATH + '\\standalone',
+    // default template to use when creating the project
+    CREATE_TEMPLATE = STANDALONE_PATH,
+    USE_DLL = false,
+    PROJECT_PATH, 
+    PACKAGE, 
+    NAME,
+    GUID;
+
+
+    // get version number
+var VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
+var BASE_VERSION = VERSION.split('rc', 1) + ".0";
+
+function Usage() {
+    Log("Usage: create PathTONewProject [ PackageName AppName ]");
+    Log("    PathTONewProject : The path to where you wish to create the project");
+    Log("    PackageName      : The namespace for the project (default is Cordova.Example)")
+    Log("    AppName          : The name of the application (default is CordovaAppProj)");
+    Log("examples:");
+    Log("    create C:\\Users\\anonymous\\Desktop\\MyProject");
+    Log("    create C:\\Users\\anonymous\\Desktop\\MyProject io.Cordova.Example AnApp");
+}
+
+// logs messaged to stdout and stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+var ForReading = 1, ForWriting = 2, ForAppending = 8;
+var TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0;
+
+function read(filename) {
+    var f=fso.OpenTextFile(filename, 1,2);
+    var s=f.ReadAll();
+    f.Close();
+    return s;
+}
+
+function write(filename, contents) {
+    var f=fso.OpenTextFile(filename, ForWriting, TristateTrue);
+    f.Write(contents);
+    f.Close();
+}
+
+function replaceInFile(filename, regexp, replacement) {
+    write(filename,read(filename).replace(regexp,replacement));
+}
+
+
+// executes a commmand in the shell
+function exec(command) {
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        WScript.sleep(100);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    //Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if (!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oShell.StdErr.AtEndOfStream) {
+        var line = oShell.StdErr.ReadAll();
+        Log(line, true);
+        WScript.Quit(1);
+    }
+}
+
+//generate guid for the project
+function genGuid() {
+    var TypeLib = WScript.CreateObject("Scriptlet.TypeLib");
+    strGuid = TypeLib.Guid.split("}")[0]; // there is extra crap after the } that is causing file streams to break, probably an EOF ... 
+    strGuid = strGuid.replace(/[\{\}]/g,""); 
+    return strGuid;
+}
+
+// builds the new cordova dll from the framework
+function build_dll(path) {
+    if (fso.FolderExists(path + FRAMEWORK_PATH + '\\Bin')) {
+        fso.DeleteFolder(path + FRAMEWORK_PATH + '\\Bin');
+    }
+    if (fso.FolderExists(path + FRAMEWORK_PATH + '\\obj')) {
+        fso.DeleteFolder(path + FRAMEWORK_PATH + '\\obj');
+    }
+    // move to framework directory
+    wscript_shell.CurrentDirectory = path + FRAMEWORK_PATH;
+    // build .dll in Release
+    exec_verbose('msbuild /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo /p:Configuration=Release;VersionNumber=' + VERSION + ';BaseVersionNumber=' + BASE_VERSION);
+    //Check if file dll was created
+    if (!fso.FileExists(path + FRAMEWORK_PATH + '\\Bin\\Release\\WPCordovaClassLib.dll')) {
+        Log('ERROR: MSBuild failed to create .dll when building WPCordovaClassLib.dll', true);
+        WScript.Quit(1);
+    }
+    Log("SUCCESS BUILDING DLL");
+}
+
+// creates new project in path, with the given package and app name
+function create(path, namespace, name, guid) {
+    Log("Creating Cordova-WP7 Project:");
+    Log("\tApp Name : " + name);
+    Log("\tNamespace : " + namespace);
+    Log("\tPath : " + path);
+
+    // Copy the template source files to the new destination
+    fso.CopyFolder(ROOT + CREATE_TEMPLATE, path);
+    var newProjGuid;
+    if (guid) {
+        newProjGuid = guid;
+    } else {
+        newProjGuid = genGuid();
+    }
+    // replace the guid in the AppManifest
+    replaceInFile(path + "\\Properties\\WMAppManifest.xml","$guid1$",newProjGuid);
+    // replace safe-project-name in AppManifest
+    replaceInFile(path + "\\Properties\\WMAppManifest.xml",/\$safeprojectname\$/g,name);
+    replaceInFile(path + "\\Properties\\WMAppManifest.xml",/\$projectname\$/g,name);
+
+
+    replaceInFile(path + "\\App.xaml",/\$safeprojectname\$/g,namespace);
+    replaceInFile(path + "\\App.xaml.cs",/\$safeprojectname\$/g,namespace);
+
+    replaceInFile(path + "\\MainPage.xaml",/\$safeprojectname\$/g,namespace);
+    replaceInFile(path + "\\MainPage.xaml.cs",/\$safeprojectname\$/g,namespace);
+    replaceInFile(path + "\\CordovaAppProj.csproj",/\$safeprojectname\$/g,namespace);
+    if (NAME != "CordovaAppProj") {
+        var valid_name = NAME.replace(/(\.\s|\s\.|\s+|\.+)/g, '_');
+        replaceInFile(path + "\\CordovaSolution.sln", /CordovaAppProj/g, valid_name);
+        // rename project and solution
+        exec('%comspec% /c ren ' + path + "\\CordovaSolution.sln " + valid_name + '.sln');
+        exec('%comspec% /c ren ' + path + "\\CordovaAppProj.csproj " + valid_name + '.csproj');
+    }
+
+    //copy .dll if necessary
+    if (USE_DLL) {
+        var dllPath = ROOT + FRAMEWORK_PATH + '\\Bin\\Release\\WPCordovaClassLib.dll';
+        if (fso.FileExists(dllPath)) {
+            Log("WPCordovaClassLib.dll Found,  creating project");
+        }
+        else {
+            Log("WPCordovaClassLib.dll was not Found in " + dllPath);
+            Log('BUILDING: WPCordovaClassLib.dll');
+            build_dll(ROOT);
+        }
+
+        if (!fso.FolderExists(path + '\\CordovaLib')) {
+            fso.CreateFolder(path + '\\CordovaLib');
+        }
+        exec('%comspec% /c xcopy ' + ROOT + FRAMEWORK_PATH + '\\Bin\\Release\\WPCordovaClassLib.dll ' + path + '\\CordovaLib');
+        if (!fso.FileExists(path + '\\CordovaLib\\WPCordovaClassLib.dll')) {
+            Log('ERROR: Failed to copy WPCordovaClassLib.dll to project from', true);
+            Log('\t' + ROOT + FRAMEWORK_PATH + '\\Bin\\Release\\WPCordovaClassLib.dll', true);
+            Log('\tto', true);
+            Log('\t' + path + '\\CordovaLib', true)
+            WScript.Quit(1);
+        }
+    }
+
+    Log("CREATE SUCCESS : " + path);
+
+    // TODO: Name the project according to the arguments
+    // update the solution to include the new project by name
+    // version BS
+    // index.html title set to project name ?
+
+}
+
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help" || args(0) == "-h") {
+        Usage();
+        WScript.Quit(1);
+    }
+
+    PROJECT_PATH = args(0);
+    if (fso.FolderExists(PROJECT_PATH)) {
+        Log("Project directory already exists:", true);
+        Log("\t" + PROJECT_PATH, true);
+        Log("CREATE FAILED.", true);
+        WScript.Quit(1);
+    }
+
+    if (args.Count() > 1) {
+        PACKAGE = args(1);
+    }
+    else {
+        PACKAGE = "Cordova.Example";
+    }
+
+    if (args.Count() > 2) {
+        NAME = args(2);
+    }
+    else {
+        NAME = "CordovaAppProj";
+    }
+
+    if (args.Count() > 3) {
+        var guid_regex = /\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}/;
+        if (args(3).substr(0,7) == "--guid=" && args(3).match(guid_regex)) {
+            GUID = args(3).split
+        } else {
+            Log("Did not recognize argument '" + args(3) + "'. If your trying to add a GUID make sure it's in the proper format.");
+            WScript.Quit(2);
+        }
+    }
+
+    create(PROJECT_PATH, PACKAGE, NAME, GUID);
+}
+else {
+    Usage();
+    WScript.Quit(1);
+}
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp8/bin/update.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/bin/update.bat b/lib/cordova-wp8/bin/update.bat
new file mode 100644
index 0000000..9da7e3c
--- /dev/null
+++ b/lib/cordova-wp8/bin/update.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%update.js (
+        cscript "%full_path%update.js" %* //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'update.js' in 'bin' folder, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f283c8ab/lib/cordova-wp8/bin/update.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/bin/update.js b/lib/cordova-wp8/bin/update.js
new file mode 100644
index 0000000..f2acd6f
--- /dev/null
+++ b/lib/cordova-wp8/bin/update.js
@@ -0,0 +1,353 @@
+/*
+       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 fso           = WScript.CreateObject("Scripting.FileSystemObject");
+var wscript_shell = WScript.CreateObject("WScript.Shell");
+var shell         = WScript.CreateObject("shell.application");
+var args          = WScript.Arguments;
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\bin\\update.js').join('');
+//Get version number
+var VERSION = read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
+var plugins_folder = "\\Plugins";
+var template_folder = "\\templates\\standalone";
+// anything thats missing to the project
+var overwrite = false;
+var replace = false;
+
+// usage function
+function Usage() {
+    Log("WARNING : Make sure to back up your project before updating!")
+    Log("Usage: update Path-To-Project ");//[ -f | -r ] ");
+    Log("    Path-To-Old-Project : The path the project you would like to update.");
+    //Log("                     -f : Will forcefully overwrite and add all core components of the application.");
+    //Log("                     -r : Will create an updated project, only keeping the www assets. *NOTE: no native code will be preserved*");
+    Log("examples:");
+    Log("    update C:\\Users\\anonymous\\Desktop\\MyProject");
+}
+
+// logs messaged to stdout and stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
+
+// executes a commmand in the shell
+function exec(command) {
+    Log("Command : " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status === 0) {
+        WScript.sleep(100);
+    }
+}
+
+// executes a commmand in the shell
+function exec_verbose(command) {
+    Log("Command: " + command);
+    var oShell=wscript_shell.Exec(command);
+    while (oShell.Status == 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if (!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadAll();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oShell.StdErr.AtEndOfStream) {
+        var line = oShell.StdErr.ReadAll();
+        Log(line, true);
+        WScript.Quit(2);
+    }
+}
+
+var ForReading = 1, ForWriting = 2, ForAppending = 8;
+var TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0;
+
+// returns the contents of a file
+function read(filename) {
+    if (fso.FileExists(filename)) {
+        var f=fso.OpenTextFile(filename, 1, 2);
+        var s=f.ReadAll();
+        f.Close();
+        return s;
+    }
+    else {
+        Log('Cannot read non-existant file : ' + filename, true);
+        WScript.Quit(2);
+    }
+    return null;
+}
+
+// writes the contents to the specified file
+function write(filename, contents) {
+    var f=fso.OpenTextFile(filename, ForWriting, TristateTrue);
+    f.Write(contents);
+    f.Close();
+}
+
+// replaces the matches of regexp with replacement
+function replaceInFile(filename, regexp, replacement) {
+    var text = read(filename).replace(regexp,replacement);
+    write(filename,text);
+}
+
+// returns true if the given path is the root of a cordova windows phone project
+// currently returns true if the folder contains a .csproj file.
+function is_windows_phone_project(path) {
+    if (fso.FolderExists(path)) {
+        var proj_folder = fso.GetFolder(path);
+        var proj_files = new Enumerator(proj_folder.Files);
+        for (;!proj_files.atEnd(); proj_files.moveNext()) {
+            if (fso.GetExtensionName(proj_files.item()) == 'csproj') {
+                return true;  
+            }
+        }
+    }
+    return false;
+}
+
+// returns the name of the application
+function get_app_name(path) {
+    var WMAppManifest = read(path + '\\Properties\\WMAppManifest.xml').split('\n');
+    for (line in WMAppManifest) {
+        if (WMAppManifest[line].match(/Title\=\"/)) {
+            return WMAppManifest[line].split('Title="')[1].split('"')[0];
+        }
+    }
+    Log("Error : unable to find applicaiton name in the project.", true);
+    Log(" Path : " + path, true);
+    WScript.Quit(2);
+}
+
+// returns the name of the application package
+function get_package_name(path) {
+    var WMAppManifest = read(path + '\\Properties\\WMAppManifest.xml').split('\n');
+    for (line in WMAppManifest) {
+        if (WMAppManifest[line].match(/Title\=\"/)) {
+            return WMAppManifest[line].split('Title="')[1].split('"')[0];
+        }
+    }
+    Log("Error : unable to find applicaiton name in the project.", true);
+    Log(" Path : " + path, true);
+    WScript.Quit(2);
+}
+
+// returns the GUID ame of the application
+function get_app_GUID(path) {
+    var AppXAML = read(path + '\\App.xaml').split('\n');
+    for (line in AppXAML) {
+        if (AppXAML[line].match(/x\:Class\=\"/)) {
+            return AppXAML[line].split('Class="')[1].split('"')[0];
+        }
+    }
+    Log("Error : unable to find package name in the project.", true);
+    Log(" Path : " + path, true);
+    WScript.Quit(2);
+}
+
+// updates the cordova.js and all references in the given project with this repositories version
+function update_cordova_js(path) {
+    // remove old cordova.js
+    var www_contents = shell.NameSpace(path + '\\www').Items();
+    for(i = 0; i < www_contents.Count; i++)
+    {
+        if(www_contents.Item(i).Name.match(/cordova\-(\d+)[.](\d+)[.](\d+)(rc\d)?[.]js/))
+        {
+            fso.DeleteFile(path + '\\www\\' + www_contents.Item(i).Name);
+        }
+    }
+    // update version file
+    copy_to(ROOT + "\\VERSION",  path + "\\VERSION");
+    // copy over new cordova.js
+    copy_to(ROOT + template_folder + "\\www\\cordova.js", path + "\\www\\cordova.js");
+
+    // update corodva references
+    var cordova_regex = /cordova-(\d+)[.](\d+)[.](\d+)(rc\d)?/g; //Matches *first* cordova-x.x.x[rcx] (just ad g at end to make global)
+    // update references in index.html
+    replaceInFile(path + '\\www\\index.html', cordova_regex,  "cordova");
+    version_regex = /return\s*\"(\d+)[.](\d+)[.](\d+)(rc\d)?/; //Matches return "x.x.x[rcx]
+    // update references in Device.cs
+    replaceInFile(path + '\\Plugins\\Device.cs', version_regex,  "return \"" + VERSION);
+}
+
+// Copies assets that need to be saved from source to desination.
+// TODO : Add all critical assets here
+function save_restore(source, destination) {
+    fso.CreateFolder(destination + '\\www');
+    copy_to(source + '\\www', destination + '\\www');
+    copy_to(source + '\\SplashScreenImage.jpg', destination + '\\SplashScreenImage.jpg');
+    copy_to(source + '\\Background.png', destination + '\\Background.png');
+    copy_to(source + '\\ApplicationIcon.png', destination + '\\ApplicationIcon.png');
+    copy_to(source + '\\config.xml', destination + '\\config.xml');
+}
+
+// deletes the path element if it exists
+function delete_if_exists(path) {
+    if (fso.FolderExists(path)) {
+        fso.DeleteFolder(path);
+    }
+    else if (fso.FileExists(path)) {
+        fso.DeleteFile(path);
+    }
+}
+
+// copies a folder or file from source to destination
+function copy_to(source, destination) {
+    // check that source exists
+    if (!fso.FolderExists(source)) {
+        if (!fso.FileExists(source)) {
+            Log("Error : Could not copy file/folder because it doesn't exist.", true);
+            Log("      File/Folder : " + source, true);
+            WScript.Quit(2);
+        }
+    }
+    // if source is a folder, then copy all folder contents
+    if (fso.FolderExists(source)) {
+        fso.CopyFolder(source, destination, true);
+    } 
+    // if it's a file, just copy it.
+    else { 
+        exec('%comspec% /c copy /Y /V ' + source + ' ' + destination);
+    }
+}
+
+// updates the cordova.js in project along with the cordova tooling.
+function update_project(path) {
+    // update cordova folder
+    delete_if_exists(path + '\\cordova');
+    fso.CreateFolder(path + '\\cordova');
+    copy_to(ROOT + template_folder + '\\cordova', path + '\\cordova');
+    // clean project (all generated files)
+    exec(path + '\\cordova\\clean.bat');
+
+    // update core cordovalib
+    delete_if_exists(path + '\\cordovalib');
+    fso.CreateFolder(path + '\\cordovalib');
+    copy_to(ROOT + template_folder + '\\cordovalib', path + '\\cordovalib');
+
+    // update core plugins
+    // TODO : Remove for 3.0.0
+    delete_if_exists(path + '\\Plugins');
+    fso.CreateFolder(path + '\\Plugins');
+    copy_to(ROOT + template_folder + '\\Plugins', path + '\\Plugins');
+
+    // update cordova.js
+    update_cordova_js(path);
+}
+
+// Replaces the current project with a newly created project, keeping important assets to preserve the app.
+// TODO: Things that need to be kept other then www
+// - WMAppManifest (capabilities etc...)
+// - GUID (for marketplace apps etc...)
+// - Splashscreen and other images etc...
+// - Find more things that should be kept
+function replace_project(path) {
+    //create new project and move www assets into it.
+    Log("WARNING : Upgrading your app with the \'-r\' flag will delete all native and plugin");
+    Log(" components of your application and replace them with the updated core components given");
+    Log(" by this platforms \'bin\\create\' script.  It is *HIGHLY RECOMMENDED* to back up your app");
+    Log(" before continuing. The name and package name along with all of the www assets will be");
+    Log(" preserved. Are you sure you wish to continue? (Y/N)");
+    var response;
+    while (response != 'Y') {
+        response = WScript.StdIn.ReadLine();
+        if (response == 'N') {
+            WScript.Quit(2);
+        } else if (response != "Y") {
+            Log("Error :  did not recognize '" + response + "'");
+            Log("Are you sure you wish to continue? (Y/N)");
+        }
+    }
+    // place all assets to be preserved in a temperary folder
+    delete_if_exists(ROOT + '\\temp');
+    fso.CreateFolder(ROOT + '\\temp');
+    save_restore(path, ROOT + '\\temp');
+
+    // get app name from WMAppManifest
+    var app_name = get_app_name(path);
+    // get package name from App.xaml
+    var package_name = get_package_name(path);
+    // get the GUID so that app stays the same
+    var app_GUID = get_app_GUID(path);
+    // delete previous project
+    delete_if_exists(path);
+    // create the new project from the current repository
+    exec(ROOT + '\\bin\\create.bat ' + path + ' ' + app_name + ' ' + package_name);
+    // remove default www assets
+    delete_if_exists(path + '\\www');
+    // move www assets back to project folder
+    save_restore(ROOT + '\\temp', path);
+    // cleanup temp folder
+    delete_if_exists(ROOT + '\\temp');
+}
+
+
+
+if (args.Count() > 0) {
+    if(args.Count() > 2) {
+        Log("Error : too many arguments provided.", true);
+        WScript.Quit(1);
+    }
+
+    if (args(0).indexOf("--help") > -1 ||
+          args(0).indexOf("/?") > -1 ) {
+        Usage();
+        WScript.Quit(1);
+    }
+    else if (fso.FolderExists(args(0)) && is_windows_phone_project(args(0))) {
+        if(args.Count() > 1) {
+            /*if(args(1) == '-f' || args(1) == '--force') {
+                //TODO: do something for this
+                Log("ERROR : NOT IMPLEMENTED", true);
+                WScript.Quit(2);
+            }
+            else if(args(1) == '-r' || args(1) == '--replace') {
+                replace_project(args(0));
+            }
+            else {
+                Log('Error : \'' + args(1) + '\' is not regognized as an update option', true);
+            }*/
+            Usage();
+            Log('Error : too many arguments', true);
+        } else if (args.Count() == 1) {
+            update_project(args(0));
+        }
+    }
+    else if (fso.FolderExists(args(0))) {
+        Log("The path provided is not a path to a cordova windows phone project.", true);
+        Log(" Please provide the path to the root folder of your cordova windows phone project.", true);
+        WScript.Quit(2);
+    }
+    else {
+        Log("The given path to the project does not exist.", true);
+        Log(" Please provide a path to the project you would like to update.", true);
+        Usage();
+        WScript.Quit(2);
+    }
+}
+else {
+    Usage();
+    WScript.Quit(1);
+}
\ No newline at end of file


[03/50] git commit: Reorganize specs into cordova-cli/ and platform-script/

Posted by br...@apache.org.
Reorganize specs into cordova-cli/ and platform-script/


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

Branch: refs/heads/master2
Commit: 693521da0609cc3c417f26ef023cde6179965fbe
Parents: 72cca5e
Author: Benn Mapes <be...@gmail.com>
Authored: Tue May 14 15:18:41 2013 -0700
Committer: Ian Clelland <ic...@chromium.org>
Committed: Thu May 23 16:04:01 2013 -0400

----------------------------------------------------------------------
 package.json                                       |    2 +-
 spec/compile.spec.js                               |  179 --------
 spec/config_parser.spec.js                         |  167 -------
 spec/cordova-cli/compile.spec.js                   |  179 ++++++++
 spec/cordova-cli/config_parser.spec.js             |  167 +++++++
 spec/cordova-cli/create.spec.js                    |   68 +++
 spec/cordova-cli/emulate.spec.js                   |  191 ++++++++
 spec/cordova-cli/helper.js                         |   20 +
 spec/cordova-cli/hooker.spec.js                    |  137 ++++++
 spec/cordova-cli/platform.spec.js                  |  339 +++++++++++++++
 spec/cordova-cli/plugin.spec.js                    |  164 +++++++
 spec/cordova-cli/plugin_parser.spec.js             |   42 ++
 spec/cordova-cli/prepare.spec.js                   |  133 ++++++
 spec/cordova-cli/serve.spec.js                     |  132 ++++++
 spec/create.spec.js                                |   68 ---
 spec/emulate.spec.js                               |  191 --------
 spec/helper.js                                     |   20 -
 spec/hooker.spec.js                                |  137 ------
 spec/metadata/android_parser.spec.js               |  220 ----------
 spec/metadata/blackberry_parser.spec.js            |  249 -----------
 spec/metadata/ios_parser.spec.js                   |  218 ---------
 .../platform-script/android/android_parser.spec.js |  220 ++++++++++
 .../blackberry/blackberry_parser.spec.js           |  249 +++++++++++
 spec/platform-script/ios/ios_parser.spec.js        |  218 +++++++++
 spec/platform.spec.js                              |  339 ---------------
 spec/plugin.spec.js                                |  164 -------
 spec/plugin_parser.spec.js                         |   42 --
 spec/prepare.spec.js                               |  133 ------
 spec/serve.spec.js                                 |  132 ------
 29 files changed, 2260 insertions(+), 2260 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index 3a41515..96e53bd 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
     "cordova": "./bin/cordova"
   },
   "scripts": {
-    "test": "./node_modules/jasmine-node/bin/jasmine-node --color spec",
+    "test": "./node_modules/jasmine-node/bin/jasmine-node --color spec/cordova-cli",
     "install": "node bootstrap.js"
   },
   "repository": {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/compile.spec.js
----------------------------------------------------------------------
diff --git a/spec/compile.spec.js b/spec/compile.spec.js
deleted file mode 100644
index f13792a..0000000
--- a/spec/compile.spec.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/**
-    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'),
-    et = require('elementtree'),
-    shell = require('shelljs'),
-    path = require('path'),
-    fs = require('fs'),
-    config_parser = require('../src/config_parser'),
-    android_parser = require('../src/metadata/android_parser'),
-    ios_parser = require('../src/metadata/ios_parser'),
-    blackberry_parser = require('../src/metadata/blackberry_parser'),
-    hooker = require('../src/hooker'),
-    fixtures = path.join(__dirname, 'fixtures'),
-    hooks = path.join(fixtures, 'hooks'),
-    tempDir = path.join(__dirname, '..', 'temp'),
-    cordova_project = path.join(fixtures, 'projects', 'cordova');
-
-var cwd = process.cwd();
-describe('compile command', function() {
-    beforeEach(function() {
-        shell.rm('-rf', tempDir);
-        shell.mkdir('-p', tempDir);
-    });
-
-    it('should not run inside a Cordova-based project with no added platforms', function() {
-        this.after(function() {
-            process.chdir(cwd);
-        });
-
-        cordova.create(tempDir);
-        process.chdir(tempDir);
-        expect(function() {
-            cordova.compile();
-        }).toThrow();
-    });
-    
-    it('should run inside a Cordova-based project with at least one added platform', function() {
-        // move platform project fixtures over to fake cordova into thinking platforms were added
-        // TODO: possibly add this to helper?
-        shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
-        shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
-        this.after(function() {
-            process.chdir(cwd);
-            shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
-            shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
-        });
-
-        process.chdir(cordova_project);
-
-        var sh_spy = spyOn(shell, 'exec');
-
-        expect(function() {
-            cordova.compile();
-            expect(sh_spy).toHaveBeenCalled();
-        }).not.toThrow();
-    });
-    it('should not run outside of a Cordova-based project', function() {
-        this.after(function() {
-            process.chdir(cwd);
-        });
-
-        shell.mkdir('-p', tempDir);
-        process.chdir(tempDir);
-
-        expect(function() {
-            cordova.compile();
-        }).toThrow();
-    });
-
-    describe('hooks', function() {
-        var s;
-        beforeEach(function() {
-            s = spyOn(hooker.prototype, 'fire').andReturn(true);
-        });
-
-        describe('when platforms are added', function() {
-            beforeEach(function() {
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
-                process.chdir(cordova_project);
-            });
-            afterEach(function() {
-                shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
-                shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
-                process.chdir(cwd);
-            });
-
-            it('should fire before hooks through the hooker module', function() {
-                spyOn(shell, 'exec');
-                cordova.compile();
-                expect(s).toHaveBeenCalledWith('before_compile');
-            });
-            it('should fire after hooks through the hooker module', function() {
-                var sh_spy = spyOn(shell, 'exec');
-                cordova.compile();
-                sh_spy.mostRecentCall.args[2](0); // shell cb
-                expect(s).toHaveBeenCalledWith('after_compile');
-            });
-        });
-
-        describe('with no platforms added', function() {
-            beforeEach(function() {
-                cordova.create(tempDir);
-                process.chdir(tempDir);
-            });
-            afterEach(function() {
-                process.chdir(cwd);
-            });
-            it('should not fire the hooker', function() {
-                expect(function() {
-                    cordova.compile();
-                }).toThrow();
-                expect(s).not.toHaveBeenCalledWith('before_compile');
-                expect(s).not.toHaveBeenCalledWith('after_compile');
-            });
-        });
-    });
-    describe('per platform', function() {
-        beforeEach(function() {
-            process.chdir(cordova_project);
-        });
-
-        afterEach(function() {
-            process.chdir(cwd);
-        });
-       
-        describe('Android', function() {
-            it('should shell out to build command on Android', function() {
-                var s = spyOn(require('shelljs'), 'exec').andReturn({code:0});
-                cordova.compile('android');
-                expect(s.mostRecentCall.args[0].match(/\/cordova\/build/)).not.toBeNull();
-            });
-        });
-        describe('iOS', function() {
-            it('should shell out to build command on iOS', function() {
-                var s = spyOn(require('shelljs'), 'exec');
-                cordova.compile('ios');
-                expect(s).toHaveBeenCalled();
-                expect(s.mostRecentCall.args[0].match(/\/cordova\/build/)).not.toBeNull();
-            });
-        });
-        describe('BlackBerry', function() {
-            it('should shell out to ant command on blackberry', function() {
-                var s = spyOn(shell, 'exec');
-                cordova.compile('blackberry');
-                expect(s).toHaveBeenCalled();
-                expect(s.mostRecentCall.args[0]).toMatch(/ant -f .*build\.xml" qnx load-device/);
-            });
-        });
-        it('should not treat a .gitignore file as a platform', function() {
-            var gitignore = path.join(cordova_project, 'platforms', '.gitignore');
-            fs.writeFileSync(gitignore, 'somethinghere', 'utf-8');
-            this.after(function() {
-                shell.rm('-f', gitignore);
-            });
-            var s = spyOn(shell, 'exec');
-            cordova.compile();
-            expect(s.calls[0].args[0]).not.toMatch(/\.gitignore/);
-            expect(s.calls[1].args[0]).not.toMatch(/\.gitignore/);
-            expect(s.calls[1].args[0]).not.toMatch(/\.gitignore/);
-        });
-    });
-});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/config_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/config_parser.spec.js b/spec/config_parser.spec.js
deleted file mode 100644
index 99bc717..0000000
--- a/spec/config_parser.spec.js
+++ /dev/null
@@ -1,167 +0,0 @@
-
-/**
-    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'),
-    fs = require('fs'),
-    shell = require('shelljs'),
-    config_parser = require('../src/config_parser'),
-    tempDir = path.join(__dirname, '..', 'temp'),
-    et = require('elementtree'),
-    xml = path.join(tempDir, 'www', 'config.xml');
-
-
-describe('config.xml parser', function () {
-    beforeEach(function() {
-        shell.rm('-rf', tempDir);
-        cordova.create(tempDir);
-    });
-
-    it('should create an instance based on an xml file', function() {
-        var cfg;
-        expect(function () {
-            cfg = new config_parser(xml);
-        }).not.toThrow();
-        expect(cfg).toBeDefined();
-        expect(cfg.doc).toBeDefined();
-    });
-
-    describe('package name / id', function() {
-        var cfg;
-
-        beforeEach(function() {
-            cfg = new config_parser(xml);
-        });
-
-        it('should get the (default) packagename', function() {
-            expect(cfg.packageName()).toEqual('io.cordova.hellocordova');
-        });
-        it('should allow setting the packagename', function() {
-            cfg.packageName('this.is.bat.country');
-            expect(cfg.packageName()).toEqual('this.is.bat.country');
-        });
-        it('should write to disk after setting the packagename', function() {
-            cfg.packageName('this.is.bat.country');
-            expect(fs.readFileSync(xml, 'utf-8')).toMatch(/id="this\.is\.bat\.country"/);
-        });
-    });
-
-    describe('app name', function() {
-        var cfg;
-
-        beforeEach(function() {
-            cfg = new config_parser(xml);
-        });
-
-        it('should get the (default) app name', function() {
-            expect(cfg.name()).toEqual('HelloCordova');
-        });
-        it('should allow setting the app name', function() {
-            cfg.name('this.is.bat.country');
-            expect(cfg.name()).toEqual('this.is.bat.country');
-        });
-        it('should write to disk after setting the name', function() {
-            cfg.name('one toke over the line');
-            expect(fs.readFileSync(xml, 'utf-8')).toMatch(/<name>one toke over the line<\/name>/);
-        });
-    });
-
-    describe('access elements (whitelist)', function() {
-        var cfg;
-
-        beforeEach(function() {
-            cfg = new config_parser(xml);
-        });
-
-        describe('getter', function() {
-            it('should get the (default) access element', function() {
-                expect(cfg.access.get()[0]).toEqual('*');
-            });
-            it('should return an array of all access origin uris via access()', function() {
-                expect(cfg.access.get() instanceof Array).toBe(true);
-            });
-        });
-        describe('setters', function() {
-            it('should allow removing a uri from the access list', function() {
-                cfg.access.remove('*');
-                expect(cfg.access.get().length).toEqual(0);
-            });
-            it('should write to disk after removing a uri', function() {
-                cfg.access.remove('*');
-                expect(fs.readFileSync(xml, 'utf-8')).not.toMatch(/<access.*\/>/);
-            });
-            it('should allow adding a new uri to the access list', function() {
-                cfg.access.add('http://canucks.com');
-                expect(cfg.access.get().length).toEqual(2);
-                expect(cfg.access.get().indexOf('http://canucks.com') > -1).toBe(true);
-            });
-            it('should write to disk after adding a uri', function() {
-                cfg.access.add('http://cordova.io');
-                expect(fs.readFileSync(xml, 'utf-8')).toMatch(/<access origin="http:\/\/cordova\.io/);
-            });
-            it('should allow removing all access elements when no parameter is specified', function() {
-                cfg.access.add('http://cordova.io');
-                cfg.access.remove();
-
-                expect(fs.readFileSync(xml, 'utf-8')).not.toMatch(/<access.*\/>/);
-            });
-        });
-    });
-
-    describe('preference elements', function() {
-        var cfg;
-
-        beforeEach(function() {
-            cfg = new config_parser(xml);
-        });
-
-        describe('getter', function() {
-            it('should get all preference elements', function() {
-                expect(cfg.preference.get()[0].name).toEqual('phonegap-version');
-                expect(cfg.preference.get()[0].value).toEqual('1.9.0');
-            });
-            it('should return an array of all preference name/value pairs', function() {
-                expect(cfg.preference.get() instanceof Array).toBe(true);
-            });
-        });
-        describe('setters', function() {
-            it('should allow removing a preference by name', function() {
-                cfg.preference.remove('phonegap-version');
-                expect(cfg.preference.get().length).toEqual(3);
-            });
-            it('should write to disk after removing a preference', function() {
-                cfg.preference.remove('phonegap-version');
-                expect(fs.readFileSync(xml, 'utf-8')).not.toMatch(/<preference\sname="phonegap-version"/);
-            });
-            it('should allow adding a new preference', function() {
-                cfg.preference.add({name:'UIWebViewBounce',value:'false'});
-                expect(cfg.preference.get().length).toEqual(5);
-                expect(cfg.preference.get()[4].value).toEqual('false');
-            });
-            it('should write to disk after adding a preference', function() {
-                cfg.preference.add({name:'UIWebViewBounce',value:'false'});
-                expect(fs.readFileSync(xml, 'utf-8')).toMatch(/<preference name="UIWebViewBounce" value="false"/);
-            });
-            it('should allow removing all preference elements when no parameter is specified', function() {
-                cfg.preference.remove();
-                expect(fs.readFileSync(xml, 'utf-8')).not.toMatch(/<preference.*\/>/);
-            });
-        });
-    });
-});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/cordova-cli/compile.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/compile.spec.js b/spec/cordova-cli/compile.spec.js
new file mode 100644
index 0000000..f13792a
--- /dev/null
+++ b/spec/cordova-cli/compile.spec.js
@@ -0,0 +1,179 @@
+/**
+    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'),
+    et = require('elementtree'),
+    shell = require('shelljs'),
+    path = require('path'),
+    fs = require('fs'),
+    config_parser = require('../src/config_parser'),
+    android_parser = require('../src/metadata/android_parser'),
+    ios_parser = require('../src/metadata/ios_parser'),
+    blackberry_parser = require('../src/metadata/blackberry_parser'),
+    hooker = require('../src/hooker'),
+    fixtures = path.join(__dirname, 'fixtures'),
+    hooks = path.join(fixtures, 'hooks'),
+    tempDir = path.join(__dirname, '..', 'temp'),
+    cordova_project = path.join(fixtures, 'projects', 'cordova');
+
+var cwd = process.cwd();
+describe('compile command', function() {
+    beforeEach(function() {
+        shell.rm('-rf', tempDir);
+        shell.mkdir('-p', tempDir);
+    });
+
+    it('should not run inside a Cordova-based project with no added platforms', function() {
+        this.after(function() {
+            process.chdir(cwd);
+        });
+
+        cordova.create(tempDir);
+        process.chdir(tempDir);
+        expect(function() {
+            cordova.compile();
+        }).toThrow();
+    });
+    
+    it('should run inside a Cordova-based project with at least one added platform', function() {
+        // move platform project fixtures over to fake cordova into thinking platforms were added
+        // TODO: possibly add this to helper?
+        shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
+        shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
+        this.after(function() {
+            process.chdir(cwd);
+            shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
+            shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+        });
+
+        process.chdir(cordova_project);
+
+        var sh_spy = spyOn(shell, 'exec');
+
+        expect(function() {
+            cordova.compile();
+            expect(sh_spy).toHaveBeenCalled();
+        }).not.toThrow();
+    });
+    it('should not run outside of a Cordova-based project', function() {
+        this.after(function() {
+            process.chdir(cwd);
+        });
+
+        shell.mkdir('-p', tempDir);
+        process.chdir(tempDir);
+
+        expect(function() {
+            cordova.compile();
+        }).toThrow();
+    });
+
+    describe('hooks', function() {
+        var s;
+        beforeEach(function() {
+            s = spyOn(hooker.prototype, 'fire').andReturn(true);
+        });
+
+        describe('when platforms are added', function() {
+            beforeEach(function() {
+                shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
+                shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
+                process.chdir(cordova_project);
+            });
+            afterEach(function() {
+                shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
+                shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+                process.chdir(cwd);
+            });
+
+            it('should fire before hooks through the hooker module', function() {
+                spyOn(shell, 'exec');
+                cordova.compile();
+                expect(s).toHaveBeenCalledWith('before_compile');
+            });
+            it('should fire after hooks through the hooker module', function() {
+                var sh_spy = spyOn(shell, 'exec');
+                cordova.compile();
+                sh_spy.mostRecentCall.args[2](0); // shell cb
+                expect(s).toHaveBeenCalledWith('after_compile');
+            });
+        });
+
+        describe('with no platforms added', function() {
+            beforeEach(function() {
+                cordova.create(tempDir);
+                process.chdir(tempDir);
+            });
+            afterEach(function() {
+                process.chdir(cwd);
+            });
+            it('should not fire the hooker', function() {
+                expect(function() {
+                    cordova.compile();
+                }).toThrow();
+                expect(s).not.toHaveBeenCalledWith('before_compile');
+                expect(s).not.toHaveBeenCalledWith('after_compile');
+            });
+        });
+    });
+    describe('per platform', function() {
+        beforeEach(function() {
+            process.chdir(cordova_project);
+        });
+
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+       
+        describe('Android', function() {
+            it('should shell out to build command on Android', function() {
+                var s = spyOn(require('shelljs'), 'exec').andReturn({code:0});
+                cordova.compile('android');
+                expect(s.mostRecentCall.args[0].match(/\/cordova\/build/)).not.toBeNull();
+            });
+        });
+        describe('iOS', function() {
+            it('should shell out to build command on iOS', function() {
+                var s = spyOn(require('shelljs'), 'exec');
+                cordova.compile('ios');
+                expect(s).toHaveBeenCalled();
+                expect(s.mostRecentCall.args[0].match(/\/cordova\/build/)).not.toBeNull();
+            });
+        });
+        describe('BlackBerry', function() {
+            it('should shell out to ant command on blackberry', function() {
+                var s = spyOn(shell, 'exec');
+                cordova.compile('blackberry');
+                expect(s).toHaveBeenCalled();
+                expect(s.mostRecentCall.args[0]).toMatch(/ant -f .*build\.xml" qnx load-device/);
+            });
+        });
+        it('should not treat a .gitignore file as a platform', function() {
+            var gitignore = path.join(cordova_project, 'platforms', '.gitignore');
+            fs.writeFileSync(gitignore, 'somethinghere', 'utf-8');
+            this.after(function() {
+                shell.rm('-f', gitignore);
+            });
+            var s = spyOn(shell, 'exec');
+            cordova.compile();
+            expect(s.calls[0].args[0]).not.toMatch(/\.gitignore/);
+            expect(s.calls[1].args[0]).not.toMatch(/\.gitignore/);
+            expect(s.calls[1].args[0]).not.toMatch(/\.gitignore/);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/cordova-cli/config_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/config_parser.spec.js b/spec/cordova-cli/config_parser.spec.js
new file mode 100644
index 0000000..99bc717
--- /dev/null
+++ b/spec/cordova-cli/config_parser.spec.js
@@ -0,0 +1,167 @@
+
+/**
+    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'),
+    fs = require('fs'),
+    shell = require('shelljs'),
+    config_parser = require('../src/config_parser'),
+    tempDir = path.join(__dirname, '..', 'temp'),
+    et = require('elementtree'),
+    xml = path.join(tempDir, 'www', 'config.xml');
+
+
+describe('config.xml parser', function () {
+    beforeEach(function() {
+        shell.rm('-rf', tempDir);
+        cordova.create(tempDir);
+    });
+
+    it('should create an instance based on an xml file', function() {
+        var cfg;
+        expect(function () {
+            cfg = new config_parser(xml);
+        }).not.toThrow();
+        expect(cfg).toBeDefined();
+        expect(cfg.doc).toBeDefined();
+    });
+
+    describe('package name / id', function() {
+        var cfg;
+
+        beforeEach(function() {
+            cfg = new config_parser(xml);
+        });
+
+        it('should get the (default) packagename', function() {
+            expect(cfg.packageName()).toEqual('io.cordova.hellocordova');
+        });
+        it('should allow setting the packagename', function() {
+            cfg.packageName('this.is.bat.country');
+            expect(cfg.packageName()).toEqual('this.is.bat.country');
+        });
+        it('should write to disk after setting the packagename', function() {
+            cfg.packageName('this.is.bat.country');
+            expect(fs.readFileSync(xml, 'utf-8')).toMatch(/id="this\.is\.bat\.country"/);
+        });
+    });
+
+    describe('app name', function() {
+        var cfg;
+
+        beforeEach(function() {
+            cfg = new config_parser(xml);
+        });
+
+        it('should get the (default) app name', function() {
+            expect(cfg.name()).toEqual('HelloCordova');
+        });
+        it('should allow setting the app name', function() {
+            cfg.name('this.is.bat.country');
+            expect(cfg.name()).toEqual('this.is.bat.country');
+        });
+        it('should write to disk after setting the name', function() {
+            cfg.name('one toke over the line');
+            expect(fs.readFileSync(xml, 'utf-8')).toMatch(/<name>one toke over the line<\/name>/);
+        });
+    });
+
+    describe('access elements (whitelist)', function() {
+        var cfg;
+
+        beforeEach(function() {
+            cfg = new config_parser(xml);
+        });
+
+        describe('getter', function() {
+            it('should get the (default) access element', function() {
+                expect(cfg.access.get()[0]).toEqual('*');
+            });
+            it('should return an array of all access origin uris via access()', function() {
+                expect(cfg.access.get() instanceof Array).toBe(true);
+            });
+        });
+        describe('setters', function() {
+            it('should allow removing a uri from the access list', function() {
+                cfg.access.remove('*');
+                expect(cfg.access.get().length).toEqual(0);
+            });
+            it('should write to disk after removing a uri', function() {
+                cfg.access.remove('*');
+                expect(fs.readFileSync(xml, 'utf-8')).not.toMatch(/<access.*\/>/);
+            });
+            it('should allow adding a new uri to the access list', function() {
+                cfg.access.add('http://canucks.com');
+                expect(cfg.access.get().length).toEqual(2);
+                expect(cfg.access.get().indexOf('http://canucks.com') > -1).toBe(true);
+            });
+            it('should write to disk after adding a uri', function() {
+                cfg.access.add('http://cordova.io');
+                expect(fs.readFileSync(xml, 'utf-8')).toMatch(/<access origin="http:\/\/cordova\.io/);
+            });
+            it('should allow removing all access elements when no parameter is specified', function() {
+                cfg.access.add('http://cordova.io');
+                cfg.access.remove();
+
+                expect(fs.readFileSync(xml, 'utf-8')).not.toMatch(/<access.*\/>/);
+            });
+        });
+    });
+
+    describe('preference elements', function() {
+        var cfg;
+
+        beforeEach(function() {
+            cfg = new config_parser(xml);
+        });
+
+        describe('getter', function() {
+            it('should get all preference elements', function() {
+                expect(cfg.preference.get()[0].name).toEqual('phonegap-version');
+                expect(cfg.preference.get()[0].value).toEqual('1.9.0');
+            });
+            it('should return an array of all preference name/value pairs', function() {
+                expect(cfg.preference.get() instanceof Array).toBe(true);
+            });
+        });
+        describe('setters', function() {
+            it('should allow removing a preference by name', function() {
+                cfg.preference.remove('phonegap-version');
+                expect(cfg.preference.get().length).toEqual(3);
+            });
+            it('should write to disk after removing a preference', function() {
+                cfg.preference.remove('phonegap-version');
+                expect(fs.readFileSync(xml, 'utf-8')).not.toMatch(/<preference\sname="phonegap-version"/);
+            });
+            it('should allow adding a new preference', function() {
+                cfg.preference.add({name:'UIWebViewBounce',value:'false'});
+                expect(cfg.preference.get().length).toEqual(5);
+                expect(cfg.preference.get()[4].value).toEqual('false');
+            });
+            it('should write to disk after adding a preference', function() {
+                cfg.preference.add({name:'UIWebViewBounce',value:'false'});
+                expect(fs.readFileSync(xml, 'utf-8')).toMatch(/<preference name="UIWebViewBounce" value="false"/);
+            });
+            it('should allow removing all preference elements when no parameter is specified', function() {
+                cfg.preference.remove();
+                expect(fs.readFileSync(xml, 'utf-8')).not.toMatch(/<preference.*\/>/);
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/cordova-cli/create.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/create.spec.js b/spec/cordova-cli/create.spec.js
new file mode 100644
index 0000000..fcb3e76
--- /dev/null
+++ b/spec/cordova-cli/create.spec.js
@@ -0,0 +1,68 @@
+var cordova = require('../cordova'),
+    path    = require('path'),
+    shell   = require('shelljs'),
+    fs      = require('fs'),
+    tempDir = path.join(__dirname, '..', 'temp');
+
+describe('create command', function () {
+    beforeEach(function() {
+        shell.rm('-rf', tempDir);
+    });
+
+    it('should print out help txt if no directory is provided', function() {
+        expect(cordova.create()).toMatch(/synopsis/i);
+    });
+    it('should create a cordova project in the specified directory if parameter is provided', function() {
+        cordova.create(tempDir);
+        var dotc = path.join(tempDir, '.cordova', 'config.json');
+        expect(fs.lstatSync(dotc).isFile()).toBe(true);
+        expect(JSON.parse(fs.readFileSync(dotc, 'utf8')).name).toBe("HelloCordova");
+        var hooks = path.join(tempDir, '.cordova', 'hooks');
+        expect(fs.existsSync(hooks)).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'before_platform_add'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'before_prepare'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'before_compile'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'after_platform_add'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'before_platform_rm'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'after_platform_rm'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'before_platform_ls'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'after_platform_ls'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'before_plugin_add'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'after_plugin_add'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'before_plugin_rm'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'after_plugin_rm'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'before_plugin_ls'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'after_plugin_ls'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'after_prepare'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'after_compile'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'before_build'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'after_build'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'before_emulate'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'after_emulate'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'before_docs'))).toBe(true);
+        expect(fs.existsSync(path.join(hooks, 'after_docs'))).toBe(true);
+    });
+    it('should throw if the directory is already a cordova project', function() {
+        shell.mkdir('-p', path.join(tempDir, '.cordova'));
+        
+        expect(function() {
+            cordova.create(tempDir);
+        }).toThrow();
+    });
+    it('should create a cordova project in the specified dir with specified name if provided', function() {
+        cordova.create(tempDir, "balls");
+
+        expect(fs.lstatSync(path.join(tempDir, '.cordova', 'config.json')).isFile()).toBe(true);
+
+        expect(fs.readFileSync(path.join(tempDir, 'www', 'config.xml')).toString('utf8')).toMatch(/<name>balls<\/name>/);
+    });
+    it('should create a cordova project in the specified dir with specified name and id if provided', function() {
+        cordova.create(tempDir, "birdy.nam.nam", "numnum");
+
+        expect(fs.lstatSync(path.join(tempDir, '.cordova', 'config.json')).isFile()).toBe(true);
+
+        var config = fs.readFileSync(path.join(tempDir, 'www', 'config.xml')).toString('utf8');
+        expect(config).toMatch(/<name>numnum<\/name>/);
+        expect(config).toMatch(/id="birdy\.nam\.nam"/);
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/cordova-cli/emulate.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/emulate.spec.js b/spec/cordova-cli/emulate.spec.js
new file mode 100644
index 0000000..3a4d1a4
--- /dev/null
+++ b/spec/cordova-cli/emulate.spec.js
@@ -0,0 +1,191 @@
+/**
+    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'),
+    et = require('elementtree'),
+    shell = require('shelljs'),
+    path = require('path'),
+    fs = require('fs'),
+    config_parser = require('../src/config_parser'),
+    android_parser = require('../src/metadata/android_parser'),
+    ios_parser = require('../src/metadata/ios_parser'),
+    blackberry_parser = require('../src/metadata/blackberry_parser'),
+    hooker = require('../src/hooker'),
+    fixtures = path.join(__dirname, 'fixtures'),
+    hooks = path.join(fixtures, 'hooks'),
+    tempDir = path.join(__dirname, '..', 'temp'),
+    cordova_project = path.join(fixtures, 'projects', 'cordova');
+
+var cwd = process.cwd();
+
+describe('emulate command', function() {
+    beforeEach(function() {
+        shell.rm('-rf', tempDir);
+        shell.mkdir('-p', tempDir);
+    });
+
+    it('should not run inside a Cordova-based project with no added platforms', function() {
+        this.after(function() {
+            process.chdir(cwd);
+        });
+
+        cordova.create(tempDir);
+        process.chdir(tempDir);
+        expect(function() {
+            cordova.emulate();
+        }).toThrow();
+    });
+    
+    it('should run inside a Cordova-based project with at least one added platform', function() {
+        shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
+        shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
+        this.after(function() {
+            process.chdir(cwd);
+            shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
+            shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+        });
+
+        process.chdir(cordova_project);
+
+        var s = spyOn(shell, 'exec');
+        var a_spy = spyOn(android_parser.prototype, 'update_project');
+        expect(function() {
+            cordova.emulate();
+            a_spy.mostRecentCall.args[1](); // fake out android parser
+            expect(s).toHaveBeenCalled();
+        }).not.toThrow();
+    });
+    it('should not run outside of a Cordova-based project', function() {
+        this.after(function() {
+            process.chdir(cwd);
+        });
+
+        shell.mkdir('-p', tempDir);
+        process.chdir(tempDir);
+
+        expect(function() {
+            cordova.emulate();
+        }).toThrow();
+    });
+    describe('per platform', function() {
+        beforeEach(function() {
+            process.chdir(cordova_project);
+        });
+
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+       
+        describe('Android', function() {
+            var s;
+            beforeEach(function() {
+                s = spyOn(require('shelljs'), 'exec');
+            });
+            it('should shell out to run command on Android', function() {
+                cordova.emulate('android');
+                expect(s.mostRecentCall.args[0].match(/\/cordova\/run/)).not.toBeNull();
+            });
+            it('should call android_parser\'s update_project', function() {
+                var spy = spyOn(android_parser.prototype, 'update_project');
+                cordova.emulate('android');
+                expect(spy).toHaveBeenCalled();
+            });
+        });
+        describe('iOS', function() {
+            it('should shell out to emulate command on iOS', function() {
+                var s = spyOn(require('shelljs'), 'exec');
+                var proj_spy = spyOn(ios_parser.prototype, 'update_project');
+                cordova.emulate('ios');
+                proj_spy.mostRecentCall.args[1]();
+                expect(s).toHaveBeenCalled();
+                expect(s.mostRecentCall.args[0].match(/\/cordova\/emulate/)).not.toBeNull();
+            });
+            it('should call ios_parser\'s update_project', function() {
+                var s = spyOn(ios_parser.prototype, 'update_project');
+                cordova.emulate('ios');
+                expect(s).toHaveBeenCalled();
+            });
+        });
+        describe('BlackBerry', function() {
+            it('should shell out to ant command on blackberry', function() {
+                var proj_spy = spyOn(blackberry_parser.prototype, 'update_project');
+                var s = spyOn(require('shelljs'), 'exec');
+                cordova.emulate('blackberry');
+                proj_spy.mostRecentCall.args[1](); // update_project fake
+                expect(s).toHaveBeenCalled();
+                expect(s.mostRecentCall.args[0]).toMatch(/ant -f .*build\.xml" qnx load-simulator/);
+            });
+            it('should call blackberry_parser\'s update_project', function() {
+                var s = spyOn(blackberry_parser.prototype, 'update_project');
+                cordova.emulate('blackberry');
+                expect(s).toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('hooks', function() {
+        var s, sh, ap;
+        beforeEach(function() {
+            s = spyOn(hooker.prototype, 'fire').andReturn(true);
+        });
+
+        describe('when platforms are added', function() {
+            beforeEach(function() {
+                shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
+                shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
+                sh = spyOn(shell, 'exec');
+                ap = spyOn(android_parser.prototype, 'update_project');
+                process.chdir(cordova_project);
+            });
+            afterEach(function() {
+                shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
+                shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+                process.chdir(cwd);
+            });
+
+            it('should fire before hooks through the hooker module', function() {
+                cordova.emulate();
+                expect(s).toHaveBeenCalledWith('before_emulate');
+            });
+            it('should fire after hooks through the hooker module', function() {
+                cordova.emulate();
+                ap.mostRecentCall.args[1](); // fake parser call
+                sh.mostRecentCall.args[2](0); //fake shell call
+                expect(s).toHaveBeenCalledWith('after_emulate');
+            });
+        });
+
+        describe('with no platforms added', function() {
+            beforeEach(function() {
+                cordova.create(tempDir);
+                process.chdir(tempDir);
+            });
+            afterEach(function() {
+                process.chdir(cwd);
+            });
+            it('should not fire the hooker', function() {
+                spyOn(shell, 'exec');
+                expect(function() {
+                    cordova.emulate();
+                }).toThrow();
+                expect(s).not.toHaveBeenCalledWith('before_emulate');
+                expect(s).not.toHaveBeenCalledWith('after_emulate');
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/cordova-cli/helper.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/helper.js b/spec/cordova-cli/helper.js
new file mode 100644
index 0000000..2c4f331
--- /dev/null
+++ b/spec/cordova-cli/helper.js
@@ -0,0 +1,20 @@
+
+/**
+    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.
+*/
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/cordova-cli/hooker.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/hooker.spec.js b/spec/cordova-cli/hooker.spec.js
new file mode 100644
index 0000000..4a0ca7d
--- /dev/null
+++ b/spec/cordova-cli/hooker.spec.js
@@ -0,0 +1,137 @@
+/**
+    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 hooker = require('../src/hooker'),
+    shell  = require('shelljs'),
+    path   = require('path'),
+    fs     = require('fs'),
+    tempDir= path.join(__dirname, '..', 'temp'),
+    hooks  = path.join(__dirname, 'fixtures', 'hooks'),
+    cordova= require('../cordova');
+
+var cwd = process.cwd();
+
+describe('hooker', function() {
+    it('should throw if provided directory is not a cordova project', function() {
+        shell.rm('-rf', tempDir);
+        shell.mkdir('-p', tempDir); 
+        this.after(function() {
+            shell.rm('-rf', tempDir);
+        });
+
+        expect(function() {
+            var h = new hooker(tempDir);
+        }).toThrow();
+    });
+    it('should not throw if provided directory is a cordova project', function() {
+        cordova.create(tempDir);
+        this.after(function() {
+            shell.rm('-rf', tempDir);
+        });
+
+        expect(function() {
+            var h = new hooker(tempDir);
+        }).not.toThrow();
+    });
+
+    describe('fire method', function() {
+        var h;
+
+        beforeEach(function() {
+            cordova.create(tempDir);
+            h = new hooker(tempDir);
+        });
+        afterEach(function() {
+            shell.rm('-rf', tempDir);
+        });
+
+        describe('failure', function() {
+            it('should not throw if the hook is unrecognized', function() {
+                expect(function() {
+                    h.fire('CLEAN YOUR SHORTS GODDAMNIT LIKE A BIG BOY!');
+                }).not.toThrow();
+            });
+            it('should throw if any script exits with non-zero code', function() {
+                var script = path.join(tempDir, '.cordova', 'hooks', 'before_build', 'fail.sh');
+                shell.cp(path.join(hooks, 'fail', 'fail.sh'), script);
+                fs.chmodSync(script, '754');
+                expect(function() {
+                    h.fire('before_build');
+                }).toThrow();
+            });
+        });
+
+        describe('success', function() {
+            it('should execute all scripts in order and return true', function() {
+                var hook = path.join(tempDir, '.cordova', 'hooks', 'before_build');
+                shell.cp(path.join(hooks, 'test', '*'), path.join(hook, '.'));
+                fs.readdirSync(hook).forEach(function(script) {
+                    fs.chmodSync(path.join(hook, script), '754');
+                });
+                var returnValue;
+                var s = spyOn(shell, 'exec').andReturn({code:0});
+                expect(function() {
+                    returnValue = h.fire('before_build');
+                }).not.toThrow();
+                expect(returnValue).toBe(true);
+                expect(s.calls[0].args[0]).toMatch(/0.sh/);
+                expect(s.calls[1].args[0]).toMatch(/1.sh/);
+            });
+            it('should pass the project root folder as parameter into the project-level hooks', function() {
+                var hook = path.join(tempDir, '.cordova', 'hooks', 'before_build');
+                shell.cp(path.join(hooks, 'test', '0.sh'), path.join(hook, '.'));
+                fs.readdirSync(hook).forEach(function(script) {
+                    fs.chmodSync(path.join(hook, script), '754');
+                });
+                var returnValue;
+                var s = spyOn(shell, 'exec').andReturn({code:0});
+                expect(function() {
+                    returnValue = h.fire('before_build');
+                }).not.toThrow();
+                expect(returnValue).toBe(true);
+                var paramRegex = new RegExp('0.sh "'+tempDir+'"$');
+                expect(s.calls[0].args[0]).toMatch(paramRegex);
+            });
+            describe('module-level hooks', function() {
+                var handler = jasmine.createSpy();
+                var test_event = 'before_build';
+                afterEach(function() {
+                    cordova.off(test_event, handler);
+                    handler.reset();
+                });
+
+                it('should fire handlers using cordova.on', function() {
+                    cordova.on(test_event, handler);
+                    h.fire(test_event);
+                    expect(handler).toHaveBeenCalled();
+                });
+                it('should pass the project root folder as parameter into the module-level handlers', function() {
+                    cordova.on(test_event, handler);
+                    h.fire('before_build');
+                    expect(handler).toHaveBeenCalledWith(tempDir);
+                });
+                it('should be able to stop listening to events using cordova.off', function() {
+                    cordova.on(test_event, handler);
+                    cordova.off(test_event, handler);
+                    h.fire('before_build');
+                    expect(handler).not.toHaveBeenCalled();
+                });
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/cordova-cli/platform.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/platform.spec.js b/spec/cordova-cli/platform.spec.js
new file mode 100644
index 0000000..7138c7e
--- /dev/null
+++ b/spec/cordova-cli/platform.spec.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 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'),
+    ios_parser = require('../src/metadata/ios_parser'),
+    cordova_project = path.join(__dirname, 'fixtures', 'projects', 'cordova'),
+    blackberry_parser = require('../src/metadata/blackberry_parser');
+
+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() {
+            expect(cordova.platform('list').length).toEqual(3);
+        });
+    });
+
+    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(path.join(cordova_project, 'www', 'config.xml'), 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(path.join(cordova_project, 'www', 'config.xml'), 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 irc = spyOn(ios_parser, 'check_requirements');
+            var sh = spyOn(shell, 'exec');
+            cordova.platform('add', ['android', 'ios']);
+            arc.mostRecentCall.args[0](false);
+            irc.mostRecentCall.args[0](false);
+            expect(sh.argsForCall[0][0]).toMatch(/android\/bin\/create/);
+            expect(sh.argsForCall[1][0]).toMatch(/ios\/bin\/create/);
+        });
+    });
+
+    describe('`remove`',function() { 
+        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(2);
+        });
+        it('should be able to remove multiple platforms', function() {
+            cordova.platform('remove', ['android','ios']);
+            expect(cordova.platform('ls').length).toEqual(1);
+        });
+    });
+
+    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/693521da/spec/cordova-cli/plugin.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/plugin.spec.js b/spec/cordova-cli/plugin.spec.js
new file mode 100644
index 0000000..3d370e7
--- /dev/null
+++ b/spec/cordova-cli/plugin.spec.js
@@ -0,0 +1,164 @@
+/**
+    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.');
+                });
+            });
+            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');
+            });
+        });
+    });
+});
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/cordova-cli/plugin_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/plugin_parser.spec.js b/spec/cordova-cli/plugin_parser.spec.js
new file mode 100644
index 0000000..4391003
--- /dev/null
+++ b/spec/cordova-cli/plugin_parser.spec.js
@@ -0,0 +1,42 @@
+
+/**
+    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'),
+    fs = require('fs'),
+    plugin_parser = require('../src/plugin_parser'),
+    et = require('elementtree'),
+    xml = path.join(__dirname, '..', 'fixtures', 'plugins', 'test', 'plugin.xml');
+
+describe('plugin.xml parser', function () {
+    it('should read a proper plugin.xml file', function() {
+        var cfg;
+        expect(function () {
+            cfg = new plugin_parser(xml);
+        }).not.toThrow();
+        expect(cfg).toBeDefined();
+        expect(cfg.doc).toBeDefined();
+    });
+    it('should be able to figure out which platforms the plugin supports', function() {
+        var cfg = new plugin_parser(xml);
+        expect(cfg.platforms.length).toBe(1);
+        expect(cfg.platforms.indexOf('ios') > -1).toBe(true);
+    });
+});
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/cordova-cli/prepare.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/prepare.spec.js b/spec/cordova-cli/prepare.spec.js
new file mode 100644
index 0000000..f232b3e
--- /dev/null
+++ b/spec/cordova-cli/prepare.spec.js
@@ -0,0 +1,133 @@
+/**
+    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'),
+    et = require('elementtree'),
+    shell = require('shelljs'),
+    path = require('path'),
+    fs = require('fs'),
+    config_parser = require('../src/config_parser'),
+    android_parser = require('../src/metadata/android_parser'),
+    ios_parser = require('../src/metadata/ios_parser'),
+    blackberry_parser = require('../src/metadata/blackberry_parser'),
+    hooker = require('../src/hooker'),
+    fixtures = path.join(__dirname, 'fixtures'),
+    hooks = path.join(fixtures, 'hooks'),
+    tempDir = path.join(__dirname, '..', 'temp'),
+    cordova_project = path.join(fixtures, 'projects', 'cordova');
+
+var cwd = process.cwd();
+
+describe('prepare command', function() {
+    beforeEach(function() {
+        shell.rm('-rf', tempDir);
+        shell.mkdir('-p', tempDir);
+    });
+
+    it('should not run inside a Cordova-based project with no added platforms', function() {
+        this.after(function() {
+            process.chdir(cwd);
+        });
+
+        cordova.create(tempDir);
+        process.chdir(tempDir);
+        expect(function() {
+            cordova.prepare();
+        }).toThrow();
+    });
+    
+    it('should run inside a Cordova-based project with at least one added platform', function() {
+        // move platform project fixtures over to fake cordova into thinking platforms were added
+        // TODO: possibly add this to helper?
+        shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
+        this.after(function() {
+            process.chdir(cwd);
+            shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
+        });
+
+        process.chdir(cordova_project);
+
+        var a_parser_spy = spyOn(android_parser.prototype, 'update_project');
+        var i_parser_spy = spyOn(ios_parser.prototype, 'update_project');
+        expect(function() {
+            cordova.prepare();
+            expect(a_parser_spy).toHaveBeenCalled();
+            expect(i_parser_spy).toHaveBeenCalled();
+        }).not.toThrow();
+    });
+    it('should not run outside of a Cordova-based project', function() {
+        this.after(function() {
+            process.chdir(cwd);
+        });
+
+        shell.mkdir('-p', tempDir);
+        process.chdir(tempDir);
+
+        expect(function() {
+            cordova.prepare();
+        }).toThrow();
+    });
+
+    describe('hooks', function() {
+        var s;
+        beforeEach(function() {
+            s = spyOn(hooker.prototype, 'fire').andReturn(true);
+        });
+
+        describe('when platforms are added', function() {
+            beforeEach(function() {
+                shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
+                shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
+                process.chdir(cordova_project);
+            });
+            afterEach(function() {
+                shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
+                shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+                process.chdir(cwd);
+            });
+
+            it('should fire before hooks through the hooker module', function() {
+                cordova.prepare();
+                expect(s).toHaveBeenCalledWith('before_prepare');
+            });
+            it('should fire after hooks through the hooker module', function() {
+                var parser_spy = spyOn(android_parser.prototype, 'update_project');
+                cordova.prepare();
+                parser_spy.mostRecentCall.args[1](); // parser cb
+                expect(s).toHaveBeenCalledWith('after_prepare');
+            });
+        });
+
+        describe('with no platforms added', function() {
+            beforeEach(function() {
+                cordova.create(tempDir);
+                process.chdir(tempDir);
+            });
+            afterEach(function() {
+                process.chdir(cwd);
+            });
+            it('should not fire the hooker', function() {
+                expect(function() {
+                    cordova.prepare();
+                }).toThrow();
+                expect(s).not.toHaveBeenCalledWith('before_prepare');
+                expect(s).not.toHaveBeenCalledWith('after_prepare');
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/cordova-cli/serve.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/serve.spec.js b/spec/cordova-cli/serve.spec.js
new file mode 100644
index 0000000..8d00cab
--- /dev/null
+++ b/spec/cordova-cli/serve.spec.js
@@ -0,0 +1,132 @@
+
+/**
+    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');
+
+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);
+            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));
+    });
+});
+


[48/50] Fix bootstrap, app/ dir is fully reverted now.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/app/www/spec/lib/jasmine-1.2.0/jasmine.js
----------------------------------------------------------------------
diff --git a/templates/app/www/spec/lib/jasmine-1.2.0/jasmine.js b/templates/app/www/spec/lib/jasmine-1.2.0/jasmine.js
deleted file mode 100644
index 03bf89a..0000000
--- a/templates/app/www/spec/lib/jasmine-1.2.0/jasmine.js
+++ /dev/null
@@ -1,2529 +0,0 @@
-var isCommonJS = typeof window == "undefined";
-
-/**
- * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
- *
- * @namespace
- */
-var jasmine = {};
-if (isCommonJS) exports.jasmine = jasmine;
-/**
- * @private
- */
-jasmine.unimplementedMethod_ = function() {
-  throw new Error("unimplemented method");
-};
-
-/**
- * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
- * a plain old variable and may be redefined by somebody else.
- *
- * @private
- */
-jasmine.undefined = jasmine.___undefined___;
-
-/**
- * Show diagnostic messages in the console if set to true
- *
- */
-jasmine.VERBOSE = false;
-
-/**
- * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
- *
- */
-jasmine.DEFAULT_UPDATE_INTERVAL = 250;
-
-/**
- * Default timeout interval in milliseconds for waitsFor() blocks.
- */
-jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
-
-jasmine.getGlobal = function() {
-  function getGlobal() {
-    return this;
-  }
-
-  return getGlobal();
-};
-
-/**
- * Allows for bound functions to be compared.  Internal use only.
- *
- * @ignore
- * @private
- * @param base {Object} bound 'this' for the function
- * @param name {Function} function to find
- */
-jasmine.bindOriginal_ = function(base, name) {
-  var original = base[name];
-  if (original.apply) {
-    return function() {
-      return original.apply(base, arguments);
-    };
-  } else {
-    // IE support
-    return jasmine.getGlobal()[name];
-  }
-};
-
-jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
-jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
-jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
-jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
-
-jasmine.MessageResult = function(values) {
-  this.type = 'log';
-  this.values = values;
-  this.trace = new Error(); // todo: test better
-};
-
-jasmine.MessageResult.prototype.toString = function() {
-  var text = "";
-  for (var i = 0; i < this.values.length; i++) {
-    if (i > 0) text += " ";
-    if (jasmine.isString_(this.values[i])) {
-      text += this.values[i];
-    } else {
-      text += jasmine.pp(this.values[i]);
-    }
-  }
-  return text;
-};
-
-jasmine.ExpectationResult = function(params) {
-  this.type = 'expect';
-  this.matcherName = params.matcherName;
-  this.passed_ = params.passed;
-  this.expected = params.expected;
-  this.actual = params.actual;
-  this.message = this.passed_ ? 'Passed.' : params.message;
-
-  var trace = (params.trace || new Error(this.message));
-  this.trace = this.passed_ ? '' : trace;
-};
-
-jasmine.ExpectationResult.prototype.toString = function () {
-  return this.message;
-};
-
-jasmine.ExpectationResult.prototype.passed = function () {
-  return this.passed_;
-};
-
-/**
- * Getter for the Jasmine environment. Ensures one gets created
- */
-jasmine.getEnv = function() {
-  var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
-  return env;
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isArray_ = function(value) {
-  return jasmine.isA_("Array", value);
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isString_ = function(value) {
-  return jasmine.isA_("String", value);
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isNumber_ = function(value) {
-  return jasmine.isA_("Number", value);
-};
-
-/**
- * @ignore
- * @private
- * @param {String} typeName
- * @param value
- * @returns {Boolean}
- */
-jasmine.isA_ = function(typeName, value) {
-  return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
-};
-
-/**
- * Pretty printer for expecations.  Takes any object and turns it into a human-readable string.
- *
- * @param value {Object} an object to be outputted
- * @returns {String}
- */
-jasmine.pp = function(value) {
-  var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
-  stringPrettyPrinter.format(value);
-  return stringPrettyPrinter.string;
-};
-
-/**
- * Returns true if the object is a DOM Node.
- *
- * @param {Object} obj object to check
- * @returns {Boolean}
- */
-jasmine.isDomNode = function(obj) {
-  return obj.nodeType > 0;
-};
-
-/**
- * Returns a matchable 'generic' object of the class type.  For use in expecations of type when values don't matter.
- *
- * @example
- * // don't care about which function is passed in, as long as it's a function
- * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
- *
- * @param {Class} clazz
- * @returns matchable object of the type clazz
- */
-jasmine.any = function(clazz) {
-  return new jasmine.Matchers.Any(clazz);
-};
-
-/**
- * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
- * attributes on the object.
- *
- * @example
- * // don't care about any other attributes than foo.
- * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
- *
- * @param sample {Object} sample
- * @returns matchable object for the sample
- */
-jasmine.objectContaining = function (sample) {
-    return new jasmine.Matchers.ObjectContaining(sample);
-};
-
-/**
- * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
- *
- * Spies should be created in test setup, before expectations.  They can then be checked, using the standard Jasmine
- * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
- *
- * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
- *
- * Spies are torn down at the end of every spec.
- *
- * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
- *
- * @example
- * // a stub
- * var myStub = jasmine.createSpy('myStub');  // can be used anywhere
- *
- * // spy example
- * var foo = {
- *   not: function(bool) { return !bool; }
- * }
- *
- * // actual foo.not will not be called, execution stops
- * spyOn(foo, 'not');
-
- // foo.not spied upon, execution will continue to implementation
- * spyOn(foo, 'not').andCallThrough();
- *
- * // fake example
- * var foo = {
- *   not: function(bool) { return !bool; }
- * }
- *
- * // foo.not(val) will return val
- * spyOn(foo, 'not').andCallFake(function(value) {return value;});
- *
- * // mock example
- * foo.not(7 == 7);
- * expect(foo.not).toHaveBeenCalled();
- * expect(foo.not).toHaveBeenCalledWith(true);
- *
- * @constructor
- * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
- * @param {String} name
- */
-jasmine.Spy = function(name) {
-  /**
-   * The name of the spy, if provided.
-   */
-  this.identity = name || 'unknown';
-  /**
-   *  Is this Object a spy?
-   */
-  this.isSpy = true;
-  /**
-   * The actual function this spy stubs.
-   */
-  this.plan = function() {
-  };
-  /**
-   * Tracking of the most recent call to the spy.
-   * @example
-   * var mySpy = jasmine.createSpy('foo');
-   * mySpy(1, 2);
-   * mySpy.mostRecentCall.args = [1, 2];
-   */
-  this.mostRecentCall = {};
-
-  /**
-   * Holds arguments for each call to the spy, indexed by call count
-   * @example
-   * var mySpy = jasmine.createSpy('foo');
-   * mySpy(1, 2);
-   * mySpy(7, 8);
-   * mySpy.mostRecentCall.args = [7, 8];
-   * mySpy.argsForCall[0] = [1, 2];
-   * mySpy.argsForCall[1] = [7, 8];
-   */
-  this.argsForCall = [];
-  this.calls = [];
-};
-
-/**
- * Tells a spy to call through to the actual implemenatation.
- *
- * @example
- * var foo = {
- *   bar: function() { // do some stuff }
- * }
- *
- * // defining a spy on an existing property: foo.bar
- * spyOn(foo, 'bar').andCallThrough();
- */
-jasmine.Spy.prototype.andCallThrough = function() {
-  this.plan = this.originalValue;
-  return this;
-};
-
-/**
- * For setting the return value of a spy.
- *
- * @example
- * // defining a spy from scratch: foo() returns 'baz'
- * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
- *
- * // defining a spy on an existing property: foo.bar() returns 'baz'
- * spyOn(foo, 'bar').andReturn('baz');
- *
- * @param {Object} value
- */
-jasmine.Spy.prototype.andReturn = function(value) {
-  this.plan = function() {
-    return value;
-  };
-  return this;
-};
-
-/**
- * For throwing an exception when a spy is called.
- *
- * @example
- * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
- * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
- *
- * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
- * spyOn(foo, 'bar').andThrow('baz');
- *
- * @param {String} exceptionMsg
- */
-jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
-  this.plan = function() {
-    throw exceptionMsg;
-  };
-  return this;
-};
-
-/**
- * Calls an alternate implementation when a spy is called.
- *
- * @example
- * var baz = function() {
- *   // do some stuff, return something
- * }
- * // defining a spy from scratch: foo() calls the function baz
- * var foo = jasmine.createSpy('spy on foo').andCall(baz);
- *
- * // defining a spy on an existing property: foo.bar() calls an anonymnous function
- * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
- *
- * @param {Function} fakeFunc
- */
-jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
-  this.plan = fakeFunc;
-  return this;
-};
-
-/**
- * Resets all of a spy's the tracking variables so that it can be used again.
- *
- * @example
- * spyOn(foo, 'bar');
- *
- * foo.bar();
- *
- * expect(foo.bar.callCount).toEqual(1);
- *
- * foo.bar.reset();
- *
- * expect(foo.bar.callCount).toEqual(0);
- */
-jasmine.Spy.prototype.reset = function() {
-  this.wasCalled = false;
-  this.callCount = 0;
-  this.argsForCall = [];
-  this.calls = [];
-  this.mostRecentCall = {};
-};
-
-jasmine.createSpy = function(name) {
-
-  var spyObj = function() {
-    spyObj.wasCalled = true;
-    spyObj.callCount++;
-    var args = jasmine.util.argsToArray(arguments);
-    spyObj.mostRecentCall.object = this;
-    spyObj.mostRecentCall.args = args;
-    spyObj.argsForCall.push(args);
-    spyObj.calls.push({object: this, args: args});
-    return spyObj.plan.apply(this, arguments);
-  };
-
-  var spy = new jasmine.Spy(name);
-
-  for (var prop in spy) {
-    spyObj[prop] = spy[prop];
-  }
-
-  spyObj.reset();
-
-  return spyObj;
-};
-
-/**
- * Determines whether an object is a spy.
- *
- * @param {jasmine.Spy|Object} putativeSpy
- * @returns {Boolean}
- */
-jasmine.isSpy = function(putativeSpy) {
-  return putativeSpy && putativeSpy.isSpy;
-};
-
-/**
- * Creates a more complicated spy: an Object that has every property a function that is a spy.  Used for stubbing something
- * large in one call.
- *
- * @param {String} baseName name of spy class
- * @param {Array} methodNames array of names of methods to make spies
- */
-jasmine.createSpyObj = function(baseName, methodNames) {
-  if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
-    throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
-  }
-  var obj = {};
-  for (var i = 0; i < methodNames.length; i++) {
-    obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
-  }
-  return obj;
-};
-
-/**
- * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
- *
- * Be careful not to leave calls to <code>jasmine.log</code> in production code.
- */
-jasmine.log = function() {
-  var spec = jasmine.getEnv().currentSpec;
-  spec.log.apply(spec, arguments);
-};
-
-/**
- * Function that installs a spy on an existing object's method name.  Used within a Spec to create a spy.
- *
- * @example
- * // spy example
- * var foo = {
- *   not: function(bool) { return !bool; }
- * }
- * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
- *
- * @see jasmine.createSpy
- * @param obj
- * @param methodName
- * @returns a Jasmine spy that can be chained with all spy methods
- */
-var spyOn = function(obj, methodName) {
-  return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
-};
-if (isCommonJS) exports.spyOn = spyOn;
-
-/**
- * Creates a Jasmine spec that will be added to the current suite.
- *
- * // TODO: pending tests
- *
- * @example
- * it('should be true', function() {
- *   expect(true).toEqual(true);
- * });
- *
- * @param {String} desc description of this specification
- * @param {Function} func defines the preconditions and expectations of the spec
- */
-var it = function(desc, func) {
-  return jasmine.getEnv().it(desc, func);
-};
-if (isCommonJS) exports.it = it;
-
-/**
- * Creates a <em>disabled</em> Jasmine spec.
- *
- * A convenience method that allows existing specs to be disabled temporarily during development.
- *
- * @param {String} desc description of this specification
- * @param {Function} func defines the preconditions and expectations of the spec
- */
-var xit = function(desc, func) {
-  return jasmine.getEnv().xit(desc, func);
-};
-if (isCommonJS) exports.xit = xit;
-
-/**
- * Starts a chain for a Jasmine expectation.
- *
- * It is passed an Object that is the actual value and should chain to one of the many
- * jasmine.Matchers functions.
- *
- * @param {Object} actual Actual value to test against and expected value
- */
-var expect = function(actual) {
-  return jasmine.getEnv().currentSpec.expect(actual);
-};
-if (isCommonJS) exports.expect = expect;
-
-/**
- * Defines part of a jasmine spec.  Used in cominbination with waits or waitsFor in asynchrnous specs.
- *
- * @param {Function} func Function that defines part of a jasmine spec.
- */
-var runs = function(func) {
-  jasmine.getEnv().currentSpec.runs(func);
-};
-if (isCommonJS) exports.runs = runs;
-
-/**
- * Waits a fixed time period before moving to the next block.
- *
- * @deprecated Use waitsFor() instead
- * @param {Number} timeout milliseconds to wait
- */
-var waits = function(timeout) {
-  jasmine.getEnv().currentSpec.waits(timeout);
-};
-if (isCommonJS) exports.waits = waits;
-
-/**
- * Waits for the latchFunction to return true before proceeding to the next block.
- *
- * @param {Function} latchFunction
- * @param {String} optional_timeoutMessage
- * @param {Number} optional_timeout
- */
-var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
-  jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
-};
-if (isCommonJS) exports.waitsFor = waitsFor;
-
-/**
- * A function that is called before each spec in a suite.
- *
- * Used for spec setup, including validating assumptions.
- *
- * @param {Function} beforeEachFunction
- */
-var beforeEach = function(beforeEachFunction) {
-  jasmine.getEnv().beforeEach(beforeEachFunction);
-};
-if (isCommonJS) exports.beforeEach = beforeEach;
-
-/**
- * A function that is called after each spec in a suite.
- *
- * Used for restoring any state that is hijacked during spec execution.
- *
- * @param {Function} afterEachFunction
- */
-var afterEach = function(afterEachFunction) {
-  jasmine.getEnv().afterEach(afterEachFunction);
-};
-if (isCommonJS) exports.afterEach = afterEach;
-
-/**
- * Defines a suite of specifications.
- *
- * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
- * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
- * of setup in some tests.
- *
- * @example
- * // TODO: a simple suite
- *
- * // TODO: a simple suite with a nested describe block
- *
- * @param {String} description A string, usually the class under test.
- * @param {Function} specDefinitions function that defines several specs.
- */
-var describe = function(description, specDefinitions) {
-  return jasmine.getEnv().describe(description, specDefinitions);
-};
-if (isCommonJS) exports.describe = describe;
-
-/**
- * Disables a suite of specifications.  Used to disable some suites in a file, or files, temporarily during development.
- *
- * @param {String} description A string, usually the class under test.
- * @param {Function} specDefinitions function that defines several specs.
- */
-var xdescribe = function(description, specDefinitions) {
-  return jasmine.getEnv().xdescribe(description, specDefinitions);
-};
-if (isCommonJS) exports.xdescribe = xdescribe;
-
-
-// Provide the XMLHttpRequest class for IE 5.x-6.x:
-jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
-  function tryIt(f) {
-    try {
-      return f();
-    } catch(e) {
-    }
-    return null;
-  }
-
-  var xhr = tryIt(function() {
-    return new ActiveXObject("Msxml2.XMLHTTP.6.0");
-  }) ||
-    tryIt(function() {
-      return new ActiveXObject("Msxml2.XMLHTTP.3.0");
-    }) ||
-    tryIt(function() {
-      return new ActiveXObject("Msxml2.XMLHTTP");
-    }) ||
-    tryIt(function() {
-      return new ActiveXObject("Microsoft.XMLHTTP");
-    });
-
-  if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
-
-  return xhr;
-} : XMLHttpRequest;
-/**
- * @namespace
- */
-jasmine.util = {};
-
-/**
- * Declare that a child class inherit it's prototype from the parent class.
- *
- * @private
- * @param {Function} childClass
- * @param {Function} parentClass
- */
-jasmine.util.inherit = function(childClass, parentClass) {
-  /**
-   * @private
-   */
-  var subclass = function() {
-  };
-  subclass.prototype = parentClass.prototype;
-  childClass.prototype = new subclass();
-};
-
-jasmine.util.formatException = function(e) {
-  var lineNumber;
-  if (e.line) {
-    lineNumber = e.line;
-  }
-  else if (e.lineNumber) {
-    lineNumber = e.lineNumber;
-  }
-
-  var file;
-
-  if (e.sourceURL) {
-    file = e.sourceURL;
-  }
-  else if (e.fileName) {
-    file = e.fileName;
-  }
-
-  var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
-
-  if (file && lineNumber) {
-    message += ' in ' + file + ' (line ' + lineNumber + ')';
-  }
-
-  return message;
-};
-
-jasmine.util.htmlEscape = function(str) {
-  if (!str) return str;
-  return str.replace(/&/g, '&amp;')
-    .replace(/</g, '&lt;')
-    .replace(/>/g, '&gt;');
-};
-
-jasmine.util.argsToArray = function(args) {
-  var arrayOfArgs = [];
-  for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
-  return arrayOfArgs;
-};
-
-jasmine.util.extend = function(destination, source) {
-  for (var property in source) destination[property] = source[property];
-  return destination;
-};
-
-/**
- * Environment for Jasmine
- *
- * @constructor
- */
-jasmine.Env = function() {
-  this.currentSpec = null;
-  this.currentSuite = null;
-  this.currentRunner_ = new jasmine.Runner(this);
-
-  this.reporter = new jasmine.MultiReporter();
-
-  this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
-  this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
-  this.lastUpdate = 0;
-  this.specFilter = function() {
-    return true;
-  };
-
-  this.nextSpecId_ = 0;
-  this.nextSuiteId_ = 0;
-  this.equalityTesters_ = [];
-
-  // wrap matchers
-  this.matchersClass = function() {
-    jasmine.Matchers.apply(this, arguments);
-  };
-  jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
-
-  jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
-};
-
-
-jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
-jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
-jasmine.Env.prototype.setInterval = jasmine.setInterval;
-jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
-
-/**
- * @returns an object containing jasmine version build info, if set.
- */
-jasmine.Env.prototype.version = function () {
-  if (jasmine.version_) {
-    return jasmine.version_;
-  } else {
-    throw new Error('Version not set');
-  }
-};
-
-/**
- * @returns string containing jasmine version build info, if set.
- */
-jasmine.Env.prototype.versionString = function() {
-  if (!jasmine.version_) {
-    return "version unknown";
-  }
-
-  var version = this.version();
-  var versionString = version.major + "." + version.minor + "." + version.build;
-  if (version.release_candidate) {
-    versionString += ".rc" + version.release_candidate;
-  }
-  versionString += " revision " + version.revision;
-  return versionString;
-};
-
-/**
- * @returns a sequential integer starting at 0
- */
-jasmine.Env.prototype.nextSpecId = function () {
-  return this.nextSpecId_++;
-};
-
-/**
- * @returns a sequential integer starting at 0
- */
-jasmine.Env.prototype.nextSuiteId = function () {
-  return this.nextSuiteId_++;
-};
-
-/**
- * Register a reporter to receive status updates from Jasmine.
- * @param {jasmine.Reporter} reporter An object which will receive status updates.
- */
-jasmine.Env.prototype.addReporter = function(reporter) {
-  this.reporter.addReporter(reporter);
-};
-
-jasmine.Env.prototype.execute = function() {
-  this.currentRunner_.execute();
-};
-
-jasmine.Env.prototype.describe = function(description, specDefinitions) {
-  var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
-
-  var parentSuite = this.currentSuite;
-  if (parentSuite) {
-    parentSuite.add(suite);
-  } else {
-    this.currentRunner_.add(suite);
-  }
-
-  this.currentSuite = suite;
-
-  var declarationError = null;
-  try {
-    specDefinitions.call(suite);
-  } catch(e) {
-    declarationError = e;
-  }
-
-  if (declarationError) {
-    this.it("encountered a declaration exception", function() {
-      throw declarationError;
-    });
-  }
-
-  this.currentSuite = parentSuite;
-
-  return suite;
-};
-
-jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
-  if (this.currentSuite) {
-    this.currentSuite.beforeEach(beforeEachFunction);
-  } else {
-    this.currentRunner_.beforeEach(beforeEachFunction);
-  }
-};
-
-jasmine.Env.prototype.currentRunner = function () {
-  return this.currentRunner_;
-};
-
-jasmine.Env.prototype.afterEach = function(afterEachFunction) {
-  if (this.currentSuite) {
-    this.currentSuite.afterEach(afterEachFunction);
-  } else {
-    this.currentRunner_.afterEach(afterEachFunction);
-  }
-
-};
-
-jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
-  return {
-    execute: function() {
-    }
-  };
-};
-
-jasmine.Env.prototype.it = function(description, func) {
-  var spec = new jasmine.Spec(this, this.currentSuite, description);
-  this.currentSuite.add(spec);
-  this.currentSpec = spec;
-
-  if (func) {
-    spec.runs(func);
-  }
-
-  return spec;
-};
-
-jasmine.Env.prototype.xit = function(desc, func) {
-  return {
-    id: this.nextSpecId(),
-    runs: function() {
-    }
-  };
-};
-
-jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
-  if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
-    return true;
-  }
-
-  a.__Jasmine_been_here_before__ = b;
-  b.__Jasmine_been_here_before__ = a;
-
-  var hasKey = function(obj, keyName) {
-    return obj !== null && obj[keyName] !== jasmine.undefined;
-  };
-
-  for (var property in b) {
-    if (!hasKey(a, property) && hasKey(b, property)) {
-      mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
-    }
-  }
-  for (property in a) {
-    if (!hasKey(b, property) && hasKey(a, property)) {
-      mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
-    }
-  }
-  for (property in b) {
-    if (property == '__Jasmine_been_here_before__') continue;
-    if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
-      mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
-    }
-  }
-
-  if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
-    mismatchValues.push("arrays were not the same length");
-  }
-
-  delete a.__Jasmine_been_here_before__;
-  delete b.__Jasmine_been_here_before__;
-  return (mismatchKeys.length === 0 && mismatchValues.length === 0);
-};
-
-jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
-  mismatchKeys = mismatchKeys || [];
-  mismatchValues = mismatchValues || [];
-
-  for (var i = 0; i < this.equalityTesters_.length; i++) {
-    var equalityTester = this.equalityTesters_[i];
-    var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
-    if (result !== jasmine.undefined) return result;
-  }
-
-  if (a === b) return true;
-
-  if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
-    return (a == jasmine.undefined && b == jasmine.undefined);
-  }
-
-  if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
-    return a === b;
-  }
-
-  if (a instanceof Date && b instanceof Date) {
-    return a.getTime() == b.getTime();
-  }
-
-  if (a.jasmineMatches) {
-    return a.jasmineMatches(b);
-  }
-
-  if (b.jasmineMatches) {
-    return b.jasmineMatches(a);
-  }
-
-  if (a instanceof jasmine.Matchers.ObjectContaining) {
-    return a.matches(b);
-  }
-
-  if (b instanceof jasmine.Matchers.ObjectContaining) {
-    return b.matches(a);
-  }
-
-  if (jasmine.isString_(a) && jasmine.isString_(b)) {
-    return (a == b);
-  }
-
-  if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
-    return (a == b);
-  }
-
-  if (typeof a === "object" && typeof b === "object") {
-    return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
-  }
-
-  //Straight check
-  return (a === b);
-};
-
-jasmine.Env.prototype.contains_ = function(haystack, needle) {
-  if (jasmine.isArray_(haystack)) {
-    for (var i = 0; i < haystack.length; i++) {
-      if (this.equals_(haystack[i], needle)) return true;
-    }
-    return false;
-  }
-  return haystack.indexOf(needle) >= 0;
-};
-
-jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
-  this.equalityTesters_.push(equalityTester);
-};
-/** No-op base class for Jasmine reporters.
- *
- * @constructor
- */
-jasmine.Reporter = function() {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSpecResults = function(spec) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.log = function(str) {
-};
-
-/**
- * Blocks are functions with executable code that make up a spec.
- *
- * @constructor
- * @param {jasmine.Env} env
- * @param {Function} func
- * @param {jasmine.Spec} spec
- */
-jasmine.Block = function(env, func, spec) {
-  this.env = env;
-  this.func = func;
-  this.spec = spec;
-};
-
-jasmine.Block.prototype.execute = function(onComplete) {  
-  try {
-    this.func.apply(this.spec);
-  } catch (e) {
-    this.spec.fail(e);
-  }
-  onComplete();
-};
-/** JavaScript API reporter.
- *
- * @constructor
- */
-jasmine.JsApiReporter = function() {
-  this.started = false;
-  this.finished = false;
-  this.suites_ = [];
-  this.results_ = {};
-};
-
-jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
-  this.started = true;
-  var suites = runner.topLevelSuites();
-  for (var i = 0; i < suites.length; i++) {
-    var suite = suites[i];
-    this.suites_.push(this.summarize_(suite));
-  }
-};
-
-jasmine.JsApiReporter.prototype.suites = function() {
-  return this.suites_;
-};
-
-jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
-  var isSuite = suiteOrSpec instanceof jasmine.Suite;
-  var summary = {
-    id: suiteOrSpec.id,
-    name: suiteOrSpec.description,
-    type: isSuite ? 'suite' : 'spec',
-    children: []
-  };
-  
-  if (isSuite) {
-    var children = suiteOrSpec.children();
-    for (var i = 0; i < children.length; i++) {
-      summary.children.push(this.summarize_(children[i]));
-    }
-  }
-  return summary;
-};
-
-jasmine.JsApiReporter.prototype.results = function() {
-  return this.results_;
-};
-
-jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
-  return this.results_[specId];
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
-  this.finished = true;
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
-  this.results_[spec.id] = {
-    messages: spec.results().getItems(),
-    result: spec.results().failedCount > 0 ? "failed" : "passed"
-  };
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.log = function(str) {
-};
-
-jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
-  var results = {};
-  for (var i = 0; i < specIds.length; i++) {
-    var specId = specIds[i];
-    results[specId] = this.summarizeResult_(this.results_[specId]);
-  }
-  return results;
-};
-
-jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
-  var summaryMessages = [];
-  var messagesLength = result.messages.length;
-  for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
-    var resultMessage = result.messages[messageIndex];
-    summaryMessages.push({
-      text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
-      passed: resultMessage.passed ? resultMessage.passed() : true,
-      type: resultMessage.type,
-      message: resultMessage.message,
-      trace: {
-        stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
-      }
-    });
-  }
-
-  return {
-    result : result.result,
-    messages : summaryMessages
-  };
-};
-
-/**
- * @constructor
- * @param {jasmine.Env} env
- * @param actual
- * @param {jasmine.Spec} spec
- */
-jasmine.Matchers = function(env, actual, spec, opt_isNot) {
-  this.env = env;
-  this.actual = actual;
-  this.spec = spec;
-  this.isNot = opt_isNot || false;
-  this.reportWasCalled_ = false;
-};
-
-// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
-jasmine.Matchers.pp = function(str) {
-  throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
-};
-
-// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
-jasmine.Matchers.prototype.report = function(result, failing_message, details) {
-  throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
-};
-
-jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
-  for (var methodName in prototype) {
-    if (methodName == 'report') continue;
-    var orig = prototype[methodName];
-    matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
-  }
-};
-
-jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
-  return function() {
-    var matcherArgs = jasmine.util.argsToArray(arguments);
-    var result = matcherFunction.apply(this, arguments);
-
-    if (this.isNot) {
-      result = !result;
-    }
-
-    if (this.reportWasCalled_) return result;
-
-    var message;
-    if (!result) {
-      if (this.message) {
-        message = this.message.apply(this, arguments);
-        if (jasmine.isArray_(message)) {
-          message = message[this.isNot ? 1 : 0];
-        }
-      } else {
-        var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
-        message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
-        if (matcherArgs.length > 0) {
-          for (var i = 0; i < matcherArgs.length; i++) {
-            if (i > 0) message += ",";
-            message += " " + jasmine.pp(matcherArgs[i]);
-          }
-        }
-        message += ".";
-      }
-    }
-    var expectationResult = new jasmine.ExpectationResult({
-      matcherName: matcherName,
-      passed: result,
-      expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
-      actual: this.actual,
-      message: message
-    });
-    this.spec.addMatcherResult(expectationResult);
-    return jasmine.undefined;
-  };
-};
-
-
-
-
-/**
- * toBe: compares the actual to the expected using ===
- * @param expected
- */
-jasmine.Matchers.prototype.toBe = function(expected) {
-  return this.actual === expected;
-};
-
-/**
- * toNotBe: compares the actual to the expected using !==
- * @param expected
- * @deprecated as of 1.0. Use not.toBe() instead.
- */
-jasmine.Matchers.prototype.toNotBe = function(expected) {
-  return this.actual !== expected;
-};
-
-/**
- * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
- *
- * @param expected
- */
-jasmine.Matchers.prototype.toEqual = function(expected) {
-  return this.env.equals_(this.actual, expected);
-};
-
-/**
- * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
- * @param expected
- * @deprecated as of 1.0. Use not.toEqual() instead.
- */
-jasmine.Matchers.prototype.toNotEqual = function(expected) {
-  return !this.env.equals_(this.actual, expected);
-};
-
-/**
- * Matcher that compares the actual to the expected using a regular expression.  Constructs a RegExp, so takes
- * a pattern or a String.
- *
- * @param expected
- */
-jasmine.Matchers.prototype.toMatch = function(expected) {
-  return new RegExp(expected).test(this.actual);
-};
-
-/**
- * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
- * @param expected
- * @deprecated as of 1.0. Use not.toMatch() instead.
- */
-jasmine.Matchers.prototype.toNotMatch = function(expected) {
-  return !(new RegExp(expected).test(this.actual));
-};
-
-/**
- * Matcher that compares the actual to jasmine.undefined.
- */
-jasmine.Matchers.prototype.toBeDefined = function() {
-  return (this.actual !== jasmine.undefined);
-};
-
-/**
- * Matcher that compares the actual to jasmine.undefined.
- */
-jasmine.Matchers.prototype.toBeUndefined = function() {
-  return (this.actual === jasmine.undefined);
-};
-
-/**
- * Matcher that compares the actual to null.
- */
-jasmine.Matchers.prototype.toBeNull = function() {
-  return (this.actual === null);
-};
-
-/**
- * Matcher that boolean not-nots the actual.
- */
-jasmine.Matchers.prototype.toBeTruthy = function() {
-  return !!this.actual;
-};
-
-
-/**
- * Matcher that boolean nots the actual.
- */
-jasmine.Matchers.prototype.toBeFalsy = function() {
-  return !this.actual;
-};
-
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was called.
- */
-jasmine.Matchers.prototype.toHaveBeenCalled = function() {
-  if (arguments.length > 0) {
-    throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
-  }
-
-  if (!jasmine.isSpy(this.actual)) {
-    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
-  }
-
-  this.message = function() {
-    return [
-      "Expected spy " + this.actual.identity + " to have been called.",
-      "Expected spy " + this.actual.identity + " not to have been called."
-    ];
-  };
-
-  return this.actual.wasCalled;
-};
-
-/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
-jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was not called.
- *
- * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
- */
-jasmine.Matchers.prototype.wasNotCalled = function() {
-  if (arguments.length > 0) {
-    throw new Error('wasNotCalled does not take arguments');
-  }
-
-  if (!jasmine.isSpy(this.actual)) {
-    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
-  }
-
-  this.message = function() {
-    return [
-      "Expected spy " + this.actual.identity + " to not have been called.",
-      "Expected spy " + this.actual.identity + " to have been called."
-    ];
-  };
-
-  return !this.actual.wasCalled;
-};
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
- *
- * @example
- *
- */
-jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
-  var expectedArgs = jasmine.util.argsToArray(arguments);
-  if (!jasmine.isSpy(this.actual)) {
-    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
-  }
-  this.message = function() {
-    if (this.actual.callCount === 0) {
-      // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
-      return [
-        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
-        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
-      ];
-    } else {
-      return [
-        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
-        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
-      ];
-    }
-  };
-
-  return this.env.contains_(this.actual.argsForCall, expectedArgs);
-};
-
-/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
-jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
-
-/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
-jasmine.Matchers.prototype.wasNotCalledWith = function() {
-  var expectedArgs = jasmine.util.argsToArray(arguments);
-  if (!jasmine.isSpy(this.actual)) {
-    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
-  }
-
-  this.message = function() {
-    return [
-      "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
-      "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
-    ];
-  };
-
-  return !this.env.contains_(this.actual.argsForCall, expectedArgs);
-};
-
-/**
- * Matcher that checks that the expected item is an element in the actual Array.
- *
- * @param {Object} expected
- */
-jasmine.Matchers.prototype.toContain = function(expected) {
-  return this.env.contains_(this.actual, expected);
-};
-
-/**
- * Matcher that checks that the expected item is NOT an element in the actual Array.
- *
- * @param {Object} expected
- * @deprecated as of 1.0. Use not.toContain() instead.
- */
-jasmine.Matchers.prototype.toNotContain = function(expected) {
-  return !this.env.contains_(this.actual, expected);
-};
-
-jasmine.Matchers.prototype.toBeLessThan = function(expected) {
-  return this.actual < expected;
-};
-
-jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
-  return this.actual > expected;
-};
-
-/**
- * Matcher that checks that the expected item is equal to the actual item
- * up to a given level of decimal precision (default 2).
- *
- * @param {Number} expected
- * @param {Number} precision
- */
-jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
-  if (!(precision === 0)) {
-    precision = precision || 2;
-  }
-  var multiplier = Math.pow(10, precision);
-  var actual = Math.round(this.actual * multiplier);
-  expected = Math.round(expected * multiplier);
-  return expected == actual;
-};
-
-/**
- * Matcher that checks that the expected exception was thrown by the actual.
- *
- * @param {String} expected
- */
-jasmine.Matchers.prototype.toThrow = function(expected) {
-  var result = false;
-  var exception;
-  if (typeof this.actual != 'function') {
-    throw new Error('Actual is not a function');
-  }
-  try {
-    this.actual();
-  } catch (e) {
-    exception = e;
-  }
-  if (exception) {
-    result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
-  }
-
-  var not = this.isNot ? "not " : "";
-
-  this.message = function() {
-    if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
-      return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
-    } else {
-      return "Expected function to throw an exception.";
-    }
-  };
-
-  return result;
-};
-
-jasmine.Matchers.Any = function(expectedClass) {
-  this.expectedClass = expectedClass;
-};
-
-jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {
-  if (this.expectedClass == String) {
-    return typeof other == 'string' || other instanceof String;
-  }
-
-  if (this.expectedClass == Number) {
-    return typeof other == 'number' || other instanceof Number;
-  }
-
-  if (this.expectedClass == Function) {
-    return typeof other == 'function' || other instanceof Function;
-  }
-
-  if (this.expectedClass == Object) {
-    return typeof other == 'object';
-  }
-
-  return other instanceof this.expectedClass;
-};
-
-jasmine.Matchers.Any.prototype.jasmineToString = function() {
-  return '<jasmine.any(' + this.expectedClass + ')>';
-};
-
-jasmine.Matchers.ObjectContaining = function (sample) {
-  this.sample = sample;
-};
-
-jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
-  mismatchKeys = mismatchKeys || [];
-  mismatchValues = mismatchValues || [];
-
-  var env = jasmine.getEnv();
-
-  var hasKey = function(obj, keyName) {
-    return obj != null && obj[keyName] !== jasmine.undefined;
-  };
-
-  for (var property in this.sample) {
-    if (!hasKey(other, property) && hasKey(this.sample, property)) {
-      mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
-    }
-    else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
-      mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
-    }
-  }
-
-  return (mismatchKeys.length === 0 && mismatchValues.length === 0);
-};
-
-jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
-  return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>";
-};
-// Mock setTimeout, clearTimeout
-// Contributed by Pivotal Computer Systems, www.pivotalsf.com
-
-jasmine.FakeTimer = function() {
-  this.reset();
-
-  var self = this;
-  self.setTimeout = function(funcToCall, millis) {
-    self.timeoutsMade++;
-    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
-    return self.timeoutsMade;
-  };
-
-  self.setInterval = function(funcToCall, millis) {
-    self.timeoutsMade++;
-    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
-    return self.timeoutsMade;
-  };
-
-  self.clearTimeout = function(timeoutKey) {
-    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
-  };
-
-  self.clearInterval = function(timeoutKey) {
-    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
-  };
-
-};
-
-jasmine.FakeTimer.prototype.reset = function() {
-  this.timeoutsMade = 0;
-  this.scheduledFunctions = {};
-  this.nowMillis = 0;
-};
-
-jasmine.FakeTimer.prototype.tick = function(millis) {
-  var oldMillis = this.nowMillis;
-  var newMillis = oldMillis + millis;
-  this.runFunctionsWithinRange(oldMillis, newMillis);
-  this.nowMillis = newMillis;
-};
-
-jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
-  var scheduledFunc;
-  var funcsToRun = [];
-  for (var timeoutKey in this.scheduledFunctions) {
-    scheduledFunc = this.scheduledFunctions[timeoutKey];
-    if (scheduledFunc != jasmine.undefined &&
-        scheduledFunc.runAtMillis >= oldMillis &&
-        scheduledFunc.runAtMillis <= nowMillis) {
-      funcsToRun.push(scheduledFunc);
-      this.scheduledFunctions[timeoutKey] = jasmine.undefined;
-    }
-  }
-
-  if (funcsToRun.length > 0) {
-    funcsToRun.sort(function(a, b) {
-      return a.runAtMillis - b.runAtMillis;
-    });
-    for (var i = 0; i < funcsToRun.length; ++i) {
-      try {
-        var funcToRun = funcsToRun[i];
-        this.nowMillis = funcToRun.runAtMillis;
-        funcToRun.funcToCall();
-        if (funcToRun.recurring) {
-          this.scheduleFunction(funcToRun.timeoutKey,
-              funcToRun.funcToCall,
-              funcToRun.millis,
-              true);
-        }
-      } catch(e) {
-      }
-    }
-    this.runFunctionsWithinRange(oldMillis, nowMillis);
-  }
-};
-
-jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
-  this.scheduledFunctions[timeoutKey] = {
-    runAtMillis: this.nowMillis + millis,
-    funcToCall: funcToCall,
-    recurring: recurring,
-    timeoutKey: timeoutKey,
-    millis: millis
-  };
-};
-
-/**
- * @namespace
- */
-jasmine.Clock = {
-  defaultFakeTimer: new jasmine.FakeTimer(),
-
-  reset: function() {
-    jasmine.Clock.assertInstalled();
-    jasmine.Clock.defaultFakeTimer.reset();
-  },
-
-  tick: function(millis) {
-    jasmine.Clock.assertInstalled();
-    jasmine.Clock.defaultFakeTimer.tick(millis);
-  },
-
-  runFunctionsWithinRange: function(oldMillis, nowMillis) {
-    jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
-  },
-
-  scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
-    jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
-  },
-
-  useMock: function() {
-    if (!jasmine.Clock.isInstalled()) {
-      var spec = jasmine.getEnv().currentSpec;
-      spec.after(jasmine.Clock.uninstallMock);
-
-      jasmine.Clock.installMock();
-    }
-  },
-
-  installMock: function() {
-    jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
-  },
-
-  uninstallMock: function() {
-    jasmine.Clock.assertInstalled();
-    jasmine.Clock.installed = jasmine.Clock.real;
-  },
-
-  real: {
-    setTimeout: jasmine.getGlobal().setTimeout,
-    clearTimeout: jasmine.getGlobal().clearTimeout,
-    setInterval: jasmine.getGlobal().setInterval,
-    clearInterval: jasmine.getGlobal().clearInterval
-  },
-
-  assertInstalled: function() {
-    if (!jasmine.Clock.isInstalled()) {
-      throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
-    }
-  },
-
-  isInstalled: function() {
-    return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
-  },
-
-  installed: null
-};
-jasmine.Clock.installed = jasmine.Clock.real;
-
-//else for IE support
-jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
-  if (jasmine.Clock.installed.setTimeout.apply) {
-    return jasmine.Clock.installed.setTimeout.apply(this, arguments);
-  } else {
-    return jasmine.Clock.installed.setTimeout(funcToCall, millis);
-  }
-};
-
-jasmine.getGlobal().setInterval = function(funcToCall, millis) {
-  if (jasmine.Clock.installed.setInterval.apply) {
-    return jasmine.Clock.installed.setInterval.apply(this, arguments);
-  } else {
-    return jasmine.Clock.installed.setInterval(funcToCall, millis);
-  }
-};
-
-jasmine.getGlobal().clearTimeout = function(timeoutKey) {
-  if (jasmine.Clock.installed.clearTimeout.apply) {
-    return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
-  } else {
-    return jasmine.Clock.installed.clearTimeout(timeoutKey);
-  }
-};
-
-jasmine.getGlobal().clearInterval = function(timeoutKey) {
-  if (jasmine.Clock.installed.clearTimeout.apply) {
-    return jasmine.Clock.installed.clearInterval.apply(this, arguments);
-  } else {
-    return jasmine.Clock.installed.clearInterval(timeoutKey);
-  }
-};
-
-/**
- * @constructor
- */
-jasmine.MultiReporter = function() {
-  this.subReporters_ = [];
-};
-jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
-
-jasmine.MultiReporter.prototype.addReporter = function(reporter) {
-  this.subReporters_.push(reporter);
-};
-
-(function() {
-  var functionNames = [
-    "reportRunnerStarting",
-    "reportRunnerResults",
-    "reportSuiteResults",
-    "reportSpecStarting",
-    "reportSpecResults",
-    "log"
-  ];
-  for (var i = 0; i < functionNames.length; i++) {
-    var functionName = functionNames[i];
-    jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
-      return function() {
-        for (var j = 0; j < this.subReporters_.length; j++) {
-          var subReporter = this.subReporters_[j];
-          if (subReporter[functionName]) {
-            subReporter[functionName].apply(subReporter, arguments);
-          }
-        }
-      };
-    })(functionName);
-  }
-})();
-/**
- * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
- *
- * @constructor
- */
-jasmine.NestedResults = function() {
-  /**
-   * The total count of results
-   */
-  this.totalCount = 0;
-  /**
-   * Number of passed results
-   */
-  this.passedCount = 0;
-  /**
-   * Number of failed results
-   */
-  this.failedCount = 0;
-  /**
-   * Was this suite/spec skipped?
-   */
-  this.skipped = false;
-  /**
-   * @ignore
-   */
-  this.items_ = [];
-};
-
-/**
- * Roll up the result counts.
- *
- * @param result
- */
-jasmine.NestedResults.prototype.rollupCounts = function(result) {
-  this.totalCount += result.totalCount;
-  this.passedCount += result.passedCount;
-  this.failedCount += result.failedCount;
-};
-
-/**
- * Adds a log message.
- * @param values Array of message parts which will be concatenated later.
- */
-jasmine.NestedResults.prototype.log = function(values) {
-  this.items_.push(new jasmine.MessageResult(values));
-};
-
-/**
- * Getter for the results: message & results.
- */
-jasmine.NestedResults.prototype.getItems = function() {
-  return this.items_;
-};
-
-/**
- * Adds a result, tracking counts (total, passed, & failed)
- * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
- */
-jasmine.NestedResults.prototype.addResult = function(result) {
-  if (result.type != 'log') {
-    if (result.items_) {
-      this.rollupCounts(result);
-    } else {
-      this.totalCount++;
-      if (result.passed()) {
-        this.passedCount++;
-      } else {
-        this.failedCount++;
-      }
-    }
-  }
-  this.items_.push(result);
-};
-
-/**
- * @returns {Boolean} True if <b>everything</b> below passed
- */
-jasmine.NestedResults.prototype.passed = function() {
-  return this.passedCount === this.totalCount;
-};
-/**
- * Base class for pretty printing for expectation results.
- */
-jasmine.PrettyPrinter = function() {
-  this.ppNestLevel_ = 0;
-};
-
-/**
- * Formats a value in a nice, human-readable string.
- *
- * @param value
- */
-jasmine.PrettyPrinter.prototype.format = function(value) {
-  if (this.ppNestLevel_ > 40) {
-    throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
-  }
-
-  this.ppNestLevel_++;
-  try {
-    if (value === jasmine.undefined) {
-      this.emitScalar('undefined');
-    } else if (value === null) {
-      this.emitScalar('null');
-    } else if (value === jasmine.getGlobal()) {
-      this.emitScalar('<global>');
-    } else if (value.jasmineToString) {
-      this.emitScalar(value.jasmineToString());
-    } else if (typeof value === 'string') {
-      this.emitString(value);
-    } else if (jasmine.isSpy(value)) {
-      this.emitScalar("spy on " + value.identity);
-    } else if (value instanceof RegExp) {
-      this.emitScalar(value.toString());
-    } else if (typeof value === 'function') {
-      this.emitScalar('Function');
-    } else if (typeof value.nodeType === 'number') {
-      this.emitScalar('HTMLNode');
-    } else if (value instanceof Date) {
-      this.emitScalar('Date(' + value + ')');
-    } else if (value.__Jasmine_been_here_before__) {
-      this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
-    } else if (jasmine.isArray_(value) || typeof value == 'object') {
-      value.__Jasmine_been_here_before__ = true;
-      if (jasmine.isArray_(value)) {
-        this.emitArray(value);
-      } else {
-        this.emitObject(value);
-      }
-      delete value.__Jasmine_been_here_before__;
-    } else {
-      this.emitScalar(value.toString());
-    }
-  } finally {
-    this.ppNestLevel_--;
-  }
-};
-
-jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
-  for (var property in obj) {
-    if (property == '__Jasmine_been_here_before__') continue;
-    fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && 
-                                         obj.__lookupGetter__(property) !== null) : false);
-  }
-};
-
-jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
-jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
-jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
-jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
-
-jasmine.StringPrettyPrinter = function() {
-  jasmine.PrettyPrinter.call(this);
-
-  this.string = '';
-};
-jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
-
-jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
-  this.append(value);
-};
-
-jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
-  this.append("'" + value + "'");
-};
-
-jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
-  this.append('[ ');
-  for (var i = 0; i < array.length; i++) {
-    if (i > 0) {
-      this.append(', ');
-    }
-    this.format(array[i]);
-  }
-  this.append(' ]');
-};
-
-jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
-  var self = this;
-  this.append('{ ');
-  var first = true;
-
-  this.iterateObject(obj, function(property, isGetter) {
-    if (first) {
-      first = false;
-    } else {
-      self.append(', ');
-    }
-
-    self.append(property);
-    self.append(' : ');
-    if (isGetter) {
-      self.append('<getter>');
-    } else {
-      self.format(obj[property]);
-    }
-  });
-
-  this.append(' }');
-};
-
-jasmine.StringPrettyPrinter.prototype.append = function(value) {
-  this.string += value;
-};
-jasmine.Queue = function(env) {
-  this.env = env;
-  this.blocks = [];
-  this.running = false;
-  this.index = 0;
-  this.offset = 0;
-  this.abort = false;
-};
-
-jasmine.Queue.prototype.addBefore = function(block) {
-  this.blocks.unshift(block);
-};
-
-jasmine.Queue.prototype.add = function(block) {
-  this.blocks.push(block);
-};
-
-jasmine.Queue.prototype.insertNext = function(block) {
-  this.blocks.splice((this.index + this.offset + 1), 0, block);
-  this.offset++;
-};
-
-jasmine.Queue.prototype.start = function(onComplete) {
-  this.running = true;
-  this.onComplete = onComplete;
-  this.next_();
-};
-
-jasmine.Queue.prototype.isRunning = function() {
-  return this.running;
-};
-
-jasmine.Queue.LOOP_DONT_RECURSE = true;
-
-jasmine.Queue.prototype.next_ = function() {
-  var self = this;
-  var goAgain = true;
-
-  while (goAgain) {
-    goAgain = false;
-    
-    if (self.index < self.blocks.length && !this.abort) {
-      var calledSynchronously = true;
-      var completedSynchronously = false;
-
-      var onComplete = function () {
-        if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
-          completedSynchronously = true;
-          return;
-        }
-
-        if (self.blocks[self.index].abort) {
-          self.abort = true;
-        }
-
-        self.offset = 0;
-        self.index++;
-
-        var now = new Date().getTime();
-        if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
-          self.env.lastUpdate = now;
-          self.env.setTimeout(function() {
-            self.next_();
-          }, 0);
-        } else {
-          if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
-            goAgain = true;
-          } else {
-            self.next_();
-          }
-        }
-      };
-      self.blocks[self.index].execute(onComplete);
-
-      calledSynchronously = false;
-      if (completedSynchronously) {
-        onComplete();
-      }
-      
-    } else {
-      self.running = false;
-      if (self.onComplete) {
-        self.onComplete();
-      }
-    }
-  }
-};
-
-jasmine.Queue.prototype.results = function() {
-  var results = new jasmine.NestedResults();
-  for (var i = 0; i < this.blocks.length; i++) {
-    if (this.blocks[i].results) {
-      results.addResult(this.blocks[i].results());
-    }
-  }
-  return results;
-};
-
-
-/**
- * Runner
- *
- * @constructor
- * @param {jasmine.Env} env
- */
-jasmine.Runner = function(env) {
-  var self = this;
-  self.env = env;
-  self.queue = new jasmine.Queue(env);
-  self.before_ = [];
-  self.after_ = [];
-  self.suites_ = [];
-};
-
-jasmine.Runner.prototype.execute = function() {
-  var self = this;
-  if (self.env.reporter.reportRunnerStarting) {
-    self.env.reporter.reportRunnerStarting(this);
-  }
-  self.queue.start(function () {
-    self.finishCallback();
-  });
-};
-
-jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
-  beforeEachFunction.typeName = 'beforeEach';
-  this.before_.splice(0,0,beforeEachFunction);
-};
-
-jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
-  afterEachFunction.typeName = 'afterEach';
-  this.after_.splice(0,0,afterEachFunction);
-};
-
-
-jasmine.Runner.prototype.finishCallback = function() {
-  this.env.reporter.reportRunnerResults(this);
-};
-
-jasmine.Runner.prototype.addSuite = function(suite) {
-  this.suites_.push(suite);
-};
-
-jasmine.Runner.prototype.add = function(block) {
-  if (block instanceof jasmine.Suite) {
-    this.addSuite(block);
-  }
-  this.queue.add(block);
-};
-
-jasmine.Runner.prototype.specs = function () {
-  var suites = this.suites();
-  var specs = [];
-  for (var i = 0; i < suites.length; i++) {
-    specs = specs.concat(suites[i].specs());
-  }
-  return specs;
-};
-
-jasmine.Runner.prototype.suites = function() {
-  return this.suites_;
-};
-
-jasmine.Runner.prototype.topLevelSuites = function() {
-  var topLevelSuites = [];
-  for (var i = 0; i < this.suites_.length; i++) {
-    if (!this.suites_[i].parentSuite) {
-      topLevelSuites.push(this.suites_[i]);
-    }
-  }
-  return topLevelSuites;
-};
-
-jasmine.Runner.prototype.results = function() {
-  return this.queue.results();
-};
-/**
- * Internal representation of a Jasmine specification, or test.
- *
- * @constructor
- * @param {jasmine.Env} env
- * @param {jasmine.Suite} suite
- * @param {String} description
- */
-jasmine.Spec = function(env, suite, description) {
-  if (!env) {
-    throw new Error('jasmine.Env() required');
-  }
-  if (!suite) {
-    throw new Error('jasmine.Suite() required');
-  }
-  var spec = this;
-  spec.id = env.nextSpecId ? env.nextSpecId() : null;
-  spec.env = env;
-  spec.suite = suite;
-  spec.description = description;
-  spec.queue = new jasmine.Queue(env);
-
-  spec.afterCallbacks = [];
-  spec.spies_ = [];
-
-  spec.results_ = new jasmine.NestedResults();
-  spec.results_.description = description;
-  spec.matchersClass = null;
-};
-
-jasmine.Spec.prototype.getFullName = function() {
-  return this.suite.getFullName() + ' ' + this.description + '.';
-};
-
-
-jasmine.Spec.prototype.results = function() {
-  return this.results_;
-};
-
-/**
- * All parameters are pretty-printed and concatenated together, then written to the spec's output.
- *
- * Be careful not to leave calls to <code>jasmine.log</code> in production code.
- */
-jasmine.Spec.prototype.log = function() {
-  return this.results_.log(arguments);
-};
-
-jasmine.Spec.prototype.runs = function (func) {
-  var block = new jasmine.Block(this.env, func, this);
-  this.addToQueue(block);
-  return this;
-};
-
-jasmine.Spec.prototype.addToQueue = function (block) {
-  if (this.queue.isRunning()) {
-    this.queue.insertNext(block);
-  } else {
-    this.queue.add(block);
-  }
-};
-
-/**
- * @param {jasmine.ExpectationResult} result
- */
-jasmine.Spec.prototype.addMatcherResult = function(result) {
-  this.results_.addResult(result);
-};
-
-jasmine.Spec.prototype.expect = function(actual) {
-  var positive = new (this.getMatchersClass_())(this.env, actual, this);
-  positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
-  return positive;
-};
-
-/**
- * Waits a fixed time period before moving to the next block.
- *
- * @deprecated Use waitsFor() instead
- * @param {Number} timeout milliseconds to wait
- */
-jasmine.Spec.prototype.waits = function(timeout) {
-  var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
-  this.addToQueue(waitsFunc);
-  return this;
-};
-
-/**
- * Waits for the latchFunction to return true before proceeding to the next block.
- *
- * @param {Function} latchFunction
- * @param {String} optional_timeoutMessage
- * @param {Number} optional_timeout
- */
-jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
-  var latchFunction_ = null;
-  var optional_timeoutMessage_ = null;
-  var optional_timeout_ = null;
-
-  for (var i = 0; i < arguments.length; i++) {
-    var arg = arguments[i];
-    switch (typeof arg) {
-      case 'function':
-        latchFunction_ = arg;
-        break;
-      case 'string':
-        optional_timeoutMessage_ = arg;
-        break;
-      case 'number':
-        optional_timeout_ = arg;
-        break;
-    }
-  }
-
-  var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
-  this.addToQueue(waitsForFunc);
-  return this;
-};
-
-jasmine.Spec.prototype.fail = function (e) {
-  var expectationResult = new jasmine.ExpectationResult({
-    passed: false,
-    message: e ? jasmine.util.formatException(e) : 'Exception',
-    trace: { stack: e.stack }
-  });
-  this.results_.addResult(expectationResult);
-};
-
-jasmine.Spec.prototype.getMatchersClass_ = function() {
-  return this.matchersClass || this.env.matchersClass;
-};
-
-jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
-  var parent = this.getMatchersClass_();
-  var newMatchersClass = function() {
-    parent.apply(this, arguments);
-  };
-  jasmine.util.inherit(newMatchersClass, parent);
-  jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
-  this.matchersClass = newMatchersClass;
-};
-
-jasmine.Spec.prototype.finishCallback = function() {
-  this.env.reporter.reportSpecResults(this);
-};
-
-jasmine.Spec.prototype.finish = function(onComplete) {
-  this.removeAllSpies();
-  this.finishCallback();
-  if (onComplete) {
-    onComplete();
-  }
-};
-
-jasmine.Spec.prototype.after = function(doAfter) {
-  if (this.queue.isRunning()) {
-    this.queue.add(new jasmine.Block(this.env, doAfter, this));
-  } else {
-    this.afterCallbacks.unshift(doAfter);
-  }
-};
-
-jasmine.Spec.prototype.execute = function(onComplete) {
-  var spec = this;
-  if (!spec.env.specFilter(spec)) {
-    spec.results_.skipped = true;
-    spec.finish(onComplete);
-    return;
-  }
-
-  this.env.reporter.reportSpecStarting(this);
-
-  spec.env.currentSpec = spec;
-
-  spec.addBeforesAndAftersToQueue();
-
-  spec.queue.start(function () {
-    spec.finish(onComplete);
-  });
-};
-
-jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
-  var runner = this.env.currentRunner();
-  var i;
-
-  for (var suite = this.suite; suite; suite = suite.parentSuite) {
-    for (i = 0; i < suite.before_.length; i++) {
-      this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
-    }
-  }
-  for (i = 0; i < runner.before_.length; i++) {
-    this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
-  }
-  for (i = 0; i < this.afterCallbacks.length; i++) {
-    this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
-  }
-  for (suite = this.suite; suite; suite = suite.parentSuite) {
-    for (i = 0; i < suite.after_.length; i++) {
-      this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
-    }
-  }
-  for (i = 0; i < runner.after_.length; i++) {
-    this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
-  }
-};
-
-jasmine.Spec.prototype.explodes = function() {
-  throw 'explodes function should not have been called';
-};
-
-jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
-  if (obj == jasmine.undefined) {
-    throw "spyOn could not find an object to spy upon for " + methodName + "()";
-  }
-
-  if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
-    throw methodName + '() method does not exist';
-  }
-
-  if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
-    throw new Error(methodName + ' has already been spied upon');
-  }
-
-  var spyObj = jasmine.createSpy(methodName);
-
-  this.spies_.push(spyObj);
-  spyObj.baseObj = obj;
-  spyObj.methodName = methodName;
-  spyObj.originalValue = obj[methodName];
-
-  obj[methodName] = spyObj;
-
-  return spyObj;
-};
-
-jasmine.Spec.prototype.removeAllSpies = function() {
-  for (var i = 0; i < this.spies_.length; i++) {
-    var spy = this.spies_[i];
-    spy.baseObj[spy.methodName] = spy.originalValue;
-  }
-  this.spies_ = [];
-};
-
-/**
- * Internal representation of a Jasmine suite.
- *
- * @constructor
- * @param {jasmine.Env} env
- * @param {String} description
- * @param {Function} specDefinitions
- * @param {jasmine.Suite} parentSuite
- */
-jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
-  var self = this;
-  self.id = env.nextSuiteId ? env.nextSuiteId() : null;
-  self.description = description;
-  self.queue = new jasmine.Queue(env);
-  self.parentSuite = parentSuite;
-  self.env = env;
-  self.before_ = [];
-  self.after_ = [];
-  self.children_ = [];
-  self.suites_ = [];
-  self.specs_ = [];
-};
-
-jasmine.Suite.prototype.getFullName = function() {
-  var fullName = this.description;
-  for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
-    fullName = parentSuite.description + ' ' + fullName;
-  }
-  return fullName;
-};
-
-jasmine.Suite.prototype.finish = function(onComplete) {
-  this.env.reporter.reportSuiteResults(this);
-  this.finished = true;
-  if (typeof(onComplete) == 'function') {
-    onComplete();
-  }
-};
-
-jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
-  beforeEachFunction.typeName = 'beforeEach';
-  this.before_.unshift(beforeEachFunction);
-};
-
-jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
-  afterEachFunction.typeName = 'afterEach';
-  this.after_.unshift(afterEachFunction);
-};
-
-jasmine.Suite.prototype.results = function() {
-  return this.queue.results();
-};
-
-jasmine.Suite.prototype.add = function(suiteOrSpec) {
-  this.children_.push(suiteOrSpec);
-  if (suiteOrSpec instanceof jasmine.Suite) {
-    this.suites_.push(suiteOrSpec);
-    this.env.currentRunner().addSuite(suiteOrSpec);
-  } else {
-    this.specs_.push(suiteOrSpec);
-  }
-  this.queue.add(suiteOrSpec);
-};
-
-jasmine.Suite.prototype.specs = function() {
-  return this.specs_;
-};
-
-jasmine.Suite.prototype.suites = function() {
-  return this.suites_;
-};
-
-jasmine.Suite.prototype.children = function() {
-  return this.children_;
-};
-
-jasmine.Suite.prototype.execute = function(onComplete) {
-  var self = this;
-  this.queue.start(function () {
-    self.finish(onComplete);
-  });
-};
-jasmine.WaitsBlock = function(env, timeout, spec) {
-  this.timeout = timeout;
-  jasmine.Block.call(this, env, null, spec);
-};
-
-jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
-
-jasmine.WaitsBlock.prototype.execute = function (onComplete) {
-  if (jasmine.VERBOSE) {
-    this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
-  }
-  this.env.setTimeout(function () {
-    onComplete();
-  }, this.timeout);
-};
-/**
- * A block which waits for some condition to become true, with timeout.
- *
- * @constructor
- * @extends jasmine.Block
- * @param {jasmine.Env} env The Jasmine environment.
- * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
- * @param {Function} latchFunction A function which returns true when the desired condition has been met.
- * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
- * @param {jasmine.Spec} spec The Jasmine spec.
- */
-jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
-  this.timeout = timeout || env.defaultTimeoutInterval;
-  this.latchFunction = latchFunction;
-  this.message = message;
-  this.totalTimeSpentWaitingForLatch = 0;
-  jasmine.Block.call(this, env, null, spec);
-};
-jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
-
-jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
-
-jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
-  if (jasmine.VERBOSE) {
-    this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
-  }
-  var latchFunctionResult;
-  try {
-    latchFunctionResult = this.latchFunction.apply(this.spec);
-  } catch (e) {
-    this.spec.fail(e);
-    onComplete();
-    return;
-  }
-
-  if (latchFunctionResult) {
-    onComplete();
-  } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
-    var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
-    this.spec.fail({
-      name: 'timeout',
-      message: message
-    });
-
-    this.abort = true;
-    onComplete();
-  } else {
-    this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
-    var self = this;
-    this.env.setTimeout(function() {
-      self.execute(onComplete);
-    }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
-  }
-};
-
-jasmine.version_= {
-  "major": 1,
-  "minor": 2,
-  "build": 0,
-  "revision": 1337005947
-};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/config.xml
----------------------------------------------------------------------
diff --git a/templates/www/config.xml b/templates/www/config.xml
new file mode 100644
index 0000000..aca4d5e
--- /dev/null
+++ b/templates/www/config.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<widget xmlns     = "http://www.w3.org/ns/widgets"
+        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"  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" />
+
+    <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>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/css/index.css
----------------------------------------------------------------------
diff --git a/templates/www/css/index.css b/templates/www/css/index.css
new file mode 100644
index 0000000..c869f87
--- /dev/null
+++ b/templates/www/css/index.css
@@ -0,0 +1,100 @@
+html,
+body {
+    height:100%;
+    font-size:12px;
+    width:100%;
+}
+
+html {
+    display:table;
+}
+
+body {
+    background-color:#A7A7A7;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    display:table-cell;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    text-transform:uppercase;
+    vertical-align:middle;
+}
+
+.app {
+    background-image:url(../img/cordova.png);
+    background-repeat:no-repeat;
+    margin:0px auto;
+    width:275px;
+}
+
+h1 {
+    font-size:2em;
+    font-weight:300;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.status {
+    background-color:#333333;
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:1em;
+    margin:0px auto;
+    padding:2px 10px;
+    text-align:center;
+    width:100%;
+    max-width:175px;
+}
+
+.status.complete {
+    background-color:#4B946A;
+}
+
+.hide {
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}
+
+/* portrait */
+/* @media screen and (max-aspect-ratio: 1/1) */
+.app {
+    background-position:center top;
+    height:100px;              /* adds enough room for text */
+    padding:180px 0px 0px 0px; /* background height - shadow offset */
+}
+
+/* lanscape (when wide enough) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:445px) {
+    .app {
+        background-position:left center;
+        height:140px;       /* height + padding = background image size */
+        padding-left:170px; /* background width */
+        padding-top:60px;   /* center the text */
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/img/cordova.png
----------------------------------------------------------------------
diff --git a/templates/www/img/cordova.png b/templates/www/img/cordova.png
new file mode 100644
index 0000000..e8169cf
Binary files /dev/null and b/templates/www/img/cordova.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/index.html
----------------------------------------------------------------------
diff --git a/templates/www/index.html b/templates/www/index.html
new file mode 100644
index 0000000..202af51
--- /dev/null
+++ b/templates/www/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+        <meta name = "format-detection" content = "telephone=no"/>
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width;" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello Cordova</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready">
+                <p class="status pending blink">Connecting to Device</p>
+                <p class="status complete blink hide">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/js/index.js
----------------------------------------------------------------------
diff --git a/templates/www/js/index.js b/templates/www/js/index.js
new file mode 100644
index 0000000..6140331
--- /dev/null
+++ b/templates/www/js/index.js
@@ -0,0 +1,20 @@
+var app = {
+    initialize: function() {
+        this.bind();
+    },
+    bind: function() {
+        document.addEventListener('deviceready', this.deviceready, false);
+    },
+    deviceready: function() {
+        // note that this is an event handler so the scope is that of the event
+        // so we need to call app.report(), and not this.report()
+        app.report('deviceready');
+    },
+    report: function(id) { 
+        console.log("report:" + id);
+        // hide the .pending <p> and show the .complete <p>
+        document.querySelector('#' + id + ' .pending').className += ' hide';
+        var completeElem = document.querySelector('#' + id + ' .complete');
+        completeElem.className = completeElem.className.split('hide').join('');
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_128.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_128.png b/templates/www/res/icon/cordova_128.png
new file mode 100644
index 0000000..3516df3
Binary files /dev/null and b/templates/www/res/icon/cordova_128.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_16.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_16.png b/templates/www/res/icon/cordova_16.png
new file mode 100644
index 0000000..54e19c5
Binary files /dev/null and b/templates/www/res/icon/cordova_16.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_24.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_24.png b/templates/www/res/icon/cordova_24.png
new file mode 100644
index 0000000..c7d43ad
Binary files /dev/null and b/templates/www/res/icon/cordova_24.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_256.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_256.png b/templates/www/res/icon/cordova_256.png
new file mode 100644
index 0000000..e1cd0e6
Binary files /dev/null and b/templates/www/res/icon/cordova_256.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_32.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_32.png b/templates/www/res/icon/cordova_32.png
new file mode 100644
index 0000000..734fffc
Binary files /dev/null and b/templates/www/res/icon/cordova_32.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_48.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_48.png b/templates/www/res/icon/cordova_48.png
new file mode 100644
index 0000000..8ad8bac
Binary files /dev/null and b/templates/www/res/icon/cordova_48.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_512.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_512.png b/templates/www/res/icon/cordova_512.png
new file mode 100644
index 0000000..c9465f3
Binary files /dev/null and b/templates/www/res/icon/cordova_512.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_64.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_64.png b/templates/www/res/icon/cordova_64.png
new file mode 100644
index 0000000..03b3849
Binary files /dev/null and b/templates/www/res/icon/cordova_64.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_android_36.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_android_36.png b/templates/www/res/icon/cordova_android_36.png
new file mode 100644
index 0000000..cd5032a
Binary files /dev/null and b/templates/www/res/icon/cordova_android_36.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_android_48.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_android_48.png b/templates/www/res/icon/cordova_android_48.png
new file mode 100644
index 0000000..e79c606
Binary files /dev/null and b/templates/www/res/icon/cordova_android_48.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_android_72.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_android_72.png b/templates/www/res/icon/cordova_android_72.png
new file mode 100644
index 0000000..4d27634
Binary files /dev/null and b/templates/www/res/icon/cordova_android_72.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_android_96.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_android_96.png b/templates/www/res/icon/cordova_android_96.png
new file mode 100644
index 0000000..ec7ffbf
Binary files /dev/null and b/templates/www/res/icon/cordova_android_96.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_bb_80.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_bb_80.png b/templates/www/res/icon/cordova_bb_80.png
new file mode 100644
index 0000000..f86a27a
Binary files /dev/null and b/templates/www/res/icon/cordova_bb_80.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_ios_114.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_ios_114.png b/templates/www/res/icon/cordova_ios_114.png
new file mode 100644
index 0000000..efd9c37
Binary files /dev/null and b/templates/www/res/icon/cordova_ios_114.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_ios_144.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_ios_144.png b/templates/www/res/icon/cordova_ios_144.png
new file mode 100644
index 0000000..dd819da
Binary files /dev/null and b/templates/www/res/icon/cordova_ios_144.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_ios_57.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_ios_57.png b/templates/www/res/icon/cordova_ios_57.png
new file mode 100644
index 0000000..c795fc4
Binary files /dev/null and b/templates/www/res/icon/cordova_ios_57.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/icon/cordova_ios_72.png
----------------------------------------------------------------------
diff --git a/templates/www/res/icon/cordova_ios_72.png b/templates/www/res/icon/cordova_ios_72.png
new file mode 100644
index 0000000..b1cfde7
Binary files /dev/null and b/templates/www/res/icon/cordova_ios_72.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/android_hdpi_landscape.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/android_hdpi_landscape.png b/templates/www/res/screen/android_hdpi_landscape.png
new file mode 100644
index 0000000..a61e2b1
Binary files /dev/null and b/templates/www/res/screen/android_hdpi_landscape.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/android_hdpi_portrait.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/android_hdpi_portrait.png b/templates/www/res/screen/android_hdpi_portrait.png
new file mode 100644
index 0000000..5d6a28a
Binary files /dev/null and b/templates/www/res/screen/android_hdpi_portrait.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/android_ldpi_landscape.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/android_ldpi_landscape.png b/templates/www/res/screen/android_ldpi_landscape.png
new file mode 100644
index 0000000..f3934cd
Binary files /dev/null and b/templates/www/res/screen/android_ldpi_landscape.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/android_ldpi_portrait.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/android_ldpi_portrait.png b/templates/www/res/screen/android_ldpi_portrait.png
new file mode 100644
index 0000000..65ad163
Binary files /dev/null and b/templates/www/res/screen/android_ldpi_portrait.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/android_mdpi_landscape.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/android_mdpi_landscape.png b/templates/www/res/screen/android_mdpi_landscape.png
new file mode 100644
index 0000000..a1b697c
Binary files /dev/null and b/templates/www/res/screen/android_mdpi_landscape.png differ


[50/50] git commit: Fix the tests.

Posted by br...@apache.org.
Fix the tests.

- Updated to the latest jasmine-node.
- Added a require for ios_parser to platform.spec, probably removed by a
  bad merge.


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

Branch: refs/heads/master2
Commit: f7965d15a2191467d227a9d0902bd82a6a4e5807
Parents: 8dd2c2e
Author: Braden Shepherdson <br...@gmail.com>
Authored: Thu May 23 17:26:40 2013 -0400
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Thu May 23 17:26:40 2013 -0400

----------------------------------------------------------------------
 package.json                      |    2 +-
 spec/cordova-cli/platform.spec.js |    1 +
 2 files changed, 2 insertions(+), 1 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f7965d15/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index 9edf503..295f27c 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
     "open": "0.0.3"
   },
   "devDependencies": {
-    "jasmine-node":"1.1.x"
+    "jasmine-node":"latest"
   },
   "author": "Anis Kadri",
   "contributors": [

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f7965d15/spec/cordova-cli/platform.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/platform.spec.js b/spec/cordova-cli/platform.spec.js
index 698582c..b791bd9 100644
--- a/spec/cordova-cli/platform.spec.js
+++ b/spec/cordova-cli/platform.spec.js
@@ -29,6 +29,7 @@ var cordova = require('../../cordova'),
     platforms = require('../../platforms'),
     tempDir = path.join(__dirname, '..', '..', 'temp');
     android_parser = require('../../src/metadata/android_parser'),
+    ios_parser = require('../../src/metadata/ios_parser'),
     blackberry_parser = require('../../src/metadata/blackberry_parser'),
     cordova_project = path.join(__dirname, '..', 'fixtures', 'projects', 'cordova');
 


[15/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/SpecView.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/SpecView.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/SpecView.js
new file mode 100644
index 0000000..8769bb8
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/SpecView.js
@@ -0,0 +1,79 @@
+jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
+  this.spec = spec;
+  this.dom = dom;
+  this.views = views;
+
+  this.symbol = this.createDom('li', { className: 'pending' });
+  this.dom.symbolSummary.appendChild(this.symbol);
+
+  this.summary = this.createDom('div', { className: 'specSummary' },
+      this.createDom('a', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
+        title: this.spec.getFullName()
+      }, this.spec.description)
+  );
+
+  this.detail = this.createDom('div', { className: 'specDetail' },
+      this.createDom('a', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
+        title: this.spec.getFullName()
+      }, this.spec.getFullName())
+  );
+};
+
+jasmine.HtmlReporter.SpecView.prototype.status = function() {
+  return this.getSpecStatus(this.spec);
+};
+
+jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
+  this.symbol.className = this.status();
+
+  switch (this.status()) {
+    case 'skipped':
+      break;
+
+    case 'passed':
+      this.appendSummaryToSuiteDiv();
+      break;
+
+    case 'failed':
+      this.appendSummaryToSuiteDiv();
+      this.appendFailureDetail();
+      break;
+  }
+};
+
+jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
+  this.summary.className += ' ' + this.status();
+  this.appendToSummary(this.spec, this.summary);
+};
+
+jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
+  this.detail.className += ' ' + this.status();
+
+  var resultItems = this.spec.results().getItems();
+  var messagesDiv = this.createDom('div', { className: 'messages' });
+
+  for (var i = 0; i < resultItems.length; i++) {
+    var result = resultItems[i];
+
+    if (result.type == 'log') {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+    } else if (result.type == 'expect' && result.passed && !result.passed()) {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+      if (result.trace.stack) {
+        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+      }
+    }
+  }
+
+  if (messagesDiv.childNodes.length > 0) {
+    this.detail.appendChild(messagesDiv);
+    this.dom.details.appendChild(this.detail);
+  }
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/SuiteView.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/SuiteView.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/SuiteView.js
new file mode 100644
index 0000000..19a1efa
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/SuiteView.js
@@ -0,0 +1,22 @@
+jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
+  this.suite = suite;
+  this.dom = dom;
+  this.views = views;
+
+  this.element = this.createDom('div', { className: 'suite' },
+      this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
+  );
+
+  this.appendToSummary(this.suite, this.element);
+};
+
+jasmine.HtmlReporter.SuiteView.prototype.status = function() {
+  return this.getSpecStatus(this.suite);
+};
+
+jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
+  this.element.className += " " + this.status();
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/TrivialReporter.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/TrivialReporter.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/TrivialReporter.js
new file mode 100644
index 0000000..167ac50
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/html/TrivialReporter.js
@@ -0,0 +1,192 @@
+/* @deprecated Use jasmine.HtmlReporter instead
+ */
+jasmine.TrivialReporter = function(doc) {
+  this.document = doc || document;
+  this.suiteDivs = {};
+  this.logRunningSpecs = false;
+};
+
+jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
+  var el = document.createElement(type);
+
+  for (var i = 2; i < arguments.length; i++) {
+    var child = arguments[i];
+
+    if (typeof child === 'string') {
+      el.appendChild(document.createTextNode(child));
+    } else {
+      if (child) { el.appendChild(child); }
+    }
+  }
+
+  for (var attr in attrs) {
+    if (attr == "className") {
+      el[attr] = attrs[attr];
+    } else {
+      el.setAttribute(attr, attrs[attr]);
+    }
+  }
+
+  return el;
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
+  var showPassed, showSkipped;
+
+  this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
+      this.createDom('div', { className: 'banner' },
+        this.createDom('div', { className: 'logo' },
+            this.createDom('span', { className: 'title' }, "Jasmine"),
+            this.createDom('span', { className: 'version' }, runner.env.versionString())),
+        this.createDom('div', { className: 'options' },
+            "Show ",
+            showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
+            showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
+            )
+          ),
+
+      this.runnerDiv = this.createDom('div', { className: 'runner running' },
+          this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
+          this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
+          this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
+      );
+
+  this.document.body.appendChild(this.outerDiv);
+
+  var suites = runner.suites();
+  for (var i = 0; i < suites.length; i++) {
+    var suite = suites[i];
+    var suiteDiv = this.createDom('div', { className: 'suite' },
+        this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
+        this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
+    this.suiteDivs[suite.id] = suiteDiv;
+    var parentDiv = this.outerDiv;
+    if (suite.parentSuite) {
+      parentDiv = this.suiteDivs[suite.parentSuite.id];
+    }
+    parentDiv.appendChild(suiteDiv);
+  }
+
+  this.startedAt = new Date();
+
+  var self = this;
+  showPassed.onclick = function(evt) {
+    if (showPassed.checked) {
+      self.outerDiv.className += ' show-passed';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
+    }
+  };
+
+  showSkipped.onclick = function(evt) {
+    if (showSkipped.checked) {
+      self.outerDiv.className += ' show-skipped';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
+    }
+  };
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
+  var results = runner.results();
+  var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
+  this.runnerDiv.setAttribute("class", className);
+  //do it twice for IE
+  this.runnerDiv.setAttribute("className", className);
+  var specs = runner.specs();
+  var specCount = 0;
+  for (var i = 0; i < specs.length; i++) {
+    if (this.specFilter(specs[i])) {
+      specCount++;
+    }
+  }
+  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
+  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
+  this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
+
+  this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
+};
+
+jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
+  var results = suite.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.totalCount === 0) { // todo: change this to check results.skipped
+    status = 'skipped';
+  }
+  this.suiteDivs[suite.id].className += " " + status;
+};
+
+jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
+  if (this.logRunningSpecs) {
+    this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+  }
+};
+
+jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
+  var results = spec.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.skipped) {
+    status = 'skipped';
+  }
+  var specDiv = this.createDom('div', { className: 'spec '  + status },
+      this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
+      this.createDom('a', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(spec.getFullName()),
+        title: spec.getFullName()
+      }, spec.description));
+
+
+  var resultItems = results.getItems();
+  var messagesDiv = this.createDom('div', { className: 'messages' });
+  for (var i = 0; i < resultItems.length; i++) {
+    var result = resultItems[i];
+
+    if (result.type == 'log') {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+    } else if (result.type == 'expect' && result.passed && !result.passed()) {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+      if (result.trace.stack) {
+        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+      }
+    }
+  }
+
+  if (messagesDiv.childNodes.length > 0) {
+    specDiv.appendChild(messagesDiv);
+  }
+
+  this.suiteDivs[spec.suite.id].appendChild(specDiv);
+};
+
+jasmine.TrivialReporter.prototype.log = function() {
+  var console = jasmine.getGlobal().console;
+  if (console && console.log) {
+    if (console.log.apply) {
+      console.log.apply(console, arguments);
+    } else {
+      console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+    }
+  }
+};
+
+jasmine.TrivialReporter.prototype.getLocation = function() {
+  return this.document.location;
+};
+
+jasmine.TrivialReporter.prototype.specFilter = function(spec) {
+  var paramMap = {};
+  var params = this.getLocation().search.substring(1).split('&');
+  for (var i = 0; i < params.length; i++) {
+    var p = params[i].split('=');
+    paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
+  }
+
+  if (!paramMap.spec) {
+    return true;
+  }
+  return spec.getFullName().indexOf(paramMap.spec) === 0;
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/index.html
new file mode 100644
index 0000000..cf1117f
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/index.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
+    <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+
+    <title>Cordova API Specs</title>
+
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" src="../cordova.js"></script>
+  </head>
+  <body id="stage" class="theme">
+    <h1>Cordova API Specs</h1>
+
+    <a href="pages/all.html" class="btn large" style="width:100%;">Run All Tests</a>
+    <a href="pages/accelerometer.html" class="btn large" style="width:100%;">Run Accelerometer Tests</a>
+    <a href="pages/battery.html" class="btn large" style="width:100%;">Run Battery Tests</a>
+    <a href="pages/camera.html" class="btn large" style="width:100%;">Run Camera Tests</a>
+    <a href="pages/capture.html" class="btn large" style="width:100%;">Run Capture Tests</a>
+    <a href="pages/compass.html" class="btn large" style="width:100%;">Run Compass Tests</a>
+    <a href="pages/contacts.html" class="btn large" style="width:100%;">Run Contacts Tests</a>
+    <a href="pages/datauri.html" class="btn large" style="width:100%;">Run Data URI Tests</a>
+    <a href="pages/device.html" class="btn large" style="width:100%;">Run Device Tests</a>
+    <a href="pages/file.html" class="btn large" style="width:100%;">Run File Tests</a>
+    <a href="pages/filetransfer.html" class="btn large" style="width:100%;">Run FileTransfer Tests</a>
+    <a href="pages/geolocation.html" class="btn large" style="width:100%;">Run Geolocation Tests</a>
+    <a href="pages/globalization.html" class="btn large" style="width:100%;">Run Globalization Tests</a>
+    <a href="pages/media.html" class="btn large" style="width:100%;">Run Media Tests</a>
+    <a href="pages/network.html" class="btn large" style="width:100%;">Run Network Tests</a>
+    <a href="pages/notification.html" class="btn large" style="width:100%;">Run Notification Tests</a>
+    <a href="pages/platform.html" class="btn large" style="width:100%;">Run Platform Tests</a>
+    <a href="pages/storage.html" class="btn large" style="width:100%;">Run Storage Tests</a>
+    <a href="pages/bridge.html" class="btn large" style="width:100%;">Run Bridge Tests</a>
+
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/jasmine.css
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/jasmine.css b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/jasmine.css
new file mode 100644
index 0000000..826e575
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/jasmine.css
@@ -0,0 +1,81 @@
+body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
+
+#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
+#HTMLReporter a { text-decoration: none; }
+#HTMLReporter a:hover { text-decoration: underline; }
+#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
+#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
+#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
+#HTMLReporter .version { color: #aaaaaa; }
+#HTMLReporter .banner { margin-top: 14px; }
+#HTMLReporter .duration { color: #aaaaaa; float: right; }
+#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
+#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
+#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
+#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
+#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
+#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
+#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
+#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
+#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
+#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
+#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
+#HTMLReporter .runningAlert { background-color: #666666; }
+#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
+#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
+#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
+#HTMLReporter .passingAlert { background-color: #a6b779; }
+#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
+#HTMLReporter .failingAlert { background-color: #cf867e; }
+#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
+#HTMLReporter .results { margin-top: 14px; }
+#HTMLReporter #details { display: none; }
+#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
+#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
+#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
+#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
+#HTMLReporter.showDetails .summary { display: none; }
+#HTMLReporter.showDetails #details { display: block; }
+#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
+#HTMLReporter .summary { margin-top: 14px; }
+#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
+#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
+#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
+#HTMLReporter .description + .suite { margin-top: 0; }
+#HTMLReporter .suite { margin-top: 14px; }
+#HTMLReporter .suite a { color: #333333; }
+#HTMLReporter #details .specDetail { margin-bottom: 28px; }
+#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
+#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
+#HTMLReporter .resultMessage span.result { display: block; }
+#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
+
+#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
+#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
+#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
+#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
+#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
+#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
+#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
+#TrivialReporter .runner.running { background-color: yellow; }
+#TrivialReporter .options { text-align: right; font-size: .8em; }
+#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
+#TrivialReporter .suite .suite { margin: 5px; }
+#TrivialReporter .suite.passed { background-color: #dfd; }
+#TrivialReporter .suite.failed { background-color: #fdd; }
+#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
+#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
+#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
+#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
+#TrivialReporter .spec.skipped { background-color: #bbb; }
+#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
+#TrivialReporter .passed { background-color: #cfc; display: none; }
+#TrivialReporter .failed { background-color: #fbb; }
+#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
+#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
+#TrivialReporter .resultMessage .mismatch { color: black; }
+#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
+#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
+#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
+#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
+#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }


[12/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/file.tests.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/file.tests.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/file.tests.js
new file mode 100644
index 0000000..06d8ec8
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/tests/file.tests.js
@@ -0,0 +1,3462 @@
+describe('File API', function() {
+    // Adding a Jasmine helper matcher, to report errors when comparing to FileError better.
+    var fileErrorMap = {
+        1: 'NOT_FOUND_ERR',
+        2: 'SECURITY_ERR',
+        3: 'ABORT_ERR',
+        4: 'NOT_READABLE_ERR',
+        5: 'ENCODING_ERR',
+        6: 'NO_MODIFICATION_ALLOWED_ERR',
+        7: 'INVALID_STATE_ERR',
+        8: 'SYNTAX_ERR',
+        9: 'INVALID_MODIFICATION_ERR',
+        10:'QUOTA_EXCEEDED_ERR',
+        11:'TYPE_MISMATCH_ERR',
+        12:'PATH_EXISTS_ERR'
+    };
+    beforeEach(function() {
+        this.addMatchers({
+            toBeFileError: function(code) {
+                var error = this.actual;
+                this.message = function(){
+                    return "Expected FileError with code " + fileErrorMap[error.code] + " (" + error.code + ") to be " + fileErrorMap[code] + "(" + code + ")";
+                };
+                return (error.code == code);
+            },
+            toCanonicallyMatch:function(path){
+                this.message = function(){
+                    return "Expected paths to match : " + path + " should be " + this.actual;
+                };
+
+                var a = path.split("/").join("").split("\\").join("");
+                var b = this.actual.split("/").join("").split("\\").join("");
+
+                return a == b;
+            }
+        });
+    });
+
+    // HELPER FUNCTIONS
+
+    // deletes specified file or directory
+    var deleteEntry = function(name, success, error) {
+        // deletes entry, if it exists
+        window.resolveLocalFileSystemURI(root.toURL() + '/' + name,
+            function(entry) {
+                if (entry.isDirectory === true) {
+                    entry.removeRecursively(success, error);
+                } else {
+                    entry.remove(success, error);
+                }
+            }, success);
+    };
+    // deletes file, if it exists, then invokes callback
+    var deleteFile = function(fileName, callback) {
+        root.getFile(fileName, null,
+                // remove file system entry
+                function(entry) {
+                    entry.remove(callback, function() { console.log('[ERROR] deleteFile cleanup method invoked fail callback.'); });
+                },
+                // doesn't exist
+                callback);
+    };
+    // deletes and re-creates the specified file
+    var createFile = function(fileName, success, error) {
+        deleteEntry(fileName, function() {
+            root.getFile(fileName, {create: true}, success, error);
+        }, error);
+    };
+    // deletes and re-creates the specified directory
+    var createDirectory = function(dirName, success, error) {
+        deleteEntry(dirName, function() {
+           root.getDirectory(dirName, {create: true}, success, error);
+        }, error);
+    };
+
+    var createFail = function(module) {
+        return jasmine.createSpy().andCallFake(function(err) {
+            console.log('[ERROR ' + module + '] ' + JSON.stringify(err));
+        });
+    };
+
+    var createWin = function(module) {
+        return jasmine.createSpy().andCallFake(function() {
+            console.log('[ERROR ' + module + '] Unexpected success callback');
+        });
+    };
+
+    describe('FileError object', function() {
+        it("should define FileError constants", function() {
+            expect(FileError.NOT_FOUND_ERR).toBe(1);
+            expect(FileError.SECURITY_ERR).toBe(2);
+            expect(FileError.ABORT_ERR).toBe(3);
+            expect(FileError.NOT_READABLE_ERR).toBe(4);
+            expect(FileError.ENCODING_ERR).toBe(5);
+            expect(FileError.NO_MODIFICATION_ALLOWED_ERR).toBe(6);
+            expect(FileError.INVALID_STATE_ERR).toBe(7);
+            expect(FileError.SYNTAX_ERR).toBe(8);
+            expect(FileError.INVALID_MODIFICATION_ERR).toBe(9);
+            expect(FileError.QUOTA_EXCEEDED_ERR).toBe(10);
+            expect(FileError.TYPE_MISMATCH_ERR).toBe(11);
+            expect(FileError.PATH_EXISTS_ERR).toBe(12);
+        });
+    });
+
+    describe('LocalFileSystem', function() {
+
+        it("should define LocalFileSystem constants", function() {
+            expect(LocalFileSystem.TEMPORARY).toBe(0);
+            expect(LocalFileSystem.PERSISTENT).toBe(1);
+        });
+
+        describe('window.requestFileSystem', function() {
+            it("should be defined", function() {
+                expect(window.requestFileSystem).toBeDefined();
+            });
+            it("should be able to retrieve a PERSISTENT file system", function() {
+                var win = jasmine.createSpy().andCallFake(function(fileSystem) {
+                    expect(fileSystem).toBeDefined();
+                    expect(fileSystem.name).toBeDefined();
+                    expect(fileSystem.name).toBe("persistent");
+                    expect(fileSystem.root).toBeDefined();
+                }),
+                fail = createFail('window.requestFileSystem');
+
+                // retrieve PERSISTENT file system
+                runs(function() {
+                    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, win, fail);
+                });
+
+                waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).not.toHaveBeenCalled();
+                    expect(win).toHaveBeenCalled();
+                });
+            });
+            it("should be able to retrieve a TEMPORARY file system", function() {
+                var win = jasmine.createSpy().andCallFake(function(fileSystem) {
+                    expect(fileSystem).toBeDefined();
+                    expect(fileSystem.name).toBeDefined();
+                    expect(fileSystem.name).toBe("temporary");
+                    expect(fileSystem.root).toBeDefined();
+                }),
+                fail = createFail('window.requestFileSystem');
+
+                // Request the file system
+                runs(function() {
+                    window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, win, fail);
+                });
+
+                waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).not.toHaveBeenCalled();
+                    expect(win).toHaveBeenCalled();
+                });
+            });
+            it("should error if you request a file system that is too large", function() {
+                var fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.QUOTA_EXCEEDED_ERR);
+                }),
+                win = createWin('window.requestFileSystem');
+
+                // Request the file system
+                runs(function() {
+                    window.requestFileSystem(LocalFileSystem.TEMPORARY, 1000000000000000, win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(win).not.toHaveBeenCalled();
+                    expect(fail).toHaveBeenCalled();
+                });
+            });
+            it("should error out if you request a file system that does not exist", function() {
+                var fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.SYNTAX_ERR);
+                }),
+                win = createWin('window.requestFileSystem');
+
+                // Request the file system
+                runs(function() {
+                    window.requestFileSystem(-1, 0, win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(win).not.toHaveBeenCalled();
+                    expect(fail).toHaveBeenCalled();
+                });
+            });
+        });
+
+        describe('window.resolveLocalFileSystemURI', function() {
+            it("should be defined", function() {
+                expect(window.resolveLocalFileSystemURI).toBeDefined();
+            });
+            it("should resolve a valid file name", function() {
+                var fileName = "resolve.file.uri",
+                win = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.name).toCanonicallyMatch(fileName);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                }),
+                fail = createFail('window.resolveLocalFileSystemURI');
+                resolveCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // lookup file system entry
+                    runs(function() {
+                        window.resolveLocalFileSystemURI(entry.toURL(), win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "resolveLocalFileSystemURI callback never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                });
+
+                // create a new file entry
+                runs(function() {
+                    createFile(fileName, resolveCallback, fail);
+                });
+
+                waitsFor(function() { return resolveCallback.wasCalled; }, "createFile callback never called", Tests.TEST_TIMEOUT);
+            });
+            it("resolve valid file name with parameters", function() {
+                var fileName = "resolve.file.uri.params",
+                win = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.name).toBe(fileName);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                }),
+                fail = createFail('window.resolveLocalFileSystemURI');
+                resolveCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // lookup file system entry
+                    runs(function() {
+                        window.resolveLocalFileSystemURI(entry.toURL() + "?1234567890", win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "resolveLocalFileSystemURI callback never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                });
+
+                // create a new file entry
+                runs(function() {
+                    createFile(fileName, resolveCallback, fail);
+                });
+
+                waitsFor(function() { return resolveCallback.wasCalled; }, "createFile callback never called", Tests.TEST_TIMEOUT);
+            });
+            it("should error (NOT_FOUND_ERR) when resolving (non-existent) invalid file name", function() {
+                var fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                win = createWin('window.resolveLocalFileSystemURI');
+
+                // lookup file system entry
+                runs(function() {
+                    window.resolveLocalFileSystemURI("file:///this.is.not.a.valid.file.txt", win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).toHaveBeenCalled();
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
+            it("should error (ENCODING_ERR) when resolving invalid URI with leading /", function() {
+                var fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.ENCODING_ERR);
+                }),
+                win = createWin('window.resolveLocalFileSystemURI');
+
+                // lookup file system entry
+                runs(function() {
+                    window.resolveLocalFileSystemURI("/this.is.not.a.valid.url", win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).toHaveBeenCalled();
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
+        });
+    });
+
+    describe('Metadata interface', function() {
+        it("should exist and have the right properties", function() {
+            var metadata = new Metadata();
+            expect(metadata).toBeDefined();
+            expect(metadata.modificationTime).toBeDefined();
+        });
+    });
+
+    describe('Flags interface', function() {
+        it("should exist and have the right properties", function() {
+            var flags = new Flags(false, true);
+            expect(flags).toBeDefined();
+            expect(flags.create).toBeDefined();
+            expect(flags.create).toBe(false);
+            expect(flags.exclusive).toBeDefined();
+            expect(flags.exclusive).toBe(true);
+        });
+    });
+
+    describe('FileSystem interface', function() {
+        it("should have a root that is a DirectoryEntry", function() {
+            var win = jasmine.createSpy().andCallFake(function(entry) {
+                expect(entry).toBeDefined();
+                expect(entry.isFile).toBe(false);
+                expect(entry.isDirectory).toBe(true);
+                expect(entry.name).toBeDefined();
+                expect(entry.fullPath).toBeDefined();
+                expect(entry.getMetadata).toBeDefined();
+                expect(entry.moveTo).toBeDefined();
+                expect(entry.copyTo).toBeDefined();
+                expect(entry.toURL).toBeDefined();
+                expect(entry.remove).toBeDefined();
+                expect(entry.getParent).toBeDefined();
+                expect(entry.createReader).toBeDefined();
+                expect(entry.getFile).toBeDefined();
+                expect(entry.getDirectory).toBeDefined();
+                expect(entry.removeRecursively).toBeDefined();
+            }),
+            fail = createFail('FileSystem');
+
+            runs(function() {
+                window.resolveLocalFileSystemURI(root.toURL(), win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).not.toHaveBeenCalled();
+                expect(win).toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('DirectoryEntry', function() {
+        it("getFile: get Entry for file that does not exist", function() {
+            var fileName = "de.no.file",
+                filePath = root.fullPath + '/' + fileName,
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, file does not exist
+            runs(function() {
+                root.getFile(fileName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+        it("etFile: create new file", function() {
+            var fileName = "de.create.file",
+                filePath = root.fullPath + '/' + fileName,
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toBe(filePath);
+                    // cleanup
+                    entry.remove(null, null);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, file does not exist
+            runs(function() {
+                root.getFile(fileName, {create: true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("getFile: create new file (exclusive)", function() {
+            var fileName = "de.create.exclusive.file",
+                filePath = root.fullPath + '/' + fileName,
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toBe(fileName);
+                    expect(entry.fullPath).toBe(filePath);
+
+                    // cleanup
+                    entry.remove(null, null);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:true, file does not exist
+            runs(function() {
+                root.getFile(fileName, {create: true, exclusive:true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("getFile: create file that already exists", function() {
+            var fileName = "de.create.existing.file",
+                filePath = root.fullPath + '/' + fileName,
+                getFile = jasmine.createSpy().andCallFake(function(file) {
+                    // create:true, exclusive:false, file exists
+                    runs(function() {
+                        root.getFile(fileName, {create:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win was never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('DirectoryEntry'),
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toBe(filePath);
+
+                    // cleanup
+                    entry.remove(null, fail);
+                });
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "getFile was never called", Tests.TEST_TIMEOUT);
+        });
+        it("getFile: create file that already exists (exclusive)", function() {
+            var fileName = "de.create.exclusive.existing.file",
+                filePath = root.fullPath + '/' + fileName,
+                existingFile,
+                getFile = jasmine.createSpy().andCallFake(function(file) {
+                    existingFile = file;
+                    // create:true, exclusive:true, file exists
+                    runs(function() {
+                        root.getFile(fileName, {create:true, exclusive:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.PATH_EXISTS_ERR);
+
+                    // cleanup
+                    existingFile.remove(null, fail);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "getFile never called", Tests.TEST_TIMEOUT);
+        });
+        it("getFile: get Entry for existing file", function() {
+            var fileName = "de.get.file",
+                filePath = root.fullPath + '/' + fileName,
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toCanonicallyMatch(filePath);
+
+                    entry.remove(null, fail); //clean up
+                }),
+                fail = createFail('DirectoryEntry'),
+                getFile = jasmine.createSpy().andCallFake(function(file) {
+                    // create:false, exclusive:false, file exists
+                    runs(function() {
+                        root.getFile(fileName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "getFile success callback", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                });
+
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "file creation", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.getFile: get FileEntry for invalid path", function() {
+            var fileName = "de:invalid:path",
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.ENCODING_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, invalid path
+            runs(function() {
+                root.getFile(fileName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+
+        });
+        it("DirectoryEntry.getDirectory: get Entry for directory that does not exist", function() {
+            var dirName = "de.no.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+        it("DirectoryEntry.getDirectory: create new dir with space then resolveFileSystemURI", function() {
+            var dirName = "de create dir",
+                dirPath = root.fullPath + '/' + dirName,
+                getDir = jasmine.createSpy().andCallFake(function(dirEntry) {
+                    var dirURI = dirEntry.toURL();
+                    // now encode URI and try to resolve
+                    runs(function() {
+                        window.resolveLocalFileSystemURI(dirURI, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+
+                }), win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create: true}, getDir, fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.getDirectory: create new dir with space resolveFileSystemURI with encoded URI", function() {
+            var dirName = "de create dir",
+                dirPath = root.fullPath + '/' + dirName,
+                getDir = jasmine.createSpy().andCallFake(function(dirEntry) {
+                    var dirURI = dirEntry.toURL();
+                    // now encode URI and try to resolve
+                    runs(function() {
+                        window.resolveLocalFileSystemURI(encodeURI(dirURI), win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create: true}, getDir, fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+
+        it("DirectoryEntry.getDirectory: create new directory", function() {
+            var dirName = "de.create.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create: true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("DirectoryEntry.getDirectory: create new directory (exclusive)", function() {
+            var dirName = "de.create.exclusive.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+            // create:true, exclusive:true, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create: true, exclusive:true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("DirectoryEntry.getDirectory: create directory that already exists", function() {
+            var dirName = "de.create.existing.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                getDir = jasmine.createSpy().andCallFake(function(directory) {
+                    // create:true, exclusive:false, directory exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create directory to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, getDir, this.fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.getDirectory: create directory that already exists (exclusive)", function() {
+            var dirName = "de.create.exclusive.existing.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                existingDir,
+                getDir = jasmine.createSpy().andCallFake(function(directory) {
+                    existingDir = directory;
+                    // create:true, exclusive:true, directory exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:true, exclusive:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.PATH_EXISTS_ERR);
+
+                    // cleanup
+                    existingDir.remove(null, fail);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create directory to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, getDir, fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.getDirectory: get Entry for existing directory", function() {
+            var dirName = "de.get.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                getDir = jasmine.createSpy().andCallFake(function(directory) {
+                    // create:false, exclusive:false, directory exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create directory to kick off it
+            root.getDirectory(dirName, {create:true}, getDir, fail);
+        });
+        it("DirectoryEntry.getDirectory: get DirectoryEntry for invalid path", function() {
+            var dirName = "de:invalid:path",
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.ENCODING_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, invalid path
+            runs(function() {
+                root.getDirectory(dirName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+        it("DirectoryEntry.getDirectory: get DirectoryEntry for existing file", function() {
+            var fileName = "de.existing.file",
+                existingFile,
+                filePath = root.fullPath + '/' + fileName,
+                getDir = jasmine.createSpy().andCallFake(function(file) {
+                    existingFile = file;
+                    // create:false, exclusive:false, existing file
+                    runs(function() {
+                        root.getDirectory(fileName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR);
+
+                    // cleanup
+                    existingFile.remove(null, null);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getDir, fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir was called", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.getFile: get FileEntry for existing directory", function() {
+            var dirName = "de.existing.dir",
+                existingDir,
+                dirPath = root.fullPath + '/' + dirName,
+                getFile = jasmine.createSpy().andCallFake(function(directory) {
+                    existingDir = directory;
+                    // create:false, exclusive:false, existing directory
+                    runs(function() {
+                        root.getFile(dirName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR);
+
+                    // cleanup
+                    existingDir.remove(null, null);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create directory to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "getFile never called", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.removeRecursively on directory", function() {
+            var dirName = "de.removeRecursively",
+                subDirName = "dir",
+                dirPath = root.fullPath + '/' + dirName,
+                //subDirPath = this.root.fullPath + '/' + subDirName,
+                subDirPath = dirPath + '/' + subDirName,
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // delete directory
+                    var deleteDirectory = jasmine.createSpy().andCallFake(function(directory) {
+                        runs(function() {
+                            entry.removeRecursively(remove, fail);
+                        });
+
+                        waitsFor(function() { return remove.wasCalled; }, "remove never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a sub-directory within directory
+                    runs(function() {
+                        entry.getDirectory(subDirName, {create: true}, deleteDirectory, fail);
+                    });
+
+                    waitsFor(function() { return deleteDirectory.wasCalled; }, "deleteDirectory never called", Tests.TEST_TIMEOUT);
+                }),
+                remove = jasmine.createSpy().andCallFake(function() {
+                    // it that removed directory no longer exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:false}, win, dirExists);
+                    });
+
+                    waitsFor(function() { return dirExists.wasCalled; }, "dirExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(dirExists).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                dirExists = jasmine.createSpy().andCallFake(function(error){
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                fail = createFail('DirectoryEntry'),
+                win = createWin('DirectoryEntry');
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("createReader: create reader on existing directory", function() {
+            // create reader for root directory
+            var reader = root.createReader();
+            expect(reader).toBeDefined();
+            expect(typeof reader.readEntries).toBe('function');
+        });
+        it("removeRecursively on root file system", function() {
+            var remove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NO_MODIFICATION_ALLOWED_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // remove root file system
+            runs(function() {
+                root.removeRecursively(win, remove);
+            });
+
+            waitsFor(function() { return remove.wasCalled; }, "remove never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).not.toHaveBeenCalled();
+                expect(remove).toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('DirectoryReader interface', function() {
+        describe("readEntries", function() {
+            it("should read contents of existing directory", function() {
+                var reader,
+                    win = jasmine.createSpy().andCallFake(function(entries) {
+                        expect(entries).toBeDefined();
+                        expect(entries instanceof Array).toBe(true);
+                    }),
+                    fail = createFail('DirectoryReader');
+
+                // create reader for root directory
+                reader = root.createReader();
+                // read entries
+                runs(function() {
+                    reader.readEntries(win, fail);
+                });
+
+                waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(win).toHaveBeenCalled();
+                    expect(fail).not.toHaveBeenCalled();
+                });
+            });
+            it("should read contents of directory that has been removed", function() {
+                var dirName = "de.createReader.notfound",
+                    dirPath = root.fullPath + '/' + dirName,
+                    entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                        // read entries
+                        var readEntries = jasmine.createSpy().andCallFake(function() {
+                            var reader = directory.createReader();
+
+                            runs(function() {
+                                reader.readEntries(win, itReader);
+                            });
+
+                            waitsFor(function() { return itReader.wasCalled; }, "itReader never called", Tests.TEST_TIMEOUT);
+                        });
+                        // delete directory
+                        runs(function() {
+                            directory.removeRecursively(readEntries, fail);
+                        });
+
+                        waitsFor(function() { return readEntries.wasCalled; }, "readEntries never called", Tests.TEST_TIMEOUT);
+                    }),
+                    itReader = jasmine.createSpy().andCallFake(function(error) {
+                        var itDirectoryExists = jasmine.createSpy().andCallFake(function(error) {
+                            expect(error).toBeDefined();
+                            expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                        });
+
+                        expect(error).toBeDefined();
+                        expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+
+                        runs(function() {
+                            root.getDirectory(dirName, {create:false}, win, itDirectoryExists);
+                        });
+
+                        waitsFor(function() { return itDirectoryExists.wasCalled; }, "itDirectoryExists never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(itDirectoryExists).toHaveBeenCalled();
+                            expect(win).not.toHaveBeenCalled();
+                        });
+                    }),
+                    fail = createFail('DirectoryReader'),
+                    win = createWin('DirectoryReader');
+
+                // create a new directory entry to kick off it
+                runs(function() {
+                    root.getDirectory(dirName, {create:true}, entryCallback, fail);
+                });
+
+                waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+            });
+        });
+    });
+
+    describe('File', function() {
+        it("constructor should be defined", function() {
+            expect(File).toBeDefined();
+            expect(typeof File).toBe('function');
+        });
+        it("should be define File attributes", function() {
+            var file = new File();
+            expect(file.name).toBeDefined();
+            expect(file.fullPath).toBeDefined();
+            expect(file.type).toBeDefined();
+            expect(file.lastModifiedDate).toBeDefined();
+            expect(file.size).toBeDefined();
+        });
+    });
+
+    describe('FileEntry', function() {
+        it("should be define FileEntry methods", function() {
+            var fileName = "fe.methods",
+                itFileEntry = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(typeof fileEntry.createWriter).toBe('function');
+                    expect(typeof fileEntry.file).toBe('function');
+
+                    // cleanup
+                    fileEntry.remove(null, fail);
+                }),
+                fail = createFail('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, itFileEntry, fail);
+            });
+
+            waitsFor(function() { return itFileEntry.wasCalled; }, "itFileEntry never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itFileEntry).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("createWriter should return a FileWriter object", function() {
+            var fileName = "fe.createWriter",
+                itFile,
+                entryCallback = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    itFile = fileEntry;
+
+                    runs(function() {
+                        fileEntry.createWriter(itWriter, fail);
+                    });
+
+                    waitsFor(function() { return itWriter.wasCalled; }, "itWriter", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itWriter).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itWriter = jasmine.createSpy().andCallFake(function(writer) {
+                    expect(writer).toBeDefined();
+                    expect(writer instanceof FileWriter).toBe(true);
+
+                    // cleanup
+                    itFile.remove(null, fail);
+                }),
+                fail = createFail('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file should return a File object", function() {
+            var fileName = "fe.file",
+                newFile,
+                entryCallback = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    newFile = fileEntry;
+
+                    runs(function() {
+                        fileEntry.file(itFile, fail);
+                    });
+
+                    waitsFor(function() { return itFile.wasCalled; }, "itFile never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itFile).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itFile = jasmine.createSpy().andCallFake(function(file) {
+                    expect(file).toBeDefined();
+                    expect(file instanceof File).toBe(true);
+
+                    // cleanup
+                    newFile.remove(null, fail);
+                }),
+                fail = createFail('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file: on File that has been removed", function() {
+            var fileName = "fe.no.file",
+                entryCallback = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    // create File object
+                    var getFile = jasmine.createSpy().andCallFake(function() {
+                        runs(function() {
+                            fileEntry.file(win, itFile);
+                        });
+
+                        waitsFor(function() { return itFile.wasCalled; }, "itFile never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(itFile).toHaveBeenCalled();
+                            expect(win).not.toHaveBeenCalled();
+                        });
+                    });
+                    // delete file
+                    runs(function() {
+                        fileEntry.remove(getFile, fail);
+                    });
+
+                    waitsFor(function() { return getFile.wasCalled; }, "getFile never called", Tests.TEST_TIMEOUT);
+                }),
+                itFile = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                fail = createFail('FileEntry'),
+                win = createWin('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+    });
+    describe('Entry', function() {
+        it("Entry object", function() {
+            var fileName = "entry",
+                fullPath = root.fullPath + '/' + fileName,
+                fail = createFail('Entry'),
+                itEntry = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toCanonicallyMatch(fullPath);
+                    expect(typeof entry.getMetadata).toBe('function');
+                    expect(typeof entry.setMetadata).toBe('function');
+                    expect(typeof entry.moveTo).toBe('function');
+                    expect(typeof entry.copyTo).toBe('function');
+                    expect(typeof entry.toURL).toBe('function');
+                    expect(typeof entry.remove).toBe('function');
+                    expect(typeof entry.getParent).toBe('function');
+                    expect(typeof entry.createWriter).toBe('function');
+                    expect(typeof entry.file).toBe('function');
+
+                    // cleanup
+                    deleteEntry(fileName);
+                });
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, itEntry, fail);
+            });
+
+            waitsFor(function() { return itEntry.wasCalled; }, "itEntry", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itEntry).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("Entry.getMetadata on file", function() {
+            var fileName = "entry.metadata.file",
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getMetadata(itMetadata, fail);
+                    });
+
+                    waitsFor(function() { return itMetadata.wasCalled; }, "itMetadata never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itMetadata).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('Entry'),
+                itMetadata = jasmine.createSpy().andCallFake(function(metadata) {
+                    expect(metadata).toBeDefined();
+                    expect(metadata.modificationTime instanceof Date).toBe(true);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                });
+
+            // create a new file entry
+            createFile(fileName, entryCallback, fail);
+        });
+        it("Entry.getMetadata on directory", function() {
+            var dirName = "entry.metadata.dir",
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getMetadata(itMetadata, fail);
+                    });
+
+                    waitsFor(function() { return itMetadata.wasCalled; }, "itMetadata never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itMetadata).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('Entry'),
+                itMetadata = jasmine.createSpy().andCallFake(function(metadata) {
+                    expect(metadata).toBeDefined();
+                    expect(metadata.modificationTime instanceof Date).toBe(true);
+
+                    // cleanup
+                    deleteEntry(dirName);
+                });
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("Entry.getParent on file in root file system", function() {
+            var fileName = "entry.parent.file",
+                rootPath = root.fullPath,
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getParent(itParent, fail);
+                    });
+
+                    waitsFor(function() { return itParent.wasCalled; }, "itCalled never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itParent).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itParent = jasmine.createSpy().andCallFake(function(parent) {
+                    expect(parent).toBeDefined();
+                    expect(parent.fullPath).toCanonicallyMatch(rootPath);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                });
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("Entry.getParent on directory in root file system", function() {
+            var dirName = "entry.parent.dir",
+                rootPath = root.fullPath,
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getParent(itParent, fail);
+                    });
+
+                    waitsFor(function() { return itParent.wasCalled; }, "itParent never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itParent).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itParent = jasmine.createSpy().andCallFake(function(parent) {
+                    expect(parent).toBeDefined();
+                    expect(parent.fullPath).toCanonicallyMatch(rootPath);
+
+                    // cleanup
+                    deleteEntry(dirName);
+                });
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("Entry.getParent on root file system", function() {
+            var rootPath = root.fullPath,
+                itParent = jasmine.createSpy().andCallFake(function(parent) {
+                    expect(parent).toBeDefined();
+                    expect(parent.fullPath).toCanonicallyMatch(rootPath);
+                }),
+                fail = createFail('Entry');
+
+            // create a new directory entry
+            runs(function() {
+                root.getParent(itParent, fail);
+            });
+
+            waitsFor(function() { return itParent.wasCalled; }, "itParent never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itParent).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("Entry.toURL on file", function() {
+            var fileName = "entry.uri.file",
+                rootPath = root.fullPath,
+                itURI = jasmine.createSpy().andCallFake(function(entry) {
+                    var uri = entry.toURL();
+                    expect(uri).toBeDefined();
+                    expect(uri.indexOf(rootPath)).not.toBe(-1);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                }),
+                fail = createFail('Entry');
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, itURI, fail);
+            });
+
+            waitsFor(function() { return itURI.wasCalled; }, "itURI never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itURI).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("Entry.toURL on directory", function() {
+            var dirName = "entry.uri.dir",
+                rootPath = root.fullPath,
+                itURI = jasmine.createSpy().andCallFake(function(entry) {
+                    var uri = entry.toURL();
+                    expect(uri).toBeDefined();
+                    expect(uri.indexOf(rootPath)).not.toBe(-1);
+
+                    // cleanup
+                    deleteEntry(dirName);
+                }),
+                fail = createFail('Entry');
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, itURI, fail);
+            });
+
+            waitsFor(function() { return itURI.wasCalled; }, "itURI never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itURI).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("Entry.remove on file", function() {
+            var fileName = "entry.rm.file",
+                fullPath = root.fullPath + '/' + fileName,
+                win = createWin('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    var checkRemove = jasmine.createSpy().andCallFake(function() {
+                        runs(function() {
+                            root.getFile(fileName, null, win, itRemove);
+                        });
+
+                        waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(win).not.toHaveBeenCalled();
+                            expect(fail).not.toHaveBeenCalled();
+                            expect(itRemove).toHaveBeenCalled();
+                        });
+                    });
+                    expect(entry).toBeDefined();
+
+                    runs(function() {
+                        entry.remove(checkRemove, fail);
+                    });
+
+                    waitsFor(function() { return checkRemove.wasCalled; }, "checkRemove never called", Tests.TEST_TIMEOUT);
+                }),
+                itRemove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                    // cleanup
+                    deleteEntry(fileName);
+                }),
+                fail = createFail('Entry');
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("remove on empty directory", function() {
+            var dirName = "entry.rm.dir",
+                fullPath = root.fullPath + '/' + dirName,
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    var checkRemove = jasmine.createSpy().andCallFake(function() {
+                        runs(function() {
+                            root.getDirectory(dirName, null, win, itRemove);
+                        });
+
+                        waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(itRemove).toHaveBeenCalled();
+                            expect(win).not.toHaveBeenCalled();
+                            expect(fail).not.toHaveBeenCalled();
+                        });
+                    });
+
+                    expect(entry).toBeDefined();
+
+                    runs(function() {
+                        entry.remove(checkRemove, fail);
+                    });
+
+                    waitsFor(function() { return checkRemove.wasCalled; }, "checkRemove never called", Tests.TEST_TIMEOUT);
+                }),
+                itRemove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                    // cleanup
+                    deleteEntry(dirName);
+                }),
+                win = createWin('Entry'),
+                fail = createFail('Entry');
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("remove on non-empty directory", function() {
+            var dirName = "entry.rm.dir.not.empty",
+                fullPath = root.fullPath + '/' + dirName,
+                fileName = "remove.txt",
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    var checkFile = jasmine.createSpy().andCallFake(function(error) {
+                        expect(error).toBeDefined();
+                        expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+                        // verify that dir still exists
+                        runs(function() {
+                            root.getDirectory(dirName, null, itRemove, fail);
+                        });
+
+                        waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(win).not.toHaveBeenCalled();
+                            expect(fail).not.toHaveBeenCalled();
+                            expect(itRemove).toHaveBeenCalled();
+                        });
+                    });
+                    // delete directory
+                    var deleteDirectory = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        runs(function() {
+                            entry.remove(win, checkFile);
+                        });
+
+                        waitsFor(function() { return checkFile.wasCalled; }, "checkFile never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a file within directory, then try to delete directory
+                    runs(function() {
+                        entry.getFile(fileName, {create: true}, deleteDirectory, fail);
+                    });
+
+                    waitsFor(function() { return deleteDirectory.wasCalled; }, "deleteDirectory never called", Tests.TEST_TIMEOUT);
+                }),
+                itRemove = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.fullPath).toCanonicallyMatch(fullPath);
+                    // cleanup
+                    deleteEntry(dirName);
+                }),
+                win = createWin('Entry'),
+                fail = createFail('Entry');
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("remove on root file system", function() {
+            var itRemove = jasmine.createSpy().andCallFake(function(error) {
+                expect(error).toBeDefined();
+                expect(error).toBeFileError(FileError.NO_MODIFICATION_ALLOWED_ERR);
+            }),
+            win = createWin('Entry');
+
+            // remove entry that doesn't exist
+            runs(function() {
+                root.remove(win, itRemove);
+            });
+
+            waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).not.toHaveBeenCalled();
+                expect(itRemove).toHaveBeenCalled();
+            });
+        });
+        it("copyTo: file", function() {
+            var file1 = "entry.copy.file1",
+                file2 = "entry.copy.file2",
+                fullPath = root.fullPath + '/' + file2,
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // copy file1 to file2
+                    runs(function() {
+                        entry.copyTo(root, file2, itCopy, fail);
+                    });
+
+                    waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.fullPath).toCanonicallyMatch(fullPath);
+                    expect(entry.name).toCanonicallyMatch(file2);
+
+                    runs(function() {
+                        root.getFile(file2, {create:false}, itFileExists, fail);
+                    });
+
+                    waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(itFileExists).toHaveBeenCalled();
+                    });
+                }),
+                itFileExists = jasmine.createSpy().andCallFake(function(entry2) {
+                    // a bit redundant since copy returned this entry already
+                    expect(entry2).toBeDefined();
+                    expect(entry2.isFile).toBe(true);
+                    expect(entry2.isDirectory).toBe(false);
+                    expect(entry2.fullPath).toCanonicallyMatch(fullPath);
+                    expect(entry2.name).toCanonicallyMatch(file2);
+
+                    // cleanup
+                    deleteEntry(file1);
+                    deleteEntry(file2);
+                });
+
+            // create a new file entry to kick off it
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: file onto itself", function() {
+            var file1 = "entry.copy.fos.file1",
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // copy file1 onto itself
+                    runs(function() {
+                        entry.copyTo(root, null, win, itCopy);
+                    });
+
+                    waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itCopy).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('Entry'),
+                win = createWin('Entry'),
+                itCopy = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+
+                    // cleanup
+                    deleteEntry(file1);
+                });
+
+            // create a new file entry to kick off it
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: directory", function() {
+            var file1 = "file1",
+                srcDir = "entry.copy.srcDir",
+                dstDir = "entry.copy.dstDir",
+                dstPath = root.fullPath + '/' + dstDir,
+                filePath = dstPath + '/' + file1,
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    var copyDir = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        // copy srcDir to dstDir
+                        runs(function() {
+                            directory.copyTo(root, dstDir, itCopy, fail);
+                        });
+
+                        waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+                    });
+
+                    // create a file within new directory
+                    runs(function() {
+                        directory.getFile(file1, {create: true}, copyDir, fail);
+                    });
+
+                    waitsFor(function() { return copyDir.wasCalled; }, "copyDir never called", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.fullPath).toCanonicallyMatch(dstPath);
+                    expect(directory.name).toCanonicallyMatch(dstDir);
+
+                    runs(function() {
+                        root.getDirectory(dstDir, {create:false}, itDirExists, fail);
+                    });
+
+                    waitsFor(function() { return itDirExists.wasCalled; }, "itDirExists never called", Tests.TEST_TIMEOUT);
+                }),
+                itDirExists = jasmine.createSpy().andCallFake(function(dirEntry) {
+                     expect(dirEntry).toBeDefined();
+                     expect(dirEntry.isFile).toBe(false);
+                     expect(dirEntry.isDirectory).toBe(true);
+                     expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+                     expect(dirEntry.name).toCanonicallyMatch(dstDir);
+
+                     runs(function() {
+                         dirEntry.getFile(file1, {create:false}, itFileExists, fail);
+                     });
+
+                     waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+                     runs(function() {
+                         expect(itFileExists).toHaveBeenCalled();
+                         expect(fail).not.toHaveBeenCalled();
+                     });
+                }),
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.isFile).toBe(true);
+                    expect(fileEntry.isDirectory).toBe(false);
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+                    expect(fileEntry.name).toCanonicallyMatch(file1);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                    deleteEntry(dstDir);
+                }),
+                fail = createFail('Entry');
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: directory to backup at same root directory", function() {
+            var file1 = "file1",
+                srcDir = "entry.copy.srcDirSame",
+                dstDir = "entry.copy.srcDirSame-backup",
+                dstPath = root.fullPath + '/' + dstDir,
+                filePath = dstPath + '/' + file1,
+                fail = createFail('Entry copyTo: directory to backup at same root'),
+                entryCallback = function(directory) {
+                    var copyDir = function(fileEntry) {
+                        // copy srcDir to dstDir
+                        directory.copyTo(root, dstDir, itCopy, fail);
+                    };
+                    // create a file within new directory
+                    directory.getFile(file1, {create: true}, copyDir, fail);
+                },
+                itCopy = function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.fullPath).toCanonicallyMatch(dstPath);
+                    expect(directory.name).toCanonicallyMatch(dstDir);
+
+                    root.getDirectory(dstDir, {create:false}, itDirExists, fail);
+                },
+                itDirExists = function(dirEntry) {
+                     expect(dirEntry).toBeDefined();
+                     expect(dirEntry.isFile).toBe(false);
+                     expect(dirEntry.isDirectory).toBe(true);
+                     expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+                     expect(dirEntry.name).toCanonicallyMatch(dstDir);
+
+                     dirEntry.getFile(file1, {create:false}, itFileExists, fail);
+                },
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    var cleanSrc = jasmine.createSpy();
+                    var cleanDst = jasmine.createSpy();
+                    runs(function() {
+                        expect(fileEntry).toBeDefined();
+                        expect(fileEntry.isFile).toBe(true);
+                        expect(fileEntry.isDirectory).toBe(false);
+                        expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+                        expect(fileEntry.name).toCanonicallyMatch(file1);
+                        expect(fail).not.toHaveBeenCalled();
+
+                        // cleanup
+                        deleteEntry(srcDir, cleanSrc);
+                        deleteEntry(dstDir, cleanDst);
+                    });
+
+                    waitsFor(function() { return cleanSrc.wasCalled && cleanDst.wasCalled; }, "cleanSrc and cleanDst cleanup methods", Tests.TEST_TIMEOUT);
+                });
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists", 10000);
+        });
+        it("copyTo: directory onto itself", function() {
+            var file1 = "file1",
+                srcDir = "entry.copy.dos.srcDir",
+                srcPath = root.fullPath + '/' + srcDir,
+                filePath = srcPath + '/' + file1,
+                win = createWin('Entry'),
+                fail = createFail('Entry copyTo: directory onto itself'),
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    var copyDir = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        // copy srcDir onto itself
+                        runs(function() {
+                            directory.copyTo(root, null, win, itCopy);
+                        });
+
+                        waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a file within new directory
+                    runs(function() {
+                        directory.getFile(file1, {create: true}, copyDir, fail);
+                    });
+
+                    waitsFor(function() { return copyDir.wasCalled; }, "copyDir never called", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+
+                    runs(function() {
+                        root.getDirectory(srcDir, {create:false}, itDirectoryExists, fail);
+                    });
+
+                    waitsFor(function() { return itDirectoryExists.wasCalled; }, "itDirectoryExists", Tests.TEST_TIMEOUT);
+                }),
+                itDirectoryExists = jasmine.createSpy().andCallFake(function(dirEntry) {
+                    // returning confirms existence so just check fullPath entry
+                    expect(dirEntry).toBeDefined();
+                    expect(dirEntry.fullPath).toCanonicallyMatch(srcPath);
+
+                    runs(function() {
+                        dirEntry.getFile(file1, {create:false}, itFileExists, fail);
+                    });
+
+                    waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(itFileExists).toHaveBeenCalled();
+                    });
+                }),
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                });
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: directory into itself", function() {
+            var srcDir = "entry.copy.dis.srcDir",
+                dstDir = "entry.copy.dis.dstDir",
+                fail = createFail('Entry'),
+                win = createWin('Entry'),
+                srcPath = root.fullPath + '/' + srcDir,
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    // copy source directory into itself
+                    runs(function() {
+                        directory.copyTo(directory, dstDir, win, itCopy);
+                    });
+
+                    waitsFor(function() { return itCopy.wasCalled; }, "itCopy", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+
+                    runs(function() {
+                        root.getDirectory(srcDir, {create:false}, itDirectoryExists, fail);
+                    });
+
+                    waitsFor(function() { return itDirectoryExists.wasCalled; }, "itDirectoryExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itDirectoryExists).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itDirectoryExists = jasmine.createSpy().andCallFake(function(dirEntry) {
+                    // returning confirms existence so just check fullPath entry
+                    expect(dirEntry).toBeDefined();
+                    expect(dirEntry.fullPath).toCanonicallyMatch(srcPath);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                });
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: directory that does not exist", function() {
+            var file1 = "entry.copy.dnf.file1",
+                dstDir = "entry.copy.dnf.dstDir",
+                filePath = root.fullPath + '/' + file1,
+                dstPath = root.fullPath + '/' + dstDir,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // copy file to target directory that does not exist
+                    runs(function() {
+                        directory = new DirectoryEntry();
+                        directory.fullPath = dstPath;
+                        entry.copyTo(directory, null, win, itCopy);
+                    });
+
+                    waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                    runs(function() {
+                        root.getFile(file1, {create: false}, itFileExists, fail);
+                    });
+
+                    waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itFileExists).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+
+                    // cleanup
+                    deleteEntry(file1);
+                });
+
+            // create a new file entry to kick off it
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: invalid target name", function() {
+            var file1 = "entry.copy.itn.file1",
+                file2 = "bad:file:name",
+                filePath = root.fullPath + '/' + file1,
+                fail = createFail('Entry'),
+                win = createWin('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // copy file1 to file2
+                    runs(function() {
+                        entry.copyTo(root, file2, win, itCopy);
+                    });
+
+                    waitsFor(function() { return itCopy.wasCalled; }, "itCopy 

<TRUNCATED>

[38/50] Add Windows support to Android platform-scripts.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators b/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators
new file mode 100644
index 0000000..7911763
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators
@@ -0,0 +1,23 @@
+#!/bin/bash
+# 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.
+
+set -e
+
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash "$CORDOVA_LIB_PATH"/cordova list-started-emulators
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators.bat b/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators.bat
new file mode 100644
index 0000000..f1b3c5d
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" list-started-emulators //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/start-emulator
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/start-emulator b/lib/cordova-android/bin/templates/cordova/lib/start-emulator
new file mode 100644
index 0000000..8e8964d
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/start-emulator
@@ -0,0 +1,23 @@
+#!/bin/bash
+# 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.
+
+set -e
+
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash "$CORDOVA_LIB_PATH"/cordova start-emulator "$@"
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/lib/start-emulator.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/lib/start-emulator.bat b/lib/cordova-android/bin/templates/cordova/lib/start-emulator.bat
new file mode 100644
index 0000000..4f3fb5d
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/start-emulator.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" start-emulator %* //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/log
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/log b/lib/cordova-android/bin/templates/cordova/log
index 087a200..01fe107 100755
--- a/lib/cordova-android/bin/templates/cordova/log
+++ b/lib/cordova-android/bin/templates/cordova/log
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
 CORDOVA_PATH=$( cd "$( dirname "$0" )/.." && pwd )
 
-bash "$CORDOVA_PATH"/cordova/cordova log
+bash "$CORDOVA_PATH"/cordova/lib/cordova log "$@"

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/log.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/log.bat b/lib/cordova-android/bin/templates/cordova/log.bat
index b8cc6be..2c492e7 100644
--- a/lib/cordova-android/bin/templates/cordova/log.bat
+++ b/lib/cordova-android/bin/templates/cordova/log.bat
@@ -1,18 +1,2 @@
-:: 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.
-
-%~dp0\cordova.bat log
+@ECHO OFF
+%~dp0\cordova.bat log %*

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/release
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/release b/lib/cordova-android/bin/templates/cordova/release
deleted file mode 100755
index 73d873e..0000000
--- a/lib/cordova-android/bin/templates/cordova/release
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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.
-
-#!/bin/bash
-
-set -e
-
-CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
-
-bash "$CORDOVA_PATH"/cordova release

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/run
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/run b/lib/cordova-android/bin/templates/cordova/run
index 840a8d5..ec352b0 100755
--- a/lib/cordova-android/bin/templates/cordova/run
+++ b/lib/cordova-android/bin/templates/cordova/run
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
 CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova run
+bash "$CORDOVA_PATH"/lib/cordova run "$@"

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/run.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/run.bat b/lib/cordova-android/bin/templates/cordova/run.bat
index 7c470ed..b1cab64 100644
--- a/lib/cordova-android/bin/templates/cordova/run.bat
+++ b/lib/cordova-android/bin/templates/cordova/run.bat
@@ -1 +1,2 @@
-%~dp0\cordova.bat run
+@ECHO OFF
+%~dp0\cordova.bat run %*
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/ea1cf879/lib/cordova-android/bin/templates/cordova/version
----------------------------------------------------------------------
diff --git a/lib/cordova-android/bin/templates/cordova/version b/lib/cordova-android/bin/templates/cordova/version
new file mode 100644
index 0000000..21147ab
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/version
@@ -0,0 +1,32 @@
+#!/bin/bash
+# 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.
+
+set -e
+
+CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd -P)
+PROJECT_PATH="$(dirname "$CORDOVA_PATH")"
+
+VERSION_FILE_PATH="$PROJECT_PATH/assets/www/cordova.js"
+
+if [ -f "$VERSION_FILE_PATH" ]; then
+    JSVersion=$(sed -n '2,2p' assets/www/cordova.js)
+    echo $JSVersion | sed -e 's/\/\/ //'| cut -f 1 -d '-'
+else
+    echo "The file \"$VERSION_FILE_PATH\" does not exist."
+    exit 1
+fi


[27/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/CordovaCommandCall.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/CordovaCommandCall.cs b/lib/cordova-wp7/templates/standalone/cordovalib/CordovaCommandCall.cs
new file mode 100644
index 0000000..810f5e2
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/CordovaCommandCall.cs
@@ -0,0 +1,98 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace WPCordovaClassLib.Cordova
+{
+    /// <summary>
+    /// Represents Cordova native command call: action callback, etc
+    /// </summary>
+    public class CordovaCommandCall
+    {
+        public String Service { get; private set; }
+        public String Action { get; private set; }
+        public String CallbackId { get; private set; }
+        public String Args { get; private set; }
+
+        /// <summary>
+        /// Retrieves command call parameters and creates wrapper for them
+        /// </summary>
+        /// <param name="commandStr">Command string in the form 'service/action/callback/args'</param>
+        /// <returns>New class instance or null of string does not represent Cordova command</returns>
+        public static CordovaCommandCall Parse(string commandStr)
+        {
+            if (string.IsNullOrEmpty(commandStr))
+            {
+                return null;
+            }
+
+            string[] split = commandStr.Split('/');
+            if (split.Length < 3)
+            {
+                return null;
+            }
+
+            CordovaCommandCall commandCallParameters = new CordovaCommandCall();
+            commandCallParameters.Service = split[0];
+            commandCallParameters.Action = split[1];
+            commandCallParameters.CallbackId = split[2];
+
+            try
+            {
+                string arg = split.Length <= 3 ? "[]" : String.Join("/", split.Skip(3));
+                if (!arg.StartsWith("[")) // save the exception
+                {
+                    arg = string.Format("[{0}]", arg);
+                }
+                List<string> args = JSON.JsonHelper.Deserialize<List<string>>(arg);
+                args.Add(commandCallParameters.CallbackId);
+                commandCallParameters.Args = JSON.JsonHelper.Serialize(args.ToArray());
+            }
+            catch (Exception)
+            {
+                return null; 
+            }
+            // sanity check for illegal names
+            // was failing with ::
+            // CordovaCommandResult :: 1, Device1, {"status":1,"message":"{\"name\":\"XD.....
+            if (commandCallParameters.Service.IndexOfAny(new char[] { '@', ':', ',', '!', ' ' }) > -1)
+            {
+                return null;
+            }
+
+            return commandCallParameters;
+        }
+
+
+        /// <summary>
+        /// Private ctr to disable class creation.
+        /// New class instance must be initialized via CordovaCommandCall.Parse static method.
+        /// </summary>
+        private CordovaCommandCall() { }
+
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/CordovaView.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/CordovaView.xaml b/lib/cordova-wp7/templates/standalone/cordovalib/CordovaView.xaml
new file mode 100644
index 0000000..b993d97
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/CordovaView.xaml
@@ -0,0 +1,65 @@
+<!--
+ 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.  
+-->
+<UserControl x:Class="WPCordovaClassLib.CordovaView"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    d:DesignHeight="480" d:DesignWidth="480" 
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone">
+    
+    <Grid x:Name="LayoutRoot" Background="Transparent">
+        
+        <phone:WebBrowser x:Name="CordovaBrowser" 
+                          Opacity="0"
+                          HorizontalAlignment="Stretch"  
+                          VerticalAlignment="Stretch" 
+                          IsScriptEnabled="True" 
+                          Foreground="White"
+                          Background="Black"
+                          Navigated="GapBrowser_Navigated" 
+                          Loaded="GapBrowser_Loaded" 
+                          Unloaded="GapBrowser_Unloaded" 
+                          ScriptNotify="GapBrowser_ScriptNotify" 
+                          LoadCompleted="GapBrowser_LoadCompleted" 
+                          Navigating="GapBrowser_Navigating" 
+                          NavigationFailed="GapBrowser_NavigationFailed" 
+                          IsGeolocationEnabled="True">
+            <phone:WebBrowser.Projection>
+                <PlaneProjection x:Name="BrowserProjector" CenterOfRotationX="0" RotationY="-180"/>
+            </phone:WebBrowser.Projection>
+            <phone:WebBrowser.Resources>
+                <Storyboard x:Name="RotateIn" BeginTime="0:0:0.5">
+                    <DoubleAnimation
+                        Storyboard.TargetName="BrowserProjector"
+                        Storyboard.TargetProperty="RotationY"
+                        To="0" Duration="0:0:0.6"/>
+                </Storyboard>
+            </phone:WebBrowser.Resources>
+
+        </phone:WebBrowser>
+        
+    </Grid>
+</UserControl>
+
+    

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/CordovaView.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/CordovaView.xaml.cs b/lib/cordova-wp7/templates/standalone/cordovalib/CordovaView.xaml.cs
new file mode 100644
index 0000000..f6584a8
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/CordovaView.xaml.cs
@@ -0,0 +1,485 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+using System.IO.IsolatedStorage;
+using System.Windows.Resources;
+using System.Windows.Interop;
+using System.Runtime.Serialization.Json;
+using System.IO;
+using System.ComponentModel;
+using System.Xml.Linq;
+using WPCordovaClassLib.Cordova.Commands;
+using System.Diagnostics;
+using System.Text;
+using WPCordovaClassLib.Cordova;
+using System.Threading;
+using Microsoft.Phone.Shell;
+using WPCordovaClassLib.Cordova.JSON;
+using WPCordovaClassLib.CordovaLib;
+
+
+
+namespace WPCordovaClassLib
+{
+    public partial class CordovaView : UserControl
+    {
+
+        /// <summary>
+        /// Indicates whether web control has been loaded and no additional initialization is needed.
+        /// Prevents data clearing during page transitions.
+        /// </summary>
+        private bool IsBrowserInitialized = false;
+
+        /// <summary>
+        /// Set when the user attaches a back button handler inside the WebBrowser
+        /// </summary>
+        private bool OverrideBackButton = false;
+
+        /// <summary>
+        /// Sentinel to keep track of page changes as a result of the hardware back button
+        /// Set to false when the back-button is pressed, which calls js window.history.back()
+        /// If the page changes as a result of the back button the event is cancelled.
+        /// </summary>
+        private bool PageDidChange = false;
+
+        private static string AppRoot = "/app/";
+
+
+        /// <summary>
+        /// Handles native api calls
+        /// </summary>
+        private NativeExecution nativeExecution;
+
+        protected BrowserMouseHelper bmHelper;
+
+        protected DOMStorageHelper domStorageHelper;
+        protected OrientationHelper orientationHelper;
+
+        private ConfigHandler configHandler;
+
+        public System.Windows.Controls.Grid _LayoutRoot
+        {
+            get
+            {
+                return ((System.Windows.Controls.Grid)(this.FindName("LayoutRoot")));
+            }
+        }
+
+        public WebBrowser Browser
+        {
+            get
+            {
+                return CordovaBrowser;
+            }
+        }
+
+        /*
+         * Setting StartPageUri only has an effect if called before the view is loaded.
+         **/
+        protected Uri _startPageUri = null;
+        public Uri StartPageUri
+        {
+            get
+            {
+                if (_startPageUri == null)
+                {
+                    // default
+                    return new Uri(AppRoot + "www/index.html", UriKind.Relative);
+                }
+                else
+                {
+                    return _startPageUri;
+                }
+            }
+            set
+            {
+                if (!this.IsBrowserInitialized)
+                {
+                    _startPageUri = value;
+                }
+            }
+        }
+
+        public CordovaView()
+        {
+
+            InitializeComponent();
+
+            if (DesignerProperties.IsInDesignTool)
+            {
+                return;
+            }
+
+
+            StartupMode mode = PhoneApplicationService.Current.StartupMode;
+
+            if (mode == StartupMode.Launch)
+            {
+                PhoneApplicationService service = PhoneApplicationService.Current;
+                service.Activated += new EventHandler<Microsoft.Phone.Shell.ActivatedEventArgs>(AppActivated);
+                service.Launching += new EventHandler<LaunchingEventArgs>(AppLaunching);
+                service.Deactivated += new EventHandler<DeactivatedEventArgs>(AppDeactivated);
+                service.Closing += new EventHandler<ClosingEventArgs>(AppClosing);
+            }
+            else
+            {
+
+            }
+
+            configHandler = new ConfigHandler();
+            configHandler.LoadAppPackageConfig();
+
+            // initializes native execution logic
+            nativeExecution = new NativeExecution(ref this.CordovaBrowser);
+            bmHelper = new BrowserMouseHelper(ref this.CordovaBrowser);
+        }
+
+
+
+        void AppClosing(object sender, ClosingEventArgs e)
+        {
+            //Debug.WriteLine("AppClosing");
+        }
+
+        void AppDeactivated(object sender, DeactivatedEventArgs e)
+        {
+            Debug.WriteLine("INFO: AppDeactivated");
+
+            try
+            {
+                CordovaBrowser.InvokeScript("eval", new string[] { "cordova.fireDocumentEvent('pause');" });
+            }
+            catch (Exception)
+            {
+                Debug.WriteLine("ERROR: Pause event error");
+            }
+        }
+
+        void AppLaunching(object sender, LaunchingEventArgs e)
+        {
+            //Debug.WriteLine("INFO: AppLaunching");
+        }
+
+        void AppActivated(object sender, Microsoft.Phone.Shell.ActivatedEventArgs e)
+        {
+            Debug.WriteLine("INFO: AppActivated");
+            try
+            {
+                CordovaBrowser.InvokeScript("eval", new string[] { "cordova.fireDocumentEvent('resume');" });
+            }
+            catch (Exception)
+            {
+                Debug.WriteLine("ERROR: Resume event error");
+            }
+        }
+
+        void GapBrowser_Loaded(object sender, RoutedEventArgs e)
+        {
+            if (DesignerProperties.IsInDesignTool)
+            {
+                return;
+            }
+
+            // prevents refreshing web control to initial state during pages transitions
+            if (this.IsBrowserInitialized) return;
+
+
+
+            this.domStorageHelper = new DOMStorageHelper(this.CordovaBrowser);
+
+            try
+            {
+
+                // Before we possibly clean the ISO-Store, we need to grab our generated UUID, so we can rewrite it after.
+                string deviceUUID = "";
+
+                using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    try
+                    {
+                        IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Open, FileAccess.Read, appStorage);
+
+                        using (StreamReader reader = new StreamReader(fileStream))
+                        {
+                            deviceUUID = reader.ReadLine();
+                        }
+                    }
+                    catch (Exception /*ex*/)
+                    {
+                        deviceUUID = Guid.NewGuid().ToString();
+                    }
+
+                    Debug.WriteLine("Updating IsolatedStorage for APP:DeviceID :: " + deviceUUID);
+                    IsolatedStorageFileStream file = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Create, FileAccess.Write, appStorage);
+                    using (StreamWriter writeFile = new StreamWriter(file))
+                    {
+                        writeFile.WriteLine(deviceUUID);
+                        writeFile.Close();
+                    }
+
+                }
+
+                StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("CordovaSourceDictionary.xml", UriKind.Relative));
+
+                if (streamInfo != null)
+                {
+                    StreamReader sr = new StreamReader(streamInfo.Stream);
+                    //This will Read Keys Collection for the xml file
+
+                    XDocument document = XDocument.Parse(sr.ReadToEnd());
+
+                    var files = from results in document.Descendants("FilePath")
+                                select new
+                                {
+                                    path = (string)results.Attribute("Value")
+                                };
+                    StreamResourceInfo fileResourceStreamInfo;
+
+                    using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+
+                        foreach (var file in files)
+                        {
+                            fileResourceStreamInfo = Application.GetResourceStream(new Uri(file.path, UriKind.Relative));
+
+                            if (fileResourceStreamInfo != null)
+                            {
+                                using (BinaryReader br = new BinaryReader(fileResourceStreamInfo.Stream))
+                                {
+                                    byte[] data = br.ReadBytes((int)fileResourceStreamInfo.Stream.Length);
+
+                                    string strBaseDir = AppRoot + file.path.Substring(0, file.path.LastIndexOf(System.IO.Path.DirectorySeparatorChar));
+
+                                    if (!appStorage.DirectoryExists(strBaseDir))
+                                    {
+                                        Debug.WriteLine("INFO: Creating Directory :: " + strBaseDir);
+                                        appStorage.CreateDirectory(strBaseDir);
+                                    }
+
+                                    // This will truncate/overwrite an existing file, or 
+                                    using (IsolatedStorageFileStream outFile = appStorage.OpenFile(AppRoot + file.path, FileMode.Create))
+                                    {
+                                        Debug.WriteLine("INFO: Writing data for " + AppRoot + file.path + " and length = " + data.Length);
+                                        using (var writer = new BinaryWriter(outFile))
+                                        {
+                                            writer.Write(data);
+                                        }
+                                    }
+                                }
+                            }
+                            else
+                            {
+                                Debug.WriteLine("ERROR: Failed to write file :: " + file.path + " did you forget to add it to the project?");
+                            }
+                        }
+                    }
+                }
+
+                CordovaBrowser.Navigate(StartPageUri);
+                IsBrowserInitialized = true;
+                AttachHardwareButtonHandlers();
+            }
+            catch (Exception ex)
+            {
+                Debug.WriteLine("ERROR: Exception in GapBrowser_Loaded :: {0}", ex.Message);
+            }
+        }
+
+        void AttachHardwareButtonHandlers()
+        {
+            PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+            if (frame != null)
+            {
+                PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+
+                if (page != null)
+                {
+                    page.BackKeyPress += new EventHandler<CancelEventArgs>(page_BackKeyPress);
+
+                    this.orientationHelper = new OrientationHelper(this.CordovaBrowser, page);
+
+                }
+            }
+        }
+
+        void page_BackKeyPress(object sender, CancelEventArgs e)
+        {
+
+            if (OverrideBackButton)
+            {
+                try
+                {
+                    CordovaBrowser.InvokeScript("eval", new string[] { "cordova.fireDocumentEvent('backbutton');" });
+                    e.Cancel = true;
+                }
+                catch (Exception ex)
+                {
+                    Console.WriteLine("Exception while invoking backbutton into cordova view: " + ex.Message);
+                }
+            }
+            else
+            {
+                try
+                {
+                    PageDidChange = false;
+
+                    Uri uriBefore = this.Browser.Source;
+                    // calling js history.back with result in a page change if history was valid.
+                    CordovaBrowser.InvokeScript("eval", new string[] { "(function(){window.history.back();})()" });
+
+                    Uri uriAfter = this.Browser.Source;
+
+                    e.Cancel = PageDidChange || (uriBefore != uriAfter);
+                }
+                catch (Exception)
+                {
+                    e.Cancel = false; // exit the app ... ?
+                }
+            }
+        }
+
+        void GapBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            string nativeReady = "(function(){ cordova.require('cordova/channel').onNativeReady.fire()})();";
+
+            try
+            {
+                CordovaBrowser.InvokeScript("eval", new string[] { nativeReady });
+            }
+            catch (Exception ex)
+            {
+                Debug.WriteLine("Error calling js to fire nativeReady event. Did you include cordova-x.x.x.js in your html script tag?");
+            }
+
+            if (this.CordovaBrowser.Opacity < 1)
+            {
+                this.CordovaBrowser.Opacity = 1;
+                RotateIn.Begin();
+            }
+        }
+
+
+        void GapBrowser_Navigating(object sender, NavigatingEventArgs e)
+        {
+            if (!configHandler.URLIsAllowed(e.Uri.ToString()))
+            {
+                e.Cancel = true;
+                return;
+            }
+
+            this.PageDidChange = true;
+            // Debug.WriteLine("GapBrowser_Navigating to :: " + e.Uri.ToString());
+            this.nativeExecution.ResetAllCommands();
+
+            // TODO: check whitelist / blacklist
+            // NOTE: Navigation can be cancelled by setting :        e.Cancel = true;
+        }
+
+        /*
+         *  This method does the work of routing commands
+         *  NotifyEventArgs.Value contains a string passed from JS 
+         *  If the command already exists in our map, we will just attempt to call the method(action) specified, and pass the args along
+         *  Otherwise, we create a new instance of the command, add it to the map, and call it ...
+         *  This method may also receive JS error messages caught by window.onerror, in any case where the commandStr does not appear to be a valid command
+         *  it is simply output to the debugger output, and the method returns.
+         * 
+         **/
+        void GapBrowser_ScriptNotify(object sender, NotifyEventArgs e)
+        {
+            string commandStr = e.Value;
+
+            if (commandStr.IndexOf("DOMStorage") == 0)
+            {
+                this.domStorageHelper.HandleStorageCommand(commandStr);
+                return;
+            }
+            else if (commandStr.IndexOf("Orientation") == 0)
+            {
+                this.orientationHelper.HandleCommand(commandStr);
+                return;
+            }
+
+            CordovaCommandCall commandCallParams = CordovaCommandCall.Parse(commandStr);
+
+            if (commandCallParams == null)
+            {
+                // ERROR
+                Debug.WriteLine("ScriptNotify :: " + commandStr);
+            }
+            else if (commandCallParams.Service == "CoreEvents")
+            {
+                switch (commandCallParams.Action.ToLower())
+                {
+                    case "overridebackbutton":
+                        string arg0 = JsonHelper.Deserialize<string[]>(commandCallParams.Args)[0];
+                        this.OverrideBackButton = (arg0 != null && arg0.Length > 0 && arg0.ToLower() == "true"); 
+                        break;
+                }
+            }
+            else
+            {
+                if (configHandler.IsPluginAllowed(commandCallParams.Service))
+                {
+                    nativeExecution.ProcessCommand(commandCallParams);
+                }
+                else
+                {
+                    Debug.WriteLine("Error::Plugin not allowed in config.xml. " + commandCallParams.Service);
+                }
+            }
+        }
+
+        public void LoadPage(string url)
+        {
+            try 
+            {
+                Uri newLoc = new Uri(url,UriKind.RelativeOrAbsolute);
+                CordovaBrowser.Navigate(newLoc);
+            }
+            catch(Exception)
+            {
+
+            }
+        }
+
+        private void GapBrowser_Unloaded(object sender, RoutedEventArgs e)
+        {
+
+        }
+
+        private void GapBrowser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
+        {
+            Debug.WriteLine("GapBrowser_NavigationFailed :: " + e.Uri.ToString());
+        }
+
+        private void GapBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            Debug.WriteLine("GapBrowser_Navigated :: " + e.Uri.ToString());
+        }
+
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/DOMStorageHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/DOMStorageHelper.cs b/lib/cordova-wp7/templates/standalone/cordovalib/DOMStorageHelper.cs
new file mode 100644
index 0000000..f57bae4
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/DOMStorageHelper.cs
@@ -0,0 +1,145 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.IO.IsolatedStorage;
+using System.Collections.Generic;
+using Microsoft.Phone.Controls;
+using System.Linq;
+using WPCordovaClassLib.Cordova.JSON;
+
+/*
+ * Translates DOMStorage API between JS and Isolated Storage
+ * Missing pieces : QUOTA_EXCEEDED_ERR  + StorageEvent  
+ * */
+
+namespace WPCordovaClassLib
+{
+    public class DOMStorageHelper
+    {
+        protected WebBrowser webBrowser1;
+
+        public DOMStorageHelper(WebBrowser gapBrowser)
+        {
+            this.webBrowser1 = gapBrowser;
+            // always clear session at creation
+            UserSettings["sessionStorage"] = new Dictionary<string, string>();
+
+            if (!UserSettings.Contains("localStorage"))
+            {
+                UserSettings["localStorage"] = new Dictionary<string, string>();
+                UserSettings.Save();
+            }
+            Application.Current.Exit += new EventHandler(OnAppExit);
+        }
+
+        void OnAppExit(object sender, EventArgs e)
+        {
+            UserSettings.Remove("sessionStorage");
+            UserSettings.Save();
+        }
+
+        protected IsolatedStorageSettings UserSettings
+        {
+            get
+            {
+                return IsolatedStorageSettings.ApplicationSettings;
+            }
+        }
+
+        protected Dictionary<string, string> getStorageByType(string type)
+        {
+            if (!UserSettings.Contains(type))
+            {
+                UserSettings[type] = new Dictionary<string, string>();
+                UserSettings.Save();
+            }
+            return UserSettings[type] as Dictionary<string, string>;
+        }
+
+
+        public void HandleStorageCommand(string commandStr)
+        {
+
+            string[] split = commandStr.Split('/');
+            if (split.Length > 3)
+            {
+                string api = split[0];
+                string type = split[1]; // localStorage || sessionStorage
+                string command = split[2];
+                string param = split[3];
+
+                Dictionary<string, string> currentStorage = getStorageByType(type);
+
+                switch (command)
+                {
+                    case "get":
+                        {
+
+                            if (currentStorage.Keys.Contains(param))
+                            {
+                                string value = currentStorage[param];
+                                webBrowser1.InvokeScript("execScript", "window." + type + ".onResult('" + param + "','" + value + "');");
+                            }
+                            else
+                            {
+                                webBrowser1.InvokeScript("execScript", "window." + type + ".onResult('" + param + "');");
+                            }
+
+                        }
+                        break;
+                    case "load":
+                        {
+                            string[] keys = currentStorage.Keys.ToArray();
+                            string jsonString = JsonHelper.Serialize(keys);
+                            string callbackJS = "window." + type + ".onKeysChanged('" + jsonString + "');";
+                            webBrowser1.InvokeScript("execScript", callbackJS);
+                        }
+                        break;
+                    case "set":
+                        {
+                            // TODO: check that length is not out of bounds
+                            currentStorage[param] = split[4];
+                            UserSettings.Save();
+                            string[] keys = currentStorage.Keys.ToArray();
+                            string jsonString = JsonHelper.Serialize(keys);
+                            string callbackJS = "window." + type + ".onKeysChanged('" + jsonString + "');";
+                            webBrowser1.InvokeScript("execScript", callbackJS);
+                        }
+                        break;
+                    case "remove":
+                        currentStorage.Remove(param);
+                        UserSettings.Save();
+                        break;
+                    case "clear":
+                        currentStorage = new Dictionary<string, string>();
+                        UserSettings[type] = currentStorage;
+                        UserSettings.Save();
+                        break;
+                }
+
+            }
+
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/JSON/JsonHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/JSON/JsonHelper.cs b/lib/cordova-wp7/templates/standalone/cordovalib/JSON/JsonHelper.cs
new file mode 100644
index 0000000..44511f6
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/JSON/JsonHelper.cs
@@ -0,0 +1,97 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Runtime.Serialization.Json;
+using System.IO;
+using System.Collections.Generic;
+using System.Text;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.JSON
+{
+    /// <summary>
+    /// Provides JSON serialization/deserialization functionality.
+    /// </summary>
+    public static class JsonHelper
+    {
+        /// <summary>
+        /// Serializes object to JSON string representation
+        /// </summary>
+        /// <param name="obj">object to serialize</param>
+        /// <returns>JSON representation of the object. Returns 'null' string for null passed as argument</returns>
+        public static string Serialize(object obj)
+        {
+            if (obj == null)
+            {
+                return "null";
+            }
+
+            DataContractJsonSerializer ser = new DataContractJsonSerializer(obj.GetType());
+
+            MemoryStream ms = new MemoryStream();
+            ser.WriteObject(ms, obj);
+
+            ms.Position = 0;
+
+            string json = String.Empty;
+
+            using (StreamReader sr = new StreamReader(ms))
+            {
+                json = sr.ReadToEnd();
+            }
+
+            ms.Close();
+
+            return json;
+
+        }
+
+        /// <summary>
+        /// Parses json string to object instance
+        /// </summary>
+        /// <typeparam name="T">type of the object</typeparam>
+        /// <param name="json">json string representation of the object</param>
+        /// <returns>Deserialized object instance</returns>
+        public static T Deserialize<T>(string json)
+        {
+            DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(T));
+            object result = null;
+            try
+            {
+                using (MemoryStream mem = new MemoryStream(Encoding.UTF8.GetBytes(json)))
+                {
+                    result = deserializer.ReadObject(mem);
+                }
+            }
+            catch (Exception ex)
+            {
+                Debug.WriteLine(ex.Message);
+                Debug.WriteLine("Failed to deserialize " + typeof(T) + " with JSON value :: " + json);
+            }
+
+            return (T)result;
+
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/NativeExecution.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/NativeExecution.cs b/lib/cordova-wp7/templates/standalone/cordovalib/NativeExecution.cs
new file mode 100644
index 0000000..af6b207
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/NativeExecution.cs
@@ -0,0 +1,246 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+using Microsoft.Devices;
+using Microsoft.Phone.Controls;
+using WPCordovaClassLib.Cordova.Commands;
+using System.Windows;
+
+namespace WPCordovaClassLib.Cordova
+{
+    /// <summary>
+    /// Implements logic to execute native command and return result back.
+    /// All commands are executed asynchronous.
+    /// </summary>
+    public class NativeExecution
+    {
+        /// <summary>
+        /// Reference to web part where application is hosted
+        /// </summary>
+        private readonly WebBrowser webBrowser;
+
+        /// <summary>
+        /// Creates new instance of a NativeExecution class. 
+        /// </summary>
+        /// <param name="browser">Reference to web part where application is hosted</param>
+        public NativeExecution(ref WebBrowser browser)
+        {
+            if (browser == null)
+            {
+                throw new ArgumentNullException("browser");
+            }
+
+            this.webBrowser = browser;
+        }
+
+        /// <summary>
+        /// Returns where application is running on emulator
+        /// </summary>
+        /// <returns>True if running on emulator, otherwise False</returns>
+        public static bool IsRunningOnEmulator()
+        {
+            return Microsoft.Devices.Environment.DeviceType == DeviceType.Emulator;
+        }
+
+        public void ResetAllCommands()
+        {
+            CommandFactory.ResetAllCommands();
+        }
+
+        public void AutoLoadCommand(string commandService)
+        {
+            BaseCommand bc = CommandFactory.CreateByServiceName(commandService);
+            if (bc != null)
+            {
+                bc.OnInit();
+            }
+
+        }
+
+        /// <summary>
+        /// Executes command and returns result back.
+        /// </summary>
+        /// <param name="commandCallParams">Command to execute</param>
+        public void ProcessCommand(CordovaCommandCall commandCallParams)
+        {
+
+            if (commandCallParams == null)
+            {
+                throw new ArgumentNullException("commandCallParams");
+            }
+
+            try
+            {
+                BaseCommand bc = CommandFactory.CreateByServiceName(commandCallParams.Service);
+
+                if (bc == null)
+                {
+                    this.OnCommandResult(commandCallParams.CallbackId, new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION));
+                    return;
+                }
+
+                EventHandler<PluginResult> OnCommandResultHandler = delegate(object o, PluginResult res)
+                {
+                    if (res.CallbackId == null || res.CallbackId == commandCallParams.CallbackId)
+                    {
+                        this.OnCommandResult(commandCallParams.CallbackId, res);
+                        if (!res.KeepCallback)
+                        {
+                            bc.RemoveResultHandler(commandCallParams.CallbackId);
+                        }
+                    }
+                };
+
+                //bc.OnCommandResult += OnCommandResultHandler;
+                bc.AddResultHandler(commandCallParams.CallbackId, OnCommandResultHandler);
+
+                EventHandler<ScriptCallback> OnCustomScriptHandler = delegate(object o, ScriptCallback script)
+                {
+                    this.InvokeScriptCallback(script);
+                };
+
+                bc.OnCustomScript += OnCustomScriptHandler;
+
+                ThreadStart methodInvokation = () =>
+                {
+                    try
+                    {
+                        bc.InvokeMethodNamed(commandCallParams.CallbackId,commandCallParams.Action, commandCallParams.Args);
+                    }
+                    catch (Exception ex)
+                    {
+                        Debug.WriteLine("ERROR: Exception in ProcessCommand :: " + ex.Message);
+                        bc.RemoveResultHandler(commandCallParams.CallbackId);
+                        bc.OnCustomScript -= OnCustomScriptHandler;
+
+                        Debug.WriteLine("ERROR: failed to InvokeMethodNamed :: " + commandCallParams.Action + " on Object :: " + commandCallParams.Service);
+                        this.OnCommandResult(commandCallParams.CallbackId, new PluginResult(PluginResult.Status.INVALID_ACTION));
+                        return;
+                    }
+                };
+
+                if ((bc is File) || (bc is Accelerometer))
+                {
+                    // Due to some issues with the IsolatedStorage in current version of WP8 SDK we have to run all File Api commands synchronously.
+                    // TODO: test this in WP8 RTM
+                    methodInvokation.Invoke();
+                }
+                else
+                {
+                    new Thread(methodInvokation).Start();
+                }
+
+
+            }
+            catch (Exception ex)
+            {
+                // ERROR
+                Debug.WriteLine(String.Format("ERROR: Unable to execute command :: {0}:{1}:{3} ",
+                    commandCallParams.Service, commandCallParams.Action, ex.Message));
+
+                this.OnCommandResult(commandCallParams.CallbackId, new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+        }
+
+        /// <summary>
+        /// Handles command execution result.
+        /// </summary>
+        /// <param name="callbackId">Command callback identifier on client side</param>
+        /// <param name="result">Execution result</param>
+        private void OnCommandResult(string callbackId, PluginResult result)
+        {
+            #region  args checking
+
+            if (result == null)
+            {
+                Debug.WriteLine("ERROR: OnCommandResult missing result argument");
+                return;
+            }
+
+            if (String.IsNullOrEmpty(callbackId))
+            {
+                Debug.WriteLine("ERROR: OnCommandResult missing callbackId argument");
+                return;
+            }
+
+            if (!String.IsNullOrEmpty(result.CallbackId) && callbackId != result.CallbackId)
+            {
+                Debug.WriteLine("Multiple Overlapping Results :: " + result.CallbackId + " :: " + callbackId);
+                return;
+            }
+
+            #endregion
+
+            string jsonResult = result.ToJSONString();
+
+            string callback;
+            string args = string.Format("('{0}',{1});", callbackId, jsonResult);
+
+            if (result.Result == PluginResult.Status.NO_RESULT ||
+               result.Result == PluginResult.Status.OK)
+            {
+                callback = @"(function(callbackId,args) {
+                try { args.message = JSON.parse(args.message); } catch (ex) { }
+                cordova.callbackSuccess(callbackId,args);
+                })" + args;
+            }
+            else
+            {
+                callback = @"(function(callbackId,args) {
+                try { args.message = JSON.parse(args.message); } catch (ex) { }
+                cordova.callbackError(callbackId,args);
+                })" + args;
+            }
+            this.InvokeScriptCallback(new ScriptCallback("eval", new string[] { callback }));
+
+        }
+
+        /// <summary>
+        /// Executes client java script
+        /// </summary>
+        /// <param name="script">Script to execute on client side</param>
+        private void InvokeScriptCallback(ScriptCallback script)
+        {
+            if (script == null)
+            {
+                throw new ArgumentNullException("script");
+            }
+
+            if (String.IsNullOrEmpty(script.ScriptName))
+            {
+                throw new ArgumentNullException("ScriptName");
+            }
+
+            //Debug.WriteLine("INFO:: About to invoke ::" + script.ScriptName + " with args ::" + script.Args[0]);
+            this.webBrowser.Dispatcher.BeginInvoke((ThreadStart)delegate()
+            {
+                try
+                {
+                    //Debug.WriteLine("INFO:: InvokingScript::" + script.ScriptName + " with args ::" + script.Args[0]);
+                    this.webBrowser.InvokeScript(script.ScriptName, script.Args);
+                }
+                catch (Exception ex)
+                {
+                    Debug.WriteLine("ERROR: Exception in InvokeScriptCallback :: " + ex.Message);
+                }
+
+            });
+        }
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/OrientationHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/OrientationHelper.cs b/lib/cordova-wp7/templates/standalone/cordovalib/OrientationHelper.cs
new file mode 100644
index 0000000..81a329e
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/OrientationHelper.cs
@@ -0,0 +1,128 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+
+namespace WPCordovaClassLib.Cordova
+{
+    public class OrientationHelper
+    {
+        protected WebBrowser gapBrowser;
+        protected PhoneApplicationPage page;
+        // private PageOrientation CurrentOrientation = PageOrientation.PortraitUp;
+        //private PageOrientation[] SupportedOrientations; // TODO:
+
+        public OrientationHelper(WebBrowser gapBrowser, PhoneApplicationPage gapPage)
+        {
+            this.gapBrowser = gapBrowser;
+            page = gapPage;
+
+            page.OrientationChanged += new EventHandler<OrientationChangedEventArgs>(page_OrientationChanged);
+            gapBrowser.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(gapBrowser_LoadCompleted);
+
+
+        }
+
+        void gapBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            int i = 0;
+
+            switch (this.page.Orientation)
+            {
+                case PageOrientation.Portrait: // intentional fall through
+                case PageOrientation.PortraitUp:
+                    i = 0;
+                    break;
+                case PageOrientation.PortraitDown:
+                    i = 180;
+                    break;
+                case PageOrientation.Landscape: // intentional fall through
+                case PageOrientation.LandscapeLeft:
+                    i = -90;
+                    break;
+                case PageOrientation.LandscapeRight:
+                    i = 90;
+                    break;
+            }
+            // Cordova.fireEvent('orientationchange', window);
+            string jsCallback = String.Format("window.orientation = {0};", i);
+
+            try
+            {
+                gapBrowser.InvokeScript("execScript", jsCallback);
+            }
+            catch (Exception)
+            {
+            }
+        }
+
+        void page_OrientationChanged(object sender, OrientationChangedEventArgs e)
+        {
+            int i = 0;
+
+            switch (e.Orientation)
+            {
+                case PageOrientation.Portrait: // intentional fall through
+                case PageOrientation.PortraitUp:
+                    i = 0;
+                    break;
+                case PageOrientation.PortraitDown:
+                    i = 180;
+                    break;
+                case PageOrientation.Landscape: // intentional fall through
+                case PageOrientation.LandscapeLeft:
+                    i = -90;
+                    break;
+                case PageOrientation.LandscapeRight:
+                    i = 90;
+                    break;
+            }
+            // Cordova.fireEvent('orientationchange', window);
+            string jsCallback = String.Format("window.orientation = {0};", i);
+
+            try
+            {
+
+                gapBrowser.InvokeScript("execScript", jsCallback);
+
+                jsCallback = "var evt = document.createEvent('HTMLEvents');";
+                jsCallback += "evt.initEvent( 'orientationchange', true, false );";
+                jsCallback += "window.dispatchEvent(evt);";
+                jsCallback += "if(window.onorientationchange){window.onorientationchange(evt);}";
+
+                gapBrowser.InvokeScript("execScript", jsCallback);
+            }
+            catch (Exception)
+            {
+            }
+        }
+
+        public void HandleCommand(string commandStr)
+        {
+
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/PluginResult.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/PluginResult.cs b/lib/cordova-wp7/templates/standalone/cordovalib/PluginResult.cs
new file mode 100644
index 0000000..00017d2
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/PluginResult.cs
@@ -0,0 +1,139 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Text;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova
+{
+    /// <summary>
+    /// Represents command execution result
+    /// </summary>
+    public class PluginResult : EventArgs
+    {
+        /// <summary>
+        /// Predefined resultant messages
+        /// </summary>
+        public static string[] StatusMessages = new string[] 
+		{
+			"No result",
+			"OK",
+			"Class not found",
+			"Illegal access",
+			"Instantiation error",
+			"Malformed url",
+			"IO error",
+			"Invalid action",
+			"JSON error",
+			"Error"
+		};
+
+        /// <summary>
+        /// Possible command results status codes
+        /// </summary>
+        public enum Status : int
+        {
+            NO_RESULT = 0,
+            OK,
+            CLASS_NOT_FOUND_EXCEPTION,
+            ILLEGAL_ACCESS_EXCEPTION,
+            INSTANTIATION_EXCEPTION,
+            MALFORMED_URL_EXCEPTION,
+            IO_EXCEPTION,
+            INVALID_ACTION,
+            JSON_EXCEPTION,
+            ERROR
+        };
+
+        public Status Result { get; private set; }
+        public string Message { get; set; }
+        public bool KeepCallback { get; set; }
+        public string CallbackId { get; set; }
+
+        /// <summary>
+        /// Whether command succeded or not
+        /// </summary>
+        public bool IsSuccess
+        {
+            get
+            {
+                return this.Result == Status.OK || this.Result == Status.NO_RESULT;
+            }
+        }
+
+        /// <summary>
+        /// Creates new instance of the PluginResult class.
+        /// </summary>
+        /// <param name="status">Execution result</param>
+        public PluginResult(Status status)
+            : this(status, PluginResult.StatusMessages[(int)status])
+        {
+        }
+
+        /// <summary>
+        /// Creates new instance of the PluginResult class.
+        /// </summary>
+        /// <param name="status">Execution result</param>
+        /// <param name="message">The message</param>
+        public PluginResult(Status status, object message)
+        {
+            this.Result = status;
+            this.Message = JSON.JsonHelper.Serialize(message);
+        }
+
+        public string ToJSONString()
+        {
+            string res = String.Format("\"status\":{0},\"message\":{1},\"keepCallback\":{2}",
+                (int)this.Result,
+                this.Message,
+                this.KeepCallback.ToString().ToLower());
+
+            res = "{" + res + "}";
+            return res;
+
+        }
+
+        [Obsolete]
+        public string ToCallbackString(string callbackId, string successCallback, string errorCallback)
+        {
+            if (this.IsSuccess)
+            {
+                StringBuilder buf = new StringBuilder("");
+                buf.Append(String.Format("{0}('{1}',{2});", successCallback, callbackId, this.ToJSONString()));
+                return buf.ToString();
+            }
+            else
+            {
+                return String.Format("{0}('{1}',{2});", errorCallback, callbackId, this.ToJSONString());
+            }
+        }
+
+        public override String ToString()
+        {
+            return this.ToJSONString();
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/ScriptCallback.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/ScriptCallback.cs b/lib/cordova-wp7/templates/standalone/cordovalib/ScriptCallback.cs
new file mode 100644
index 0000000..7878134
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordovalib/ScriptCallback.cs
@@ -0,0 +1,80 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using WPCordovaClassLib.Cordova.JSON;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova
+{
+    /// <summary>
+    /// Represents client script function to execute
+    /// </summary>
+    public class ScriptCallback : EventArgs
+    {
+        /// <summary>
+        /// The scripting function to execute.
+        /// </summary>
+        public string ScriptName { get; private set; }
+
+        /// <summary>
+        /// A variable number of strings to pass to the function as parameters.
+        /// </summary>
+        public string[] Args { get; private set; }
+
+        /// <summary>
+        /// Creates new instance of a ScriptCallback class.
+        /// </summary>
+        /// <param name="function">The scripting function to execute</param>
+        /// <param name="args">A variable number of strings to pass to the function as parameters</param>
+        public ScriptCallback(string function, string[] args)
+        {
+            this.ScriptName = function;
+            this.Args = args;
+        }
+
+        /// <summary>
+        /// Creates new instance of a ScriptCallback class.
+        /// </summary>
+        /// <param name="function">The scripting function to execute</param>
+        /// <param name="id">The id argument</param>
+        /// <param name="msg">The message argument</param>
+        /// <param name="value">The value argument</param>
+        public ScriptCallback(string function, string id, object msg, object value)
+        {
+            this.ScriptName = function;
+
+            String arg = String.Format("{{\"id\": {0}, \"msg\": {1}, \"value\": {2}}}",
+                 JsonHelper.Serialize(id), JsonHelper.Serialize(msg), JsonHelper.Serialize(value));
+
+            this.Args = new string[] { arg };
+        }
+
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordovalib/resources/notification-beep.wav
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordovalib/resources/notification-beep.wav b/lib/cordova-wp7/templates/standalone/cordovalib/resources/notification-beep.wav
new file mode 100644
index 0000000..d0ad085
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/cordovalib/resources/notification-beep.wav differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/resources/notification-beep.wav
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/resources/notification-beep.wav b/lib/cordova-wp7/templates/standalone/resources/notification-beep.wav
new file mode 100644
index 0000000..d0ad085
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/resources/notification-beep.wav differ


[29/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/MimeTypeMapper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/MimeTypeMapper.cs b/lib/cordova-wp7/templates/standalone/Plugins/MimeTypeMapper.cs
new file mode 100644
index 0000000..01ba4cb
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/MimeTypeMapper.cs
@@ -0,0 +1,99 @@
+/*  
+	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.
+*/
+
+using System.Collections.Generic;
+using System.IO;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Represents file extension to mime type mapper.
+    /// </summary>
+    public static class MimeTypeMapper
+    {
+        /// <summary>
+        /// For unknown type it is recommended to use 'application/octet-stream'
+        /// http://stackoverflow.com/questions/1176022/unknown-file-type-mime
+        /// </summary>
+        private static string DefaultMimeType = "application/octet-stream";
+
+        /// <summary>
+        /// Stores mime type for all necessary extension
+        /// </summary>
+        private static readonly Dictionary<string, string> MIMETypesDictionary = new Dictionary<string, string>
+                                                                             {                                                                                
+                                                                                 {"avi", "video/x-msvideo"},
+                                                                                 {"bmp", "image/bmp"},                                                                                                                                                                
+                                                                                 {"gif", "image/gif"},                                                                                                                                                               
+                                                                                 {"jpe", "image/jpeg"},
+                                                                                 {"jpeg", "image/jpeg"},
+                                                                                 {"jpg", "image/jpeg"},                                                                                                                                                             
+                                                                                 {"mov", "video/quicktime"},
+                                                                                 {"mp2", "audio/mpeg"},
+                                                                                 {"mp3", "audio/mpeg"},
+                                                                                 {"mp4", "video/mp4"},
+                                                                                 {"mpe", "video/mpeg"},
+                                                                                 {"mpeg", "video/mpeg"},
+                                                                                 {"mpg", "video/mpeg"},
+                                                                                 {"mpga", "audio/mpeg"},                                                                                
+                                                                                 {"pbm", "image/x-portable-bitmap"},
+                                                                                 {"pcm", "audio/x-pcm"},
+                                                                                 {"pct", "image/pict"},
+                                                                                 {"pgm", "image/x-portable-graymap"},
+                                                                                 {"pic", "image/pict"},
+                                                                                 {"pict", "image/pict"},
+                                                                                 {"png", "image/png"},
+                                                                                 {"pnm", "image/x-portable-anymap"},
+                                                                                 {"pnt", "image/x-macpaint"},
+                                                                                 {"pntg", "image/x-macpaint"},
+                                                                                 {"ppm", "image/x-portable-pixmap"},
+                                                                                 {"qt", "video/quicktime"},
+                                                                                 {"ra", "audio/x-pn-realaudio"},
+                                                                                 {"ram", "audio/x-pn-realaudio"},
+                                                                                 {"ras", "image/x-cmu-raster"},
+                                                                                 {"rgb", "image/x-rgb"},
+                                                                                 {"snd", "audio/basic"},
+                                                                                 {"txt", "text/plain"},
+                                                                                 {"tif", "image/tiff"},
+                                                                                 {"tiff", "image/tiff"},
+                                                                                 {"wav", "audio/x-wav"},
+                                                                                 {"wbmp", "image/vnd.wap.wbmp"},
+
+                                                                             };
+        /// <summary>
+        /// Gets mime type by file extension
+        /// </summary>
+        /// <param name="fileName">file name to extract extension</param>
+        /// <returns>mime type</returns>
+        public static string GetMimeType(string fileName)
+        {
+            string ext = Path.GetExtension(fileName);
+
+            // invalid extension
+            if (string.IsNullOrEmpty(ext) || !ext.StartsWith("."))
+            {
+                return DefaultMimeType;
+            }
+
+            ext = ext.Remove(0, 1);
+
+            if (MIMETypesDictionary.ContainsKey(ext))
+            {
+                return MIMETypesDictionary[ext];
+            }
+
+            return DefaultMimeType;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/NetworkStatus.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/NetworkStatus.cs b/lib/cordova-wp7/templates/standalone/Plugins/NetworkStatus.cs
new file mode 100644
index 0000000..12eb061
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/NetworkStatus.cs
@@ -0,0 +1,129 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Diagnostics;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Net.NetworkInformation;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+
+    // http://msdn.microsoft.com/en-us/library/microsoft.phone.net.networkinformation(v=VS.92).aspx
+    // http://msdn.microsoft.com/en-us/library/microsoft.phone.net.networkinformation.devicenetworkinformation(v=VS.92).aspx
+
+    public class NetworkStatus : BaseCommand
+    {
+        const string UNKNOWN = "unknown";
+        const string ETHERNET = "ethernet";
+        const string WIFI = "wifi";
+        const string CELL_2G = "2g";
+        const string CELL_3G = "3g";
+        const string CELL_4G = "4g";
+        const string NONE = "none";
+        const string CELL = "cellular";
+
+        private bool HasCallback = false;
+
+        public NetworkStatus()
+        {
+            DeviceNetworkInformation.NetworkAvailabilityChanged += new EventHandler<NetworkNotificationEventArgs>(ChangeDetected);
+        }
+
+        public override void OnResume(object sender, Microsoft.Phone.Shell.ActivatedEventArgs e)
+        {
+            this.getConnectionInfo("");
+        }
+
+        public void getConnectionInfo(string empty)
+        {
+            HasCallback = true;
+            updateConnectionType(checkConnectionType());
+        }
+
+        private string checkConnectionType()
+        {
+            if (DeviceNetworkInformation.IsNetworkAvailable)
+            {
+                if (DeviceNetworkInformation.IsWiFiEnabled)
+                {
+                    return WIFI;
+                }
+                else
+                {
+                    return DeviceNetworkInformation.IsCellularDataEnabled ? CELL : UNKNOWN;
+                }
+            }
+            return NONE;
+        }
+
+        private string checkConnectionType(NetworkInterfaceSubType type)
+        {
+            switch (type)
+            {
+                case NetworkInterfaceSubType.Cellular_1XRTT: //cell
+                case NetworkInterfaceSubType.Cellular_GPRS: //cell
+                    return CELL;
+                case NetworkInterfaceSubType.Cellular_EDGE: //2
+                    return CELL_2G;
+                case NetworkInterfaceSubType.Cellular_3G:
+                case NetworkInterfaceSubType.Cellular_EVDO: //3
+                case NetworkInterfaceSubType.Cellular_EVDV: //3 
+                case NetworkInterfaceSubType.Cellular_HSPA: //3
+                    return CELL_3G;
+                case NetworkInterfaceSubType.WiFi:
+                    return WIFI;
+                case NetworkInterfaceSubType.Unknown:
+                case NetworkInterfaceSubType.Desktop_PassThru:
+                default:
+                    return UNKNOWN;
+            }
+        }
+
+        void ChangeDetected(object sender, NetworkNotificationEventArgs e)
+        {
+            switch (e.NotificationType)
+            {
+                case NetworkNotificationType.InterfaceConnected:
+                    updateConnectionType(checkConnectionType(e.NetworkInterface.InterfaceSubtype));
+                    break;
+                case NetworkNotificationType.InterfaceDisconnected:
+                    updateConnectionType(NONE);
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void updateConnectionType(string type)
+        {
+            // This should also implicitly fire offline/online events as that is handled on the JS side
+            if (this.HasCallback)
+            {
+                PluginResult result = new PluginResult(PluginResult.Status.OK, type);
+                result.KeepCallback = true;
+                DispatchCommandResult(result);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/Notification.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/Notification.cs b/lib/cordova-wp7/templates/standalone/Plugins/Notification.cs
new file mode 100644
index 0000000..11f14bd
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/Notification.cs
@@ -0,0 +1,367 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Devices;
+using System.Runtime.Serialization;
+using System.Threading;
+using System.Windows.Resources;
+using Microsoft.Phone.Controls;
+using Microsoft.Xna.Framework.Audio;
+using WPCordovaClassLib.Cordova.UI;
+using System.Diagnostics;
+
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public class Notification : BaseCommand
+    {
+        static ProgressBar progressBar = null;
+        const int DEFAULT_DURATION = 5;
+
+        private NotificationBox notifyBox;
+
+        private class NotifBoxData
+        {
+            public NotificationBox previous;
+            public string callbackId;
+        }
+
+        private PhoneApplicationPage Page
+        {
+            get
+            {
+                PhoneApplicationPage page = null;
+                PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                if (frame != null)
+                {
+                    page = frame.Content as PhoneApplicationPage;
+                }
+                return page;
+            }
+        }
+
+        // blink api - doesn't look like there is an equivalent api we can use...
+
+        [DataContract]
+        public class AlertOptions
+        {
+            [OnDeserializing]
+            public void OnDeserializing(StreamingContext context)
+            {
+                // set defaults
+                this.message = "message";
+                this.title = "Alert";
+                this.buttonLabel = "ok";
+            }
+
+            /// <summary>
+            /// message to display in the alert box
+            /// </summary>
+            [DataMember]
+            public string message;
+
+            /// <summary>
+            /// title displayed on the alert window
+            /// </summary>
+            [DataMember]
+            public string title;
+
+            /// <summary>
+            /// text to display on the button
+            /// </summary>
+            [DataMember]
+            public string buttonLabel;
+        }
+
+        public void alert(string options)
+        {
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+            AlertOptions alertOpts = new AlertOptions();
+            alertOpts.message = args[0];
+            alertOpts.title = args[1];
+            alertOpts.buttonLabel = args[2];
+            string aliasCurrentCommandCallbackId = args[3];
+
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                PhoneApplicationPage page = Page;
+                if (page != null)
+                {
+                    Grid grid = page.FindName("LayoutRoot") as Grid;
+                    if (grid != null)
+                    {
+                        var previous = notifyBox;
+                        notifyBox = new NotificationBox();
+                        notifyBox.Tag = new NotifBoxData{ previous = previous, callbackId = aliasCurrentCommandCallbackId };
+                        notifyBox.PageTitle.Text = alertOpts.title;
+                        notifyBox.SubTitle.Text = alertOpts.message;
+                        Button btnOK = new Button();
+                        btnOK.Content = alertOpts.buttonLabel;
+                        btnOK.Click += new RoutedEventHandler(btnOK_Click);
+                        btnOK.Tag = 1;
+                        notifyBox.ButtonPanel.Children.Add(btnOK);
+                        grid.Children.Add(notifyBox);
+
+                        if (previous == null)
+                        {
+                            page.BackKeyPress += page_BackKeyPress;
+                        }
+                    }
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.INSTANTIATION_EXCEPTION));
+                }
+            });
+        }
+
+        public void confirm(string options)
+        {
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+            AlertOptions alertOpts = new AlertOptions();
+            alertOpts.message = args[0];
+            alertOpts.title = args[1];
+            alertOpts.buttonLabel = args[2];
+            string aliasCurrentCommandCallbackId = args[3];
+
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                PhoneApplicationPage page = Page;
+                if (page != null)
+                {
+                    Grid grid = page.FindName("LayoutRoot") as Grid;
+                    if (grid != null)
+                    {
+                        var previous = notifyBox;
+                        notifyBox = new NotificationBox();
+                        notifyBox.Tag = new NotifBoxData{ previous = previous, callbackId = aliasCurrentCommandCallbackId };
+                        notifyBox.PageTitle.Text = alertOpts.title;
+                        notifyBox.SubTitle.Text = alertOpts.message;
+
+                        string[] labels = JSON.JsonHelper.Deserialize<string[]>(alertOpts.buttonLabel);
+
+                        if (labels == null)
+                        {
+                            labels = alertOpts.buttonLabel.Split(',');
+                        }
+
+                        for (int n = 0; n < labels.Length; n++)
+                        {
+                            Button btn = new Button();
+                            btn.Content = labels[n];
+                            btn.Tag = n;
+                            btn.Click += new RoutedEventHandler(btnOK_Click);
+                            notifyBox.ButtonPanel.Children.Add(btn);
+                        }
+
+                        grid.Children.Add(notifyBox);
+                        if (previous == null)
+                        {
+                            page.BackKeyPress += page_BackKeyPress;
+                        }
+                    }
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.INSTANTIATION_EXCEPTION));
+                }
+            });
+        }
+
+        void page_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
+        {
+            PhoneApplicationPage page = sender as PhoneApplicationPage;
+            string callbackId = "";
+            if (page != null && notifyBox != null)
+            {
+                Grid grid = page.FindName("LayoutRoot") as Grid;
+                if (grid != null)
+                {
+                    grid.Children.Remove(notifyBox);
+                    NotifBoxData notifBoxData = notifyBox.Tag as NotifBoxData;
+                    notifyBox = notifBoxData.previous;
+                    callbackId = notifBoxData.callbackId;
+                }
+                if (notifyBox == null)
+                {
+                    page.BackKeyPress -= page_BackKeyPress;
+                }
+                e.Cancel = true;
+            }
+
+            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, 0), callbackId);
+        }
+
+        void btnOK_Click(object sender, RoutedEventArgs e)
+        {
+            Button btn = sender as Button;
+            FrameworkElement notifBoxParent = null;
+            int retVal = 0;
+            string callbackId = "";
+            if (btn != null)
+            {
+                retVal = (int)btn.Tag + 1;
+
+                notifBoxParent = btn.Parent as FrameworkElement;
+                while ((notifBoxParent = notifBoxParent.Parent as FrameworkElement) != null &&
+                       !(notifBoxParent is NotificationBox)) ;
+            }
+            if (notifBoxParent != null)
+            {
+                PhoneApplicationPage page = Page;
+                if (page != null)
+                {
+                    Grid grid = page.FindName("LayoutRoot") as Grid;
+                    if (grid != null)
+                    {
+                        grid.Children.Remove(notifBoxParent);
+                    }
+
+                    NotifBoxData notifBoxData = notifBoxParent.Tag as NotifBoxData;
+                    notifyBox = notifBoxData.previous;
+                    callbackId = notifBoxData.callbackId;
+
+                    if (notifyBox == null)
+                    {
+                        page.BackKeyPress -= page_BackKeyPress;
+                    }
+                }
+
+            }
+            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, retVal),callbackId);
+        }
+
+
+
+        public void beep(string options)
+        {
+            string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+            int times = int.Parse(args[0]);
+
+            string resourcePath = BaseCommand.GetBaseURL() + "resources/notification-beep.wav";
+
+            StreamResourceInfo sri = Application.GetResourceStream(new Uri(resourcePath, UriKind.Relative));
+
+            if (sri != null)
+            {
+                SoundEffect effect = SoundEffect.FromStream(sri.Stream);
+                SoundEffectInstance inst = effect.CreateInstance();
+                ThreadPool.QueueUserWorkItem((o) =>
+                {
+                    // cannot interact with UI !!
+                    do
+                    {
+                        inst.Play();
+                        Thread.Sleep(effect.Duration + TimeSpan.FromMilliseconds(100));
+                    }
+                    while (--times > 0);
+
+                });
+
+            }
+
+            // TODO: may need a listener to trigger DispatchCommandResult after the alarm has finished executing...
+            DispatchCommandResult();
+        }
+
+        // Display an indeterminate progress indicator
+        public void activityStart(string unused)
+        {
+
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+
+                if (frame != null)
+                {
+                    PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+
+                    if (page != null)
+                    {
+                        var temp = page.FindName("LayoutRoot");
+                        Grid grid = temp as Grid;
+                        if (grid != null)
+                        {
+                            if (progressBar != null)
+                            {
+                                grid.Children.Remove(progressBar);
+                            }
+                            progressBar = new ProgressBar();
+                            progressBar.IsIndeterminate = true;
+                            progressBar.IsEnabled = true;
+
+                            grid.Children.Add(progressBar);
+                        }
+                    }
+                }
+            });
+        }
+
+
+        // Remove our indeterminate progress indicator
+        public void activityStop(string unused)
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                if (progressBar != null)
+                {
+                    progressBar.IsEnabled = false;
+                    PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                    if (frame != null)
+                    {
+                        PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+                        if (page != null)
+                        {
+                            Grid grid = page.FindName("LayoutRoot") as Grid;
+                            if (grid != null)
+                            {
+                                grid.Children.Remove(progressBar);
+                            }
+                        }
+                    }
+                    progressBar = null;
+                }
+            });
+        }
+
+        public void vibrate(string vibrateDuration)
+        {
+
+            int msecs = 200; // set default
+
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(vibrateDuration);
+
+                msecs = int.Parse(args[0]);
+                if (msecs < 1)
+                {
+                    msecs = 1;
+                }
+            }
+            catch (FormatException)
+            {
+
+            }
+
+            VibrateController.Default.Start(TimeSpan.FromMilliseconds(msecs));
+
+            // TODO: may need to add listener to trigger DispatchCommandResult when the vibration ends...
+            DispatchCommandResult();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioCaptureTask.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioCaptureTask.cs b/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioCaptureTask.cs
new file mode 100644
index 0000000..9f43d23
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioCaptureTask.cs
@@ -0,0 +1,107 @@
+/*  
+	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.
+*/
+
+using System;
+using System.IO;
+using System.Windows;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    /// <summary>
+    /// Allows an application to launch the Audio Recording application. 
+    /// Use this to allow users to record audio from your application.
+    /// </summary>
+    public class AudioCaptureTask
+    {
+        /// <summary>
+        /// Represents recorded audio returned from a call to the Show method of
+        /// a WPCordovaClassLib.Cordova.Controls.AudioCaptureTask object
+        /// </summary>
+        public class AudioResult : TaskEventArgs
+        {
+            /// <summary>
+            /// Initializes a new instance of the AudioResult class.
+            /// </summary>
+            public AudioResult()
+            { }
+
+            /// <summary>
+            /// Initializes a new instance of the AudioResult class
+            /// with the specified Microsoft.Phone.Tasks.TaskResult.
+            /// </summary>
+            /// <param name="taskResult">Associated Microsoft.Phone.Tasks.TaskResult</param>
+            public AudioResult(TaskResult taskResult)
+                : base(taskResult)
+            { }
+
+            /// <summary>
+            ///  Gets the file name of the recorded audio.
+            /// </summary>
+            public Stream AudioFile { get; internal set; }
+
+            /// <summary>
+            /// Gets the stream containing the data for the recorded audio.
+            /// </summary>
+            public string AudioFileName { get; internal set; }
+        }
+
+        /// <summary>
+        /// Occurs when a audio recording task is completed.
+        /// </summary>
+        public event EventHandler<AudioResult> Completed;
+
+        /// <summary>
+        /// Shows Audio Recording application
+        /// </summary>
+        public void Show()
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                var root = Application.Current.RootVisual as PhoneApplicationFrame;
+
+                root.Navigated += new System.Windows.Navigation.NavigatedEventHandler(NavigationService_Navigated);
+
+                string baseUrl = WPCordovaClassLib.Cordova.Commands.BaseCommand.GetBaseURL();
+                // dummy parameter is used to always open a fresh version
+                root.Navigate(new System.Uri(baseUrl + "CordovaLib/UI/AudioRecorder.xaml?dummy=" + Guid.NewGuid().ToString(), UriKind.Relative));
+
+            });
+        }
+
+        /// <summary>
+        /// Performs additional configuration of the recording application.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void NavigationService_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            if (!(e.Content is AudioRecorder)) return;
+
+            (Application.Current.RootVisual as PhoneApplicationFrame).Navigated -= NavigationService_Navigated;
+
+            AudioRecorder audioRecorder = (AudioRecorder)e.Content;
+
+            if (audioRecorder != null)
+            {
+                audioRecorder.Completed += this.Completed;
+            }
+            else if (this.Completed != null)
+            {
+                this.Completed(this, new AudioResult(TaskResult.Cancel));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioRecorder.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioRecorder.xaml b/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioRecorder.xaml
new file mode 100644
index 0000000..0fd26ab
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioRecorder.xaml
@@ -0,0 +1,66 @@
+<!--
+ 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. 
+-->
+<phone:PhoneApplicationPage 
+    x:Class="WPCordovaClassLib.Cordova.UI.AudioRecorder"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    SupportedOrientations="Portrait" Orientation="Portrait"
+    mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
+    shell:SystemTray.IsVisible="True">
+
+    <!--LayoutRoot is the root grid where all page content is placed-->
+    <Grid x:Name="LayoutRoot" Background="Transparent">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+
+        <!--TitlePanel contains the name of the application and page title-->
+        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="0,17,0,28">
+            <TextBlock x:Name="PageTitle" Text="Audio recorder" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
+        </StackPanel>
+
+        <!--ContentPanel - place additional content here-->
+        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
+            <Button Name="btnStartStop" Content="Start" Height="72" HorizontalAlignment="Left" Margin="156,96,0,0"  VerticalAlignment="Top" Width="160" Click="btnStartStop_Click" />
+            <Button Name="btnTake" Content="Take" IsEnabled="False" Height="72" HorizontalAlignment="Left" Margin="155,182,0,0" VerticalAlignment="Top" Width="160" Click="btnTake_Click" />
+            <TextBlock Height="30" HorizontalAlignment="Left" Margin="168,60,0,0" Name="txtDuration" Text="Duration: 00:00" VerticalAlignment="Top" />
+        </Grid>
+    </Grid>
+ 
+    <!--Sample code showing usage of ApplicationBar-->
+    <!--<phone:PhoneApplicationPage.ApplicationBar>
+        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
+            <shell:ApplicationBar.MenuItems>
+                <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
+                <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
+            </shell:ApplicationBar.MenuItems>
+        </shell:ApplicationBar>
+    </phone:PhoneApplicationPage.ApplicationBar>-->
+
+</phone:PhoneApplicationPage>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioRecorder.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioRecorder.xaml.cs b/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioRecorder.xaml.cs
new file mode 100644
index 0000000..bc8ba6f
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/UI/AudioRecorder.xaml.cs
@@ -0,0 +1,306 @@
+/*  
+	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.
+*/
+
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using System;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Windows;
+using System.Windows.Threading;
+using WPCordovaClassLib.Cordova.Commands;
+using AudioResult = WPCordovaClassLib.Cordova.UI.AudioCaptureTask.AudioResult;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    /// <summary>
+    /// Implements Audio Recording application
+    /// </summary>
+    public partial class AudioRecorder : PhoneApplicationPage
+    {
+
+        #region Constants
+
+        private const string RecordingStartCaption = "Start";
+        private const string RecordingStopCaption = "Stop";
+
+        private const string LocalFolderName = "AudioCache";
+        private const string FileNameFormat = "Audio-{0}.wav";
+
+        #endregion
+
+        #region Callbacks
+
+        /// <summary>
+        /// Occurs when a audio recording task is completed.
+        /// </summary>
+        public event EventHandler<AudioResult> Completed;
+
+        #endregion
+
+        #region Fields
+
+        /// <summary>
+        /// Audio source
+        /// </summary>
+        private Microphone microphone;
+
+        /// <summary>
+        /// Temporary buffer to store audio chunk
+        /// </summary>
+        private byte[] buffer;
+
+        /// <summary>
+        /// Recording duration
+        /// </summary>
+        private TimeSpan duration;
+
+        /// <summary>
+        /// Output buffer
+        /// </summary>
+        private MemoryStream memoryStream;
+
+        /// <summary>
+        /// Xna game loop dispatcher
+        /// </summary>
+        DispatcherTimer dtXna;
+
+        /// <summary>
+        /// Recording result, dispatched back when recording page is closed
+        /// </summary>
+        private AudioResult result = new AudioResult(TaskResult.Cancel);
+
+        /// <summary>
+        /// Whether we are recording audio now
+        /// </summary>
+        private bool IsRecording
+        {
+            get
+            {
+                return (this.microphone != null && this.microphone.State == MicrophoneState.Started);
+            }
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Creates new instance of the AudioRecorder class.
+        /// </summary>
+        public AudioRecorder()
+        {
+
+            this.InitializeXnaGameLoop();
+
+            // microphone requires special XNA initialization to work
+            InitializeComponent();
+        }
+
+        /// <summary>
+        /// Starts recording, data is stored in memory
+        /// </summary>
+        private void StartRecording()
+        {
+            this.microphone = Microphone.Default;
+            this.microphone.BufferDuration = TimeSpan.FromMilliseconds(500);
+
+            this.btnTake.IsEnabled = false;
+            this.btnStartStop.Content = RecordingStopCaption;
+
+            this.buffer = new byte[microphone.GetSampleSizeInBytes(this.microphone.BufferDuration)];
+            this.microphone.BufferReady += new EventHandler<EventArgs>(MicrophoneBufferReady);
+
+            this.memoryStream = new MemoryStream();
+            this.memoryStream.InitializeWavStream(this.microphone.SampleRate);
+
+            this.duration = new TimeSpan(0);
+
+            this.microphone.Start();
+        }
+
+        /// <summary>
+        /// Stops recording
+        /// </summary>
+        private void StopRecording()
+        {
+            this.microphone.Stop();
+
+            this.microphone.BufferReady -= MicrophoneBufferReady;
+
+            this.microphone = null;
+
+            btnStartStop.Content = RecordingStartCaption;
+
+            // check there is some data
+            this.btnTake.IsEnabled = true;
+        }
+
+        /// <summary>
+        /// Handles Start/Stop events
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnStartStop_Click(object sender, RoutedEventArgs e)
+        {
+
+            if (this.IsRecording)
+            {
+                this.StopRecording();
+            }
+            else
+            {
+                this.StartRecording();
+            }
+        }
+
+        /// <summary>
+        /// Handles Take button click
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnTake_Click(object sender, RoutedEventArgs e)
+        {
+            this.result = this.SaveAudioClipToLocalStorage();
+
+            if (Completed != null)
+            {
+                Completed(this, result);
+            }
+
+            if (this.NavigationService.CanGoBack)
+            {
+                this.NavigationService.GoBack();
+            }
+        }
+
+        /// <summary>
+        /// Handles page closing event, stops recording if needed and dispatches results.
+        /// </summary>
+        /// <param name="e"></param>
+        protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
+        {
+            if (IsRecording)
+            {
+                StopRecording();
+            }
+
+            this.FinalizeXnaGameLoop();
+
+            base.OnNavigatedFrom(e);
+        }
+
+        /// <summary>
+        /// Copies data from microphone to memory storages and updates recording state
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void MicrophoneBufferReady(object sender, EventArgs e)
+        {
+            this.microphone.GetData(this.buffer);
+            this.memoryStream.Write(this.buffer, 0, this.buffer.Length);
+            TimeSpan bufferDuration = this.microphone.BufferDuration;
+
+            this.Dispatcher.BeginInvoke(() =>
+            {
+                this.duration += bufferDuration;
+
+                this.txtDuration.Text = "Duration: " +
+                    this.duration.Minutes.ToString().PadLeft(2, '0') + ":" +
+                    this.duration.Seconds.ToString().PadLeft(2, '0');
+            });
+
+        }
+
+        /// <summary>
+        /// Writes audio data from memory to isolated storage
+        /// </summary>
+        /// <returns></returns>
+        private AudioResult SaveAudioClipToLocalStorage()
+        {
+            if (this.memoryStream == null || this.memoryStream.Length <= 0)
+            {
+                return new AudioResult(TaskResult.Cancel);
+            }
+
+            this.memoryStream.UpdateWavStream();
+
+            // save audio data to local isolated storage
+
+            string filename = String.Format(FileNameFormat, Guid.NewGuid().ToString());
+
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+
+                    if (!isoFile.DirectoryExists(LocalFolderName))
+                    {
+                        isoFile.CreateDirectory(LocalFolderName);
+                    }
+
+                    string filePath = System.IO.Path.Combine("/" + LocalFolderName + "/", filename);
+
+                    this.memoryStream.Seek(0, SeekOrigin.Begin);
+
+                    using (IsolatedStorageFileStream fileStream = isoFile.CreateFile(filePath))
+                    {
+
+                        this.memoryStream.CopyTo(fileStream);
+                    }
+
+                    AudioResult result = new AudioResult(TaskResult.OK);
+                    result.AudioFileName = filePath;
+
+                    result.AudioFile = this.memoryStream;
+                    result.AudioFile.Seek(0, SeekOrigin.Begin);
+
+                    return result;
+                }
+
+
+
+            }
+            catch (Exception)
+            {
+                //TODO: log or do something else
+                throw;
+            }
+        }
+
+        /// <summary>
+        /// Special initialization required for the microphone: XNA game loop
+        /// </summary>
+        private void InitializeXnaGameLoop()
+        {
+            // Timer to simulate the XNA game loop (Microphone is from XNA)
+            this.dtXna = new DispatcherTimer();
+            this.dtXna.Interval = TimeSpan.FromMilliseconds(33);
+            this.dtXna.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
+            this.dtXna.Start();
+        }
+        /// <summary>
+        /// Finalizes XNA game loop for microphone
+        /// </summary>
+        private void FinalizeXnaGameLoop()
+        {
+            // Timer to simulate the XNA game loop (Microphone is from XNA)
+            if (dtXna != null)
+            {
+                dtXna.Stop();
+                dtXna = null;
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/UI/ImageCapture.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/UI/ImageCapture.xaml b/lib/cordova-wp7/templates/standalone/Plugins/UI/ImageCapture.xaml
new file mode 100644
index 0000000..a7eee21
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/UI/ImageCapture.xaml
@@ -0,0 +1,26 @@
+<phone:PhoneApplicationPage 
+    x:Class="WPCordovaClassLib.Cordova.UI.ImageCapture"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
+    mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
+    shell:SystemTray.IsVisible="True">
+
+    <!--LayoutRoot is the root grid where all page content is placed-->
+    <Grid x:Name="LayoutRoot" Background="Yellow">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+
+    </Grid>
+ 
+
+</phone:PhoneApplicationPage>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/UI/ImageCapture.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/UI/ImageCapture.xaml.cs b/lib/cordova-wp7/templates/standalone/Plugins/UI/ImageCapture.xaml.cs
new file mode 100644
index 0000000..234b444
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/UI/ImageCapture.xaml.cs
@@ -0,0 +1,109 @@
+/*  
+	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.
+*/
+
+
+using System;
+using System.IO;
+using System.Windows;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    public partial class ImageCapture : PhoneApplicationPage
+    {
+        public ImageCapture()
+        {
+            InitializeComponent();
+        }
+    }
+
+    public class ImageCaptureTask
+    {
+        /// <summary>
+        /// Represents an image returned from a call to the Show method of
+        /// a WPCordovaClassLib.Cordova.Controls.ImageCaptureTask object
+        /// </summary>
+        //public class AudioResult : TaskEventArgs
+        //{
+        //    /// <summary>
+        //    /// Initializes a new instance of the AudioResult class.
+        //    /// </summary>
+        //    public AudioResult()
+        //    { }
+
+        //    /// <summary>
+        //    /// Initializes a new instance of the AudioResult class
+        //    /// with the specified Microsoft.Phone.Tasks.TaskResult.
+        //    /// </summary>
+        //    /// <param name="taskResult">Associated Microsoft.Phone.Tasks.TaskResult</param>
+        //    public AudioResult(TaskResult taskResult)
+        //        : base(taskResult)
+        //    { }
+
+        //    /// <summary>
+        //    ///  Gets the file name of the recorded audio.
+        //    /// </summary>
+        //    public Stream AudioFile { get; internal set; }
+
+        //    /// <summary>
+        //    /// Gets the stream containing the data for the recorded audio.
+        //    /// </summary>
+        //    public string AudioFileName { get; internal set; }
+        //}
+
+        ///// <summary>
+        ///// Occurs when a audio recording task is completed.
+        ///// </summary>
+        //public event EventHandler<AudioResult> Completed;
+
+        /// <summary>
+        /// Shows Audio Recording application
+        /// </summary>
+        public void Show()
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                var root = Application.Current.RootVisual as PhoneApplicationFrame;
+
+                root.Navigated += new System.Windows.Navigation.NavigatedEventHandler(NavigationService_Navigated);
+
+                string baseUrl = WPCordovaClassLib.Cordova.Commands.BaseCommand.GetBaseURL();
+
+                // dummy parameter is used to always open a fresh version
+                root.Navigate(new System.Uri(baseUrl + "Cordova/UI/ImageCapture.xaml?dummy=" + Guid.NewGuid().ToString(), UriKind.Relative));
+            });
+        }
+
+        /// <summary>
+        /// Performs additional configuration of the recording application.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void NavigationService_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            ImageCapture imageCapture = e.Content as ImageCapture;
+            if (imageCapture != null)
+            {
+                (Application.Current.RootVisual as PhoneApplicationFrame).Navigated -= NavigationService_Navigated;
+
+                //imageCapture.Completed += this.Completed;
+                //else if (this.Completed != null)
+                //{
+                //    this.Completed(this, new AudioResult(TaskResult.Cancel));
+                //}
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/UI/NotificationBox.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/UI/NotificationBox.xaml b/lib/cordova-wp7/templates/standalone/Plugins/UI/NotificationBox.xaml
new file mode 100644
index 0000000..1ca5d5f
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/UI/NotificationBox.xaml
@@ -0,0 +1,62 @@
+<!--
+ 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. 
+-->
+<UserControl x:Class="WPCordovaClassLib.Cordova.UI.NotificationBox"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    d:DesignHeight="800" d:DesignWidth="480" VerticalAlignment="Stretch">
+
+    <Grid x:Name="LayoutRoot" 
+          Background="{StaticResource PhoneSemitransparentBrush}" VerticalAlignment="Stretch">
+        
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+        
+
+        <!--TitlePanel contains the name of the application and page title-->
+        <StackPanel x:Name="TitlePanel" 
+                    Grid.Row="0" 
+                    Background="{StaticResource PhoneSemitransparentBrush}">
+            <TextBlock x:Name="PageTitle" 
+                       Text="Title" 
+                       Margin="10,10" 
+                       Style="{StaticResource PhoneTextTitle2Style}"/>
+            
+            <TextBlock x:Name="SubTitle" 
+                       Text="Subtitle" 
+                       TextWrapping="Wrap"
+                       Margin="10,10"
+                       Style="{StaticResource PhoneTextTitle3Style}"/>
+            
+            <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">       
+            <StackPanel x:Name="ButtonPanel"
+                        Margin="10,10"
+                        Orientation="Horizontal"/>
+            </ScrollViewer>
+
+        </StackPanel>
+    </Grid>
+</UserControl>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/UI/NotificationBox.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/UI/NotificationBox.xaml.cs b/lib/cordova-wp7/templates/standalone/Plugins/UI/NotificationBox.xaml.cs
new file mode 100644
index 0000000..50b2f2a
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/UI/NotificationBox.xaml.cs
@@ -0,0 +1,41 @@
+/*
+ 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. 
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    public partial class NotificationBox : UserControl
+    {
+        public NotificationBox()
+        {
+            InitializeComponent();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoCaptureTask.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoCaptureTask.cs b/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoCaptureTask.cs
new file mode 100644
index 0000000..def2a88
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoCaptureTask.cs
@@ -0,0 +1,105 @@
+/*  
+	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.
+*/
+
+using System;
+using System.IO;
+using System.Windows;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    /// <summary>
+    /// Allows an application to launch the Video Recording application. 
+    /// Use this to allow users to record video from your application.
+    /// </summary>
+    public class VideoCaptureTask
+    {
+        /// <summary>
+        /// Represents recorded video returned from a call to the Show method of
+        /// a WPCordovaClassLib.Cordova.Controls.VideoCaptureTask object
+        /// </summary>
+        public class VideoResult : TaskEventArgs
+        {
+            /// <summary>
+            /// Initializes a new instance of the VideoResult class.
+            /// </summary>
+            public VideoResult()
+            { }
+
+            /// <summary>
+            /// Initializes a new instance of the VideoResult class
+            /// with the specified Microsoft.Phone.Tasks.TaskResult.
+            /// </summary>
+            /// <param name="taskResult">Associated Microsoft.Phone.Tasks.TaskResult</param>
+            public VideoResult(TaskResult taskResult)
+                : base(taskResult)
+            { }
+
+            /// <summary>
+            ///  Gets the file name of the recorded Video.
+            /// </summary>
+            public Stream VideoFile { get; internal set; }
+
+            /// <summary>
+            /// Gets the stream containing the data for the recorded Video.
+            /// </summary>
+            public string VideoFileName { get; internal set; }
+        }
+
+        /// <summary>
+        /// Occurs when a Video recording task is completed.
+        /// </summary>
+        public event EventHandler<VideoResult> Completed;
+
+        /// <summary>
+        /// Shows Video Recording application
+        /// </summary>
+        public void Show()
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                var root = Application.Current.RootVisual as PhoneApplicationFrame;
+
+                root.Navigated += new System.Windows.Navigation.NavigatedEventHandler(NavigationService_Navigated);
+
+                string baseUrl = WPCordovaClassLib.Cordova.Commands.BaseCommand.GetBaseURL();
+                // dummy parameter is used to always open a fresh version
+                root.Navigate(new System.Uri(baseUrl + "CordovaLib/UI/VideoRecorder.xaml?dummy=" + Guid.NewGuid().ToString(), UriKind.Relative));
+            });
+        }
+
+        /// <summary>
+        /// Performs additional configuration of the recording application.
+        /// </summary>
+        private void NavigationService_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            if (!(e.Content is VideoRecorder)) return;
+
+            (Application.Current.RootVisual as PhoneApplicationFrame).Navigated -= NavigationService_Navigated;
+
+            VideoRecorder VideoRecorder = (VideoRecorder)e.Content;
+
+            if (VideoRecorder != null)
+            {
+                VideoRecorder.Completed += this.Completed;
+            }
+            else if (this.Completed != null)
+            {
+                this.Completed(this, new VideoResult(TaskResult.Cancel));
+            }
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoRecorder.xaml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoRecorder.xaml b/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoRecorder.xaml
new file mode 100644
index 0000000..c78fdb0
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoRecorder.xaml
@@ -0,0 +1,52 @@
+<!--
+ 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. 
+-->
+<phone:PhoneApplicationPage 
+    x:Class="WPCordovaClassLib.Cordova.UI.VideoRecorder"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="480"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    SupportedOrientations="Landscape" Orientation="LandscapeLeft"
+    shell:SystemTray.IsVisible="False">
+   
+    <Canvas x:Name="LayoutRoot" Background="Transparent" Grid.ColumnSpan="1" Grid.Column="0">
+
+        <Rectangle 
+            x:Name="viewfinderRectangle"
+            Width="640" 
+            Height="480" 
+            HorizontalAlignment="Left" 
+            Canvas.Left="80"/>
+        
+    </Canvas>
+
+    <phone:PhoneApplicationPage.ApplicationBar>
+        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" x:Name="PhoneAppBar" Opacity="0.0">
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar.feature.video.rest.png" Text="Record"  x:Name="btnStartRecording" Click="StartRecording_Click" />
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar.save.rest.png" Text="Take" x:Name="btnTakeVideo" Click="TakeVideo_Click"/>            
+        </shell:ApplicationBar>
+    </phone:PhoneApplicationPage.ApplicationBar>
+
+</phone:PhoneApplicationPage>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoRecorder.xaml.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoRecorder.xaml.cs b/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoRecorder.xaml.cs
new file mode 100644
index 0000000..6ab1cc3
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/UI/VideoRecorder.xaml.cs
@@ -0,0 +1,405 @@
+/*  
+	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.
+*/
+
+using System;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Windows.Media;
+using System.Windows.Navigation;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Shell;
+using Microsoft.Phone.Tasks;
+using VideoResult = WPCordovaClassLib.Cordova.UI.VideoCaptureTask.VideoResult;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    public partial class VideoRecorder : PhoneApplicationPage
+    {
+
+        #region Constants
+
+        /// <summary>
+        /// Caption for record button in ready state
+        /// </summary>
+        private const string RecordingStartCaption = "Record";
+
+        /// <summary>
+        /// Caption for record button in recording state
+        /// </summary>
+        private const string RecordingStopCaption = "Stop";
+
+        /// <summary>
+        /// Start record icon URI
+        /// </summary>
+        private const string StartIconUri = "/Images/appbar.feature.video.rest.png";
+
+        /// <summary>
+        /// Stop record icon URI
+        /// </summary>
+        private const string StopIconUri = "/Images/appbar.stop.rest.png";
+
+        /// <summary>
+        /// Folder to save video clips
+        /// </summary>
+        private const string LocalFolderName = "VideoCache";
+
+        /// <summary>
+        /// File name format
+        /// </summary>
+        private const string FileNameFormat = "Video-{0}.mp4";
+
+        /// <summary>
+        /// Temporary file name
+        /// </summary>
+        private const string defaultFileName = "NewVideoFile.mp4";
+
+        #endregion
+
+        #region Callbacks
+        /// <summary>
+        /// Occurs when a video recording task is completed.
+        /// </summary>
+        public event EventHandler<VideoResult> Completed;
+
+        #endregion
+
+        #region Fields
+
+        /// <summary>
+        /// Viewfinder for capturing video
+        /// </summary>
+        private VideoBrush videoRecorderBrush;
+
+        /// <summary>
+        /// Path to save video clip
+        /// </summary>
+        private string filePath;
+
+        /// <summary>
+        /// Source for capturing video. 
+        /// </summary>
+        private CaptureSource captureSource;
+
+        /// <summary>
+        /// Video device
+        /// </summary>
+        private VideoCaptureDevice videoCaptureDevice;
+
+        /// <summary>
+        /// File sink so save recording video in Isolated Storage
+        /// </summary>
+        private FileSink fileSink;
+
+        /// <summary>
+        /// For managing button and application state 
+        /// </summary>
+        private enum VideoState { Initialized, Ready, Recording, CameraNotSupported };
+
+        /// <summary>
+        /// Current video state
+        /// </summary>
+        private VideoState currentVideoState;
+
+        /// <summary>
+        /// Stream to return result
+        /// </summary>
+        private MemoryStream memoryStream;
+
+        /// <summary>
+        /// Recording result, dispatched back when recording page is closed
+        /// </summary>
+        private VideoResult result = new VideoResult(TaskResult.Cancel);
+
+        #endregion
+
+        /// <summary>
+        /// Initializes components
+        /// </summary>
+        public VideoRecorder()
+        {
+            InitializeComponent();
+
+            PhoneAppBar = (ApplicationBar)ApplicationBar;
+            PhoneAppBar.IsVisible = true;
+            btnStartRecording = ((ApplicationBarIconButton)ApplicationBar.Buttons[0]);
+            btnTakeVideo = ((ApplicationBarIconButton)ApplicationBar.Buttons[1]);
+        }
+
+        /// <summary>
+        /// Initializes the video recorder then page is loading
+        /// </summary>
+        protected override void OnNavigatedTo(NavigationEventArgs e)
+        {
+            base.OnNavigatedTo(e);
+            this.InitializeVideoRecorder();
+        }
+
+        /// <summary>
+        /// Disposes camera and media objects then leave the page
+        /// </summary>
+        protected override void OnNavigatedFrom(NavigationEventArgs e)
+        {
+            this.DisposeVideoRecorder();
+
+            if (this.Completed != null)
+            {
+                this.Completed(this, result);
+            }
+            base.OnNavigatedFrom(e);
+        }
+
+        /// <summary>
+        /// Handles TakeVideo button click
+        /// </summary>
+        private void TakeVideo_Click(object sender, EventArgs e)
+        {
+            this.result = this.SaveVideoClip();
+            this.NavigateBack();
+        }
+
+        private void NavigateBack()
+        {
+            if (this.NavigationService.CanGoBack)
+            {
+                this.NavigationService.GoBack();
+            }
+        }
+
+        /// <summary>
+        /// Resaves video clip from temporary directory to persistent 
+        /// </summary>
+        private VideoResult SaveVideoClip()
+        {
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    if (string.IsNullOrEmpty(filePath) || (!isoFile.FileExists(filePath)))
+                    {
+                        return new VideoResult(TaskResult.Cancel);
+                    }
+
+                    string fileName = String.Format(FileNameFormat, Guid.NewGuid().ToString());
+                    string newPath = Path.Combine("/" + LocalFolderName + "/", fileName);
+                    isoFile.CopyFile(filePath, newPath);
+                    isoFile.DeleteFile(filePath);
+
+                    memoryStream = new MemoryStream();
+                    using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(newPath, FileMode.Open, isoFile))
+                    {
+                        fileStream.CopyTo(memoryStream);
+                    }
+
+                    VideoResult result = new VideoResult(TaskResult.OK);
+                    result.VideoFileName = newPath;
+                    result.VideoFile = this.memoryStream;
+                    result.VideoFile.Seek(0, SeekOrigin.Begin);
+                    return result;
+                }
+
+            }
+            catch (Exception)
+            {
+                return new VideoResult(TaskResult.None);
+            }
+        }
+
+        /// <summary>
+        /// Updates the buttons on the UI thread based on current state. 
+        /// </summary>
+        /// <param name="currentState">current UI state</param>
+        private void UpdateUI(VideoState currentState)
+        {
+            Dispatcher.BeginInvoke(delegate
+            {
+                switch (currentState)
+                {
+                    case VideoState.CameraNotSupported:
+                        btnStartRecording.IsEnabled = false;
+                        btnTakeVideo.IsEnabled = false;
+                        break;
+
+                    case VideoState.Initialized:
+                        btnStartRecording.Text = RecordingStartCaption;
+                        btnStartRecording.IconUri = new Uri(StartIconUri, UriKind.Relative);
+                        btnTakeVideo.IsEnabled = false;
+                        break;
+
+                    case VideoState.Ready:
+                        btnStartRecording.Text = RecordingStartCaption;
+                        btnStartRecording.IconUri = new Uri(StartIconUri, UriKind.Relative);
+                        btnTakeVideo.IsEnabled = true;
+                        break;
+
+                    case VideoState.Recording:
+                        btnStartRecording.Text = RecordingStopCaption;
+                        btnStartRecording.IconUri = new Uri(StopIconUri, UriKind.Relative);
+                        btnTakeVideo.IsEnabled = false;
+                        break;
+
+                    default:
+                        break;
+                }
+                currentVideoState = currentState;
+            });
+        }
+
+        /// <summary>
+        /// Initializes VideoRecorder
+        /// </summary>
+        public void InitializeVideoRecorder()
+        {
+            if (captureSource == null)
+            {
+                captureSource = new CaptureSource();
+                fileSink = new FileSink();
+                videoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
+
+                if (videoCaptureDevice != null)
+                {
+                    videoRecorderBrush = new VideoBrush();
+                    videoRecorderBrush.SetSource(captureSource);
+                    viewfinderRectangle.Fill = videoRecorderBrush;
+                    captureSource.Start();
+                    this.UpdateUI(VideoState.Initialized);
+                }
+                else
+                {
+                    this.UpdateUI(VideoState.CameraNotSupported);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Sets recording state: start recording 
+        /// </summary>
+        private void StartVideoRecording()
+        {
+            try
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Started))
+                {
+                    captureSource.Stop();
+                    fileSink.CaptureSource = captureSource;
+                    filePath = System.IO.Path.Combine("/" + LocalFolderName + "/", defaultFileName);
+
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (!isoFile.DirectoryExists(LocalFolderName))
+                        {
+                            isoFile.CreateDirectory(LocalFolderName);
+                        }
+
+                        if (isoFile.FileExists(filePath))
+                        {
+                            isoFile.DeleteFile(filePath);
+                        }
+                    }
+
+                    fileSink.IsolatedStorageFileName = filePath;
+                }
+
+                if (captureSource.VideoCaptureDevice != null
+                    && captureSource.State == CaptureState.Stopped)
+                {
+                    captureSource.Start();
+                }
+                this.UpdateUI(VideoState.Recording);
+            }
+            catch (Exception)
+            {
+                this.result = new VideoResult(TaskResult.None);
+                this.NavigateBack();
+            }
+        }
+
+        /// <summary>
+        /// Sets the recording state: stop recording
+        /// </summary>
+        private void StopVideoRecording()
+        {
+            try
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Started))
+                {
+                    captureSource.Stop();
+                    fileSink.CaptureSource = null;
+                    fileSink.IsolatedStorageFileName = null;
+                    this.StartVideoPreview();
+                }
+            }
+            catch (Exception)
+            {
+                this.result = new VideoResult(TaskResult.None);
+                this.NavigateBack();
+            }
+        }
+
+        /// <summary>
+        /// Sets the recording state: display the video on the viewfinder. 
+        /// </summary>
+        private void StartVideoPreview()
+        {
+            try
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Stopped))
+                {
+                    videoRecorderBrush.SetSource(captureSource);
+                    viewfinderRectangle.Fill = videoRecorderBrush;
+                    captureSource.Start();
+                    this.UpdateUI(VideoState.Ready);
+                }
+            }
+            catch (Exception)
+            {
+                this.result = new VideoResult(TaskResult.None);
+                this.NavigateBack();
+            }
+        }
+
+        /// <summary>
+        /// Starts video recording 
+        /// </summary>
+        private void StartRecording_Click(object sender, EventArgs e)
+        {
+            if (currentVideoState == VideoState.Recording)
+            {
+                this.StopVideoRecording();
+            }
+            else
+            {
+                this.StartVideoRecording();
+            }
+        }
+
+        /// <summary>
+        /// Releases resources
+        /// </summary>
+        private void DisposeVideoRecorder()
+        {
+            if (captureSource != null)
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Started))
+                {
+                    captureSource.Stop();
+                }
+                captureSource = null;
+                videoCaptureDevice = null;
+                fileSink = null;
+                videoRecorderBrush = null;
+            }
+        }
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Properties/AppManifest.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Properties/AppManifest.xml b/lib/cordova-wp7/templates/standalone/Properties/AppManifest.xml
new file mode 100644
index 0000000..877ea4b
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Properties/AppManifest.xml
@@ -0,0 +1,6 @@
+<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+>
+  <Deployment.Parts>
+  </Deployment.Parts>
+</Deployment>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Properties/AssemblyInfo.cs b/lib/cordova-wp7/templates/standalone/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..3f5dc5a
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Properties/AssemblyInfo.cs
@@ -0,0 +1,38 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Resources;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CordovaAppProj")]
+[assembly: AssemblyDescription("2.0.0.0")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Apache Cordova")]
+[assembly: AssemblyProduct("CordovaAppProj")]
+[assembly: AssemblyCopyright("Copyright © Apache Cordova 2013")]
+[assembly: AssemblyTrademark("Apache Cordova")]
+[assembly: AssemblyCulture("")]
+
+[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("9e27b972-0825-4386-ba17-63c695262c3d")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Properties/WMAppManifest.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Properties/WMAppManifest.xml b/lib/cordova-wp7/templates/standalone/Properties/WMAppManifest.xml
new file mode 100644
index 0000000..96332e1
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Properties/WMAppManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2009/deployment" AppPlatformVersion="7.1">
+  <App xmlns="" ProductID="{$guid1$}" Title="$safeprojectname$" 
+       RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal"  
+       Author="$safeprojectname$ author" 
+       BitsPerPixel="32" 
+       Description="Apache Cordova for Windows Phone 7"
+       Publisher="$safeprojectname$">
+    
+    <IconPath IsRelative="true" IsResource="false">ApplicationIcon.png</IconPath>
+    <Capabilities>
+      <Capability Name="ID_CAP_IDENTITY_DEVICE" />
+      <Capability Name="ID_CAP_IDENTITY_USER" />
+      <Capability Name="ID_CAP_LOCATION" />
+      <Capability Name="ID_CAP_NETWORKING" />
+      <Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
+      <Capability Name="ID_CAP_APPOINTMENTS"/>
+      <Capability Name="ID_CAP_CONTACTS"/>
+      <Capability Name="ID_CAP_ISV_CAMERA"/>
+      <Capability Name="ID_CAP_MEDIALIB"/>
+      <Capability Name="ID_CAP_MICROPHONE"/>
+      <Capability Name="ID_CAP_PHONEDIALER"/>
+      <Capability Name="ID_CAP_PUSH_NOTIFICATION"/>
+      <Capability Name="ID_CAP_SENSORS"/>
+      <Capability Name="ID_HW_FRONTCAMERA"/>      
+    </Capabilities>
+    
+    <Tasks>
+      <DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
+    </Tasks>
+    <Tokens>
+      <PrimaryToken TokenID="$safeprojectname$Token" TaskName="_default">
+        <TemplateType5>
+          <BackgroundImageURI IsRelative="true" IsResource="false">Background.png</BackgroundImageURI>
+          <Count>0</Count>
+          <Title>$safeprojectname$</Title>
+        </TemplateType5>
+      </PrimaryToken>
+    </Tokens>
+  </App>
+</Deployment>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/README.md
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/README.md b/lib/cordova-wp7/templates/standalone/README.md
new file mode 100644
index 0000000..6374253
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/README.md
@@ -0,0 +1,13 @@
+Project Template
+===
+
+This project template needs to be built to be installed.
+
+
+This template includes the full source code for Windows Phone 7
+
+
+In order to build this template in Visual Studio Express for Windows Phone 7 :
+
+1. Open the solution file in Visual Studio
+2. Choose File->Export Template and follow the directions.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/SplashScreenImage.jpg
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/SplashScreenImage.jpg b/lib/cordova-wp7/templates/standalone/SplashScreenImage.jpg
new file mode 100644
index 0000000..d35501d
Binary files /dev/null and b/lib/cordova-wp7/templates/standalone/SplashScreenImage.jpg differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/VERSION
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/VERSION b/lib/cordova-wp7/templates/standalone/VERSION
new file mode 100644
index 0000000..9aa3464
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/VERSION
@@ -0,0 +1 @@
+2.7.0
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/config.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/config.xml b/lib/cordova-wp7/templates/standalone/config.xml
new file mode 100644
index 0000000..170f9fe
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/config.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+#
+# 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.
+#
+-->
+<widget>
+
+  <plugins>
+    <plugin name="Device"/>
+    <plugin name="Logger"/>
+    <plugin name="Compass"/>
+    <plugin name="Accelerometer"/>
+    <plugin name="Camera"/>
+    <plugin name="NetworkStatus"/>
+    <plugin name="Contacts"/>
+    <plugin name="DebugConsole" />
+    <plugin name="Echo"/>
+    <plugin name="File"/>
+    <plugin name="FileTransfer"/>
+    <plugin name="Geolocation"/>
+    <plugin name="Notification"/>
+    <plugin name="Media"/>
+    <plugin name="Capture"/>
+    <plugin name="SplashScreen"/>
+    <plugin name="Battery"/>
+    <plugin name="Globalization"/>
+    <plugin name="InAppBrowser"/>
+  </plugins>
+
+
+  <access origin="*"/>
+
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/build.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/build.bat b/lib/cordova-wp7/templates/standalone/cordova/build.bat
new file mode 100644
index 0000000..b48598a
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/build.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%lib\build.js (
+    cscript "%full_path%lib\build.js" %* //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'build.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/clean.bat
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/clean.bat b/lib/cordova-wp7/templates/standalone/cordova/clean.bat
new file mode 100644
index 0000000..1bafe1b
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/clean.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%lib\clean.js (
+    cscript "%full_path%lib\clean.js" %* //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'clean.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy.sln
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy.sln b/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy.sln
new file mode 100644
index 0000000..3305276
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/cordova/lib/CordovaDeploy/CordovaDeploy.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CordovaDeploy", "CordovaDeploy\CordovaDeploy.csproj", "{E752165B-AF59-4FF0-8601-A2A69FE09E0E}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x86 = Debug|x86
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{E752165B-AF59-4FF0-8601-A2A69FE09E0E}.Debug|x86.ActiveCfg = Debug|x86
+		{E752165B-AF59-4FF0-8601-A2A69FE09E0E}.Debug|x86.Build.0 = Debug|x86
+		{E752165B-AF59-4FF0-8601-A2A69FE09E0E}.Release|x86.ActiveCfg = Release|x86
+		{E752165B-AF59-4FF0-8601-A2A69FE09E0E}.Release|x86.Build.0 = Release|x86
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal


[33/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/Accelerometer.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/Accelerometer.cs b/lib/cordova-wp7/templates/standalone/Plugins/Accelerometer.cs
new file mode 100644
index 0000000..cba911c
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/Accelerometer.cs
@@ -0,0 +1,196 @@
+/*  
+	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.
+*/
+
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Threading;
+using Microsoft.Devices.Sensors;
+using System.Globalization;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Captures device motion in the x, y, and z direction.
+    /// </summary>
+    public class Accelerometer : BaseCommand
+    {
+        #region AccelerometerOptions class
+        /// <summary>
+        /// Represents Accelerometer options.
+        /// </summary>
+        [DataContract]
+        public class AccelerometerOptions
+        {
+            /// <summary>
+            /// How often to retrieve the Acceleration in milliseconds
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "frequency")]
+            public int Frequency { get; set; }
+
+            /// <summary>
+            /// Watcher id
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "id")]
+            public string Id { get; set; }
+
+            /// <summary>
+            /// Creates options object with default parameters
+            /// </summary>
+            public AccelerometerOptions()
+            {
+                this.SetDefaultValues(new StreamingContext());
+            }
+
+            /// <summary>
+            /// Initializes default values for class fields.
+            /// Implemented in separate method because default constructor is not invoked during deserialization.
+            /// </summary>
+            /// <param name="context"></param>
+            [OnDeserializing()]
+            public void SetDefaultValues(StreamingContext context)
+            {
+                this.Frequency = 10000;
+            }
+        }
+
+        #endregion
+
+        #region Status codes and Constants
+
+        public const int Stopped = 0;
+        public const int Starting = 1;
+        public const int Running = 2;
+        public const int ErrorFailedToStart = 3;
+
+        public const double gConstant = -9.81;
+
+        #endregion
+
+        #region Static members
+
+        /// <summary>
+        /// Status of listener
+        /// </summary>
+        private static int currentStatus;
+
+        /// <summary>
+        /// Accelerometer
+        /// </summary>
+        private static Microsoft.Devices.Sensors.Accelerometer accelerometer = new Microsoft.Devices.Sensors.Accelerometer();
+
+        private static DateTime StartOfEpoch = new DateTime(1970, 1, 1, 0, 0, 0);
+
+        #endregion
+
+        /// <summary>
+        /// Sensor listener event
+        /// </summary>        
+        private void accelerometer_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
+        {
+            this.SetStatus(Running);
+
+            PluginResult result = new PluginResult(PluginResult.Status.OK, GetCurrentAccelerationFormatted());
+            result.KeepCallback = true;
+            DispatchCommandResult(result);
+        }
+
+        /// <summary>
+        /// Starts listening for acceleration sensor
+        /// </summary>
+        /// <returns>status of listener</returns>
+        public void start(string options)
+        {
+            if ((currentStatus == Running) || (currentStatus == Starting))
+            {
+                return;
+            }
+            try
+            {
+                lock (accelerometer)
+                {
+                    accelerometer.CurrentValueChanged += accelerometer_CurrentValueChanged;
+                    accelerometer.Start();
+                    this.SetStatus(Starting);
+                }
+
+                long timeout = 2000;
+                while ((currentStatus == Starting) && (timeout > 0))
+                {
+                    timeout = timeout - 100;
+                    Thread.Sleep(100);
+                }
+
+                if (currentStatus != Running)
+                {
+                    this.SetStatus(ErrorFailedToStart);
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, ErrorFailedToStart));
+                    return;
+                }
+            }
+            catch (Exception)
+            {
+                this.SetStatus(ErrorFailedToStart);
+                DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, ErrorFailedToStart));
+                return;
+            }
+            PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
+            result.KeepCallback = true;
+            DispatchCommandResult(result);
+        }
+
+        public void stop(string options)
+        {
+            if (currentStatus == Running)
+            {
+                lock (accelerometer)
+                {
+                    accelerometer.CurrentValueChanged -= accelerometer_CurrentValueChanged;
+                    accelerometer.Stop();
+                    this.SetStatus(Stopped);
+                }
+            }
+            DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+        }
+
+        /// <summary>
+        /// Formats current coordinates into JSON format
+        /// </summary>
+        /// <returns>Coordinates in JSON format</returns>
+        private string GetCurrentAccelerationFormatted()
+        {
+            // convert to unix timestamp
+            // long timestamp = ((accelerometer.CurrentValue.Timestamp.DateTime - StartOfEpoch).Ticks) / 10000;
+            // Note: Removed timestamp, to let the JS side create it using (new Date().getTime()) -jm
+            // this resolves an issue with inconsistencies between JS dates and Native DateTime 
+            string resultCoordinates = String.Format("\"x\":{0},\"y\":{1},\"z\":{2}",
+                            (accelerometer.CurrentValue.Acceleration.X * gConstant).ToString("0.00000", CultureInfo.InvariantCulture),
+                            (accelerometer.CurrentValue.Acceleration.Y * gConstant).ToString("0.00000", CultureInfo.InvariantCulture),
+                            (accelerometer.CurrentValue.Acceleration.Z * gConstant).ToString("0.00000", CultureInfo.InvariantCulture));
+            return  "{" + resultCoordinates + "}";
+        }
+
+        /// <summary>
+        /// Sets current status
+        /// </summary>
+        /// <param name="status">current status</param>
+        private void SetStatus(int status)
+        {
+            currentStatus = status;
+        }
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/AudioFormatsHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/AudioFormatsHelper.cs b/lib/cordova-wp7/templates/standalone/Plugins/AudioFormatsHelper.cs
new file mode 100644
index 0000000..dca7ee6
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/AudioFormatsHelper.cs
@@ -0,0 +1,89 @@
+/*  
+	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.
+
+ */
+
+using System;
+using System.IO;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Provides extra functionality to support different audio formats.
+    /// </summary>
+    public static class AudioFormatsHelper
+    {
+        #region Wav
+        /// <summary>
+        /// Adds wav file format header to the stream
+        /// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
+        /// </summary>
+        /// <param name="stream">The stream</param>
+        /// <param name="sampleRate">Sample Rate</param>
+        public static void InitializeWavStream(this Stream stream, int sampleRate)
+        {
+            #region args checking
+
+            if (stream == null) 
+            {
+                throw new ArgumentNullException("stream can't be null or empty");
+            }
+
+            #endregion
+
+            int numBits = 16;
+            int numBytes = numBits / 8;
+
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("RIFF"), 0, 4);
+            stream.Write(BitConverter.GetBytes(0), 0, 4);
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("WAVE"), 0, 4);
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("fmt "), 0, 4);
+            stream.Write(BitConverter.GetBytes(16), 0, 4);
+            stream.Write(BitConverter.GetBytes((short)1), 0, 2);
+            stream.Write(BitConverter.GetBytes((short)1), 0, 2);
+            stream.Write(BitConverter.GetBytes(sampleRate), 0, 4);
+            stream.Write(BitConverter.GetBytes(sampleRate * numBytes), 0, 4);
+            stream.Write(BitConverter.GetBytes((short)(numBytes)), 0, 2);
+            stream.Write(BitConverter.GetBytes((short)(numBits)), 0, 2);
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("data"), 0, 4);
+            stream.Write(BitConverter.GetBytes(0), 0, 4);
+        }
+
+        /// <summary>
+        /// Updates wav file format header
+        /// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
+        /// </summary>
+        /// <param name="stream">Wav stream</param>
+        public static void UpdateWavStream(this Stream stream)
+        {
+            #region args checking
+
+            if (stream == null)
+            {
+                throw new ArgumentNullException("stream can't be null or empty");
+            }
+
+            #endregion
+
+            var position = stream.Position;
+
+            stream.Seek(4, SeekOrigin.Begin);
+            stream.Write(BitConverter.GetBytes((int)stream.Length - 8), 0, 4);
+            stream.Seek(40, SeekOrigin.Begin);
+            stream.Write(BitConverter.GetBytes((int)stream.Length - 44), 0, 4);
+            stream.Seek(position, SeekOrigin.Begin);
+        }
+
+        #endregion
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/AudioPlayer.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/AudioPlayer.cs b/lib/cordova-wp7/templates/standalone/Plugins/AudioPlayer.cs
new file mode 100644
index 0000000..a83be5b
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/AudioPlayer.cs
@@ -0,0 +1,620 @@
+/*  
+	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.
+*/
+
+using System;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Threading;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.Media;
+using Microsoft.Phone.Controls;
+using System.Diagnostics;
+using System.Windows.Resources;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Implements audio record and play back functionality.
+    /// </summary>
+    internal class AudioPlayer : IDisposable
+    {
+        #region Constants
+
+        // AudioPlayer states
+        private const int PlayerState_None = 0;
+        private const int PlayerState_Starting = 1;
+        private const int PlayerState_Running = 2;
+        private const int PlayerState_Paused = 3;
+        private const int PlayerState_Stopped = 4;
+
+        // AudioPlayer messages
+        private const int MediaState = 1;
+        private const int MediaDuration = 2;
+        private const int MediaPosition = 3;
+        private const int MediaError = 9;
+
+        // AudioPlayer errors
+        private const int MediaErrorPlayModeSet = 1;
+        private const int MediaErrorAlreadyRecording = 2;
+        private const int MediaErrorStartingRecording = 3;
+        private const int MediaErrorRecordModeSet = 4;
+        private const int MediaErrorStartingPlayback = 5;
+        private const int MediaErrorResumeState = 6;
+        private const int MediaErrorPauseState = 7;
+        private const int MediaErrorStopState = 8;
+
+        //TODO: get rid of this callback, it should be universal
+        //private const string CallbackFunction = "CordovaMediaonStatus";
+
+        #endregion
+
+        /// <summary>
+        /// The AudioHandler object
+        /// </summary>
+        private Media handler;
+
+        /// <summary>
+        /// Temporary buffer to store audio chunk
+        /// </summary>
+        private byte[] buffer;
+
+        /// <summary>
+        /// Xna game loop dispatcher
+        /// </summary>
+        DispatcherTimer dtXna;
+
+        /// <summary>
+        /// Output buffer
+        /// </summary>
+        private MemoryStream memoryStream;
+
+        /// <summary>
+        /// The id of this player (used to identify Media object in JavaScript)
+        /// </summary>
+        private String id;
+
+        /// <summary>
+        /// State of recording or playback
+        /// </summary>
+        private int state = PlayerState_None;
+
+        /// <summary>
+        /// File name to play or record to
+        /// </summary>
+        private String audioFile = null;
+
+        /// <summary>
+        /// Duration of audio
+        /// </summary>
+        private double duration = -1;
+
+        /// <summary>
+        /// Audio player object
+        /// </summary>
+        private MediaElement player = null;
+
+        /// <summary>
+        /// Audio source
+        /// </summary>
+        private Microphone recorder;
+
+        /// <summary>
+        /// Internal flag specified that we should only open audio w/o playing it
+        /// </summary>
+        private bool prepareOnly = false;
+
+        /// <summary>
+        /// Creates AudioPlayer instance
+        /// </summary>
+        /// <param name="handler">Media object</param>
+        /// <param name="id">player id</param>
+        public AudioPlayer(Media handler, String id)
+        {
+            this.handler = handler;
+            this.id = id;
+        }
+
+        /// <summary>
+        /// Destroys player and stop audio playing or recording
+        /// </summary>
+        public void Dispose()
+        {
+            if (this.player != null)
+            {
+                this.stopPlaying();
+                this.player = null;
+            }
+            if (this.recorder != null)
+            {
+                this.stopRecording();
+                this.recorder = null;
+            }
+
+            this.FinalizeXnaGameLoop();
+        }
+
+        private void InvokeCallback(int message, string value, bool removeHandler)
+        {
+            string args = string.Format("('{0}',{1},{2});", this.id, message, value);
+            string callback = @"(function(id,msg,value){
+                try {
+                    if (msg == Media.MEDIA_ERROR) {
+                        value = {'code':value};
+                    }
+                    Media.onStatus(id,msg,value);
+                }
+                catch(e) {
+                    console.log('Error calling Media.onStatus :: ' + e);
+                }
+            })" + args;
+            this.handler.InvokeCustomScript(new ScriptCallback("eval", new string[] { callback }), false);
+        }
+
+        private void InvokeCallback(int message, int value, bool removeHandler)
+        {
+            InvokeCallback(message, value.ToString(), removeHandler);
+        }
+
+        private void InvokeCallback(int message, double value, bool removeHandler)
+        {
+            InvokeCallback(message, value.ToString(), removeHandler);
+        }
+
+        /// <summary>
+        /// Starts recording, data is stored in memory
+        /// </summary>
+        /// <param name="filePath"></param>
+        public void startRecording(string filePath)
+        {
+            if (this.player != null)
+            {
+                InvokeCallback(MediaError, MediaErrorPlayModeSet, false);
+            }
+            else if (this.recorder == null)
+            {
+                try
+                {
+                    this.audioFile = filePath;
+                    this.InitializeXnaGameLoop();
+                    this.recorder = Microphone.Default;
+                    this.recorder.BufferDuration = TimeSpan.FromMilliseconds(500);
+                    this.buffer = new byte[recorder.GetSampleSizeInBytes(this.recorder.BufferDuration)];
+                    this.recorder.BufferReady += new EventHandler<EventArgs>(recorderBufferReady);
+                    this.memoryStream = new MemoryStream();
+                    this.memoryStream.InitializeWavStream(this.recorder.SampleRate);
+                    this.recorder.Start();
+                    FrameworkDispatcher.Update();
+                    this.SetState(PlayerState_Running);
+                }
+                catch (Exception)
+                {
+                    InvokeCallback(MediaError, MediaErrorStartingRecording, false);
+                    //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStartingRecording),false);
+                }
+            }
+            else
+            {
+                InvokeCallback(MediaError, MediaErrorAlreadyRecording, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorAlreadyRecording),false);
+            }
+        }
+
+        /// <summary>
+        /// Stops recording
+        /// </summary>
+        public void stopRecording()
+        {
+            if (this.recorder != null)
+            {
+                if (this.state == PlayerState_Running)
+                {
+                    try
+                    {
+                        this.recorder.Stop();
+                        this.recorder.BufferReady -= recorderBufferReady;
+                        this.recorder = null;
+                        SaveAudioClipToLocalStorage();
+                        this.FinalizeXnaGameLoop();
+                        this.SetState(PlayerState_Stopped);
+                    }
+                    catch (Exception)
+                    {
+                        //TODO 
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Starts or resume playing audio file
+        /// </summary>
+        /// <param name="filePath">The name of the audio file</param>
+        /// <summary>
+        /// Starts or resume playing audio file
+        /// </summary>
+        /// <param name="filePath">The name of the audio file</param>
+        public void startPlaying(string filePath)
+        {
+            if (this.recorder != null)
+            {
+                InvokeCallback(MediaError, MediaErrorRecordModeSet, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorRecordModeSet),false);
+                return;
+            }
+
+
+            if (this.player == null || this.player.Source.AbsolutePath.LastIndexOf(filePath) < 0)
+            {
+                try
+                {
+                    // this.player is a MediaElement, it must be added to the visual tree in order to play
+                    PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                    if (frame != null)
+                    {
+                        PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+                        if (page != null)
+                        {
+                            Grid grid = page.FindName("LayoutRoot") as Grid;
+                            if (grid != null)
+                            {
+
+                                this.player = grid.FindName("playerMediaElement") as MediaElement;
+                                if (this.player == null) // still null ?
+                                {
+                                    this.player = new MediaElement();
+                                    this.player.Name = "playerMediaElement";
+                                    grid.Children.Add(this.player);
+                                    this.player.Visibility = Visibility.Visible;
+                                }
+                                if (this.player.CurrentState == System.Windows.Media.MediaElementState.Playing)
+                                {
+                                    this.player.Stop(); // stop it!
+                                }
+                                
+                                this.player.Source = null; // Garbage collect it.
+                                this.player.MediaOpened += MediaOpened;
+                                this.player.MediaEnded += MediaEnded;
+                                this.player.MediaFailed += MediaFailed;
+                            }
+                        }
+                    }
+
+                    this.audioFile = filePath;
+
+                    Uri uri = new Uri(filePath, UriKind.RelativeOrAbsolute);
+                    if (uri.IsAbsoluteUri)
+                    {
+                        this.player.Source = uri;
+                    }
+                    else
+                    {
+                        using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                        {
+                            if (!isoFile.FileExists(filePath))
+                            {
+                                // try to unpack it from the dll into isolated storage
+                                StreamResourceInfo fileResourceStreamInfo = Application.GetResourceStream(new Uri(filePath, UriKind.Relative));
+                                if (fileResourceStreamInfo != null)
+                                {
+                                    using (BinaryReader br = new BinaryReader(fileResourceStreamInfo.Stream))
+                                    {
+                                        byte[] data = br.ReadBytes((int)fileResourceStreamInfo.Stream.Length);          
+
+                                        string[] dirParts = filePath.Split('/');
+                                        string dirName = "";
+                                        for (int n = 0; n < dirParts.Length - 1; n++)
+                                        {
+                                            dirName += dirParts[n] + "/";
+                                        }
+                                        if (!isoFile.DirectoryExists(dirName))
+                                        {
+                                            isoFile.CreateDirectory(dirName);
+                                        }
+
+                                        using (IsolatedStorageFileStream outFile = isoFile.OpenFile(filePath, FileMode.Create))
+                                        {
+                                            using (BinaryWriter writer = new BinaryWriter(outFile))
+                                            {
+                                                writer.Write(data);
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                            if (isoFile.FileExists(filePath))
+                            {
+                                using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, isoFile))
+                                {
+                                    this.player.SetSource(stream);
+                                }
+                            }
+                            else
+                            {
+                                InvokeCallback(MediaError, MediaErrorPlayModeSet, false);
+                                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, 1), false);
+                                return;
+                            }
+                        }
+                    }
+                    this.SetState(PlayerState_Starting);
+                }
+                catch (Exception e)
+                {
+                    Debug.WriteLine("Error in AudioPlayer::startPlaying : " + e.Message);
+                    InvokeCallback(MediaError, MediaErrorStartingPlayback, false);
+                    //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStartingPlayback),false);
+                }
+            }
+            else
+            {
+                if (this.state != PlayerState_Running)
+                {
+                    this.player.Play();
+                    this.SetState(PlayerState_Running);
+                }
+                else
+                {
+                    InvokeCallback(MediaError, MediaErrorResumeState, false);
+                    //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorResumeState),false);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Callback to be invoked when the media source is ready for playback
+        /// </summary>
+        private void MediaOpened(object sender, RoutedEventArgs arg)
+        {
+            if (this.player != null)
+            {
+                this.duration = this.player.NaturalDuration.TimeSpan.TotalSeconds;
+                InvokeCallback(MediaDuration, this.duration, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaDuration, this.duration),false);
+                if (!this.prepareOnly)
+                {
+                    this.player.Play();
+                    this.SetState(PlayerState_Running);
+                }
+                this.prepareOnly = false;
+            }
+            else
+            {
+                // TODO: occasionally MediaOpened is signalled, but player is null
+            }
+        }
+
+        /// <summary>
+        /// Callback to be invoked when playback of a media source has completed
+        /// </summary>
+        private void MediaEnded(object sender, RoutedEventArgs arg)
+        {
+            this.SetState(PlayerState_Stopped);
+        }
+
+        /// <summary>
+        /// Callback to be invoked when playback of a media source has failed
+        /// </summary>
+        private void MediaFailed(object sender, RoutedEventArgs arg)
+        {
+            player.Stop();
+            InvokeCallback(MediaError, MediaErrorStartingPlayback, false);
+            //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError.ToString(), "Media failed"),false);
+        }
+
+        /// <summary>
+        /// Seek or jump to a new time in the track
+        /// </summary>
+        /// <param name="milliseconds">The new track position</param>
+        public void seekToPlaying(int milliseconds)
+        {
+            if (this.player != null)
+            {
+                TimeSpan tsPos = new TimeSpan(0, 0, 0, 0, milliseconds);
+                this.player.Position = tsPos;
+                InvokeCallback(MediaPosition, milliseconds / 1000.0f, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaPosition, milliseconds / 1000.0f),false);
+            }
+        }
+
+        /// <summary>
+        /// Set the volume of the player
+        /// </summary>
+        /// <param name="vol">volume 0.0-1.0, default value is 0.5</param>
+        public void setVolume(double vol)
+        {
+            if (this.player != null)
+            {
+                this.player.Volume = vol;
+            }
+        }
+
+        /// <summary>
+        /// Pauses playing
+        /// </summary>
+        public void pausePlaying()
+        {
+            if (this.state == PlayerState_Running)
+            {
+                this.player.Pause();
+                this.SetState(PlayerState_Paused);
+            }
+            else
+            {
+                InvokeCallback(MediaError, MediaErrorPauseState, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorPauseState),false);
+            }
+        }
+
+
+        /// <summary>
+        /// Stops playing the audio file
+        /// </summary>
+        public void stopPlaying()
+        {
+            if ((this.state == PlayerState_Running) || (this.state == PlayerState_Paused))
+            {
+                this.player.Stop();
+
+                this.player.Position = new TimeSpan(0L);
+                this.SetState(PlayerState_Stopped);
+            }
+            //else // Why is it an error to call stop on a stopped media?
+            //{
+            //    this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStopState), false);
+            //}
+        }
+
+        /// <summary>
+        /// Gets current position of playback
+        /// </summary>
+        /// <returns>current position</returns>
+        public double getCurrentPosition()
+        {
+            if ((this.state == PlayerState_Running) || (this.state == PlayerState_Paused))
+            {
+                double currentPosition = this.player.Position.TotalSeconds;
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaPosition, currentPosition),false);
+                return currentPosition;
+            }
+            else
+            {
+                return 0;
+            }
+        }
+
+        /// <summary>
+        /// Gets the duration of the audio file
+        /// </summary>
+        /// <param name="filePath">The name of the audio file</param>
+        /// <returns>track duration</returns>
+        public double getDuration(string filePath)
+        {
+            if (this.recorder != null)
+            {
+                return (-2);
+            }
+
+            if (this.player != null)
+            {
+                return this.duration;
+
+            }
+            else
+            {
+                this.prepareOnly = true;
+                this.startPlaying(filePath);
+                return this.duration;
+            }
+        }
+
+        /// <summary>
+        /// Sets the state and send it to JavaScript
+        /// </summary>
+        /// <param name="state">state</param>
+        private void SetState(int state)
+        {
+            if (this.state != state)
+            {
+                InvokeCallback(MediaState, state, false);
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaState, state),false);
+            }
+
+            this.state = state;
+        }
+
+        #region record methods
+
+        /// <summary>
+        /// Copies data from recorder to memory storages and updates recording state
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void recorderBufferReady(object sender, EventArgs e)
+        {
+            this.recorder.GetData(this.buffer);
+            this.memoryStream.Write(this.buffer, 0, this.buffer.Length);
+        }
+
+        /// <summary>
+        /// Writes audio data from memory to isolated storage
+        /// </summary>
+        /// <returns></returns>
+        private void SaveAudioClipToLocalStorage()
+        {
+            if (this.memoryStream == null || this.memoryStream.Length <= 0)
+            {
+                return;
+            }
+
+            this.memoryStream.UpdateWavStream();
+
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    string directory = Path.GetDirectoryName(audioFile);
+
+                    if (!isoFile.DirectoryExists(directory))
+                    {
+                        isoFile.CreateDirectory(directory);
+                    }
+
+                    this.memoryStream.Seek(0, SeekOrigin.Begin);
+
+                    using (IsolatedStorageFileStream fileStream = isoFile.CreateFile(audioFile))
+                    {
+                        this.memoryStream.CopyTo(fileStream);
+                    }
+                }
+            }
+            catch (Exception)
+            {
+                //TODO: log or do something else
+                throw;
+            }
+        }    
+
+        #region Xna loop
+        /// <summary>
+        /// Special initialization required for the microphone: XNA game loop
+        /// </summary>
+        private void InitializeXnaGameLoop()
+        {
+            // Timer to simulate the XNA game loop (Microphone is from XNA)
+            this.dtXna = new DispatcherTimer();
+            this.dtXna.Interval = TimeSpan.FromMilliseconds(33);
+            this.dtXna.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
+            this.dtXna.Start();
+        }
+        /// <summary>
+        /// Finalizes XNA game loop for microphone
+        /// </summary>
+        private void FinalizeXnaGameLoop()
+        {
+            // Timer to simulate the XNA game loop (Microphone is from XNA)
+            if (this.dtXna != null)
+            {
+                this.dtXna.Stop();
+                this.dtXna = null;
+            }
+        }
+
+        #endregion
+
+        #endregion
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/Battery.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/Battery.cs b/lib/cordova-wp7/templates/standalone/Plugins/Battery.cs
new file mode 100644
index 0000000..962959e
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/Battery.cs
@@ -0,0 +1,79 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+
+using Microsoft.Phone.Info;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Listens for changes to the state of the battery on the device.
+    /// Currently only the "isPlugged" parameter available via native APIs.
+    /// </summary>
+    public class Battery : BaseCommand
+    {
+        private bool isPlugged = false;
+        private EventHandler powerChanged;
+
+        public Battery()
+        {
+            powerChanged = new EventHandler(DeviceStatus_PowerSourceChanged);
+            isPlugged = DeviceStatus.PowerSource.ToString().CompareTo("External") == 0;
+        }
+
+        public void start(string options)
+        {
+            // Register power changed event handler
+            DeviceStatus.PowerSourceChanged += powerChanged;
+
+            PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
+            result.KeepCallback = true;
+            DispatchCommandResult(result);
+        }
+        public void stop(string options)
+        {
+            // Unregister power changed event handler
+            DeviceStatus.PowerSourceChanged -= powerChanged;
+        }
+
+        private void DeviceStatus_PowerSourceChanged(object sender, EventArgs e)
+        {
+            isPlugged = DeviceStatus.PowerSource.ToString().CompareTo("External") == 0;
+            PluginResult result = new PluginResult(PluginResult.Status.OK, GetCurrentBatteryStateFormatted());
+            result.KeepCallback = true;
+            DispatchCommandResult(result);
+        }
+
+        private string GetCurrentBatteryStateFormatted()
+        {
+            string batteryState = String.Format("\"level\":{0},\"isPlugged\":{1}",
+                                                    "null",
+                                                    isPlugged ? "true" : "false"
+                            );
+            batteryState = "{" + batteryState + "}";
+            return batteryState;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/Camera.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/Camera.cs b/lib/cordova-wp7/templates/standalone/Plugins/Camera.cs
new file mode 100644
index 0000000..5ff8045
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/Camera.cs
@@ -0,0 +1,490 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Collections.Generic;
+using Microsoft.Phone.Tasks;
+using System.Runtime.Serialization;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Windows.Media.Imaging;
+using Microsoft.Phone;
+using Microsoft.Xna.Framework.Media;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    public class Camera : BaseCommand
+    {
+
+        /// <summary>
+        /// Return base64 encoded string
+        /// </summary>
+        private const int DATA_URL = 0;
+
+        /// <summary>
+        /// Return file uri
+        /// </summary>
+        private const int FILE_URI = 1;
+
+        /// <summary>
+        /// Choose image from picture library
+        /// </summary>
+        private const int PHOTOLIBRARY = 0;
+
+        /// <summary>
+        /// Take picture from camera
+        /// </summary>
+
+        private const int CAMERA = 1;
+
+        /// <summary>
+        /// Choose image from picture library
+        /// </summary>
+        private const int SAVEDPHOTOALBUM = 2;
+
+        /// <summary>
+        /// Take a picture of type JPEG
+        /// </summary>
+        private const int JPEG = 0;
+
+        /// <summary>
+        /// Take a picture of type PNG
+        /// </summary>
+        private const int PNG = 1;
+
+        /// <summary>
+        /// Folder to store captured images
+        /// </summary>
+        private const string isoFolder = "CapturedImagesCache";
+
+        /// <summary>
+        /// Represents captureImage action options.
+        /// </summary>
+        [DataContract]
+        public class CameraOptions
+        {
+            /// <summary>
+            /// Source to getPicture from.
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "sourceType")]
+            public int PictureSourceType { get; set; }
+
+            /// <summary>
+            /// Format of image that returned from getPicture.
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "destinationType")]
+            public int DestinationType { get; set; }
+
+            /// <summary>
+            /// Quality of saved image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "quality")]
+            public int Quality { get; set; }
+
+            /// <summary>
+            /// Controls whether or not the image is also added to the device photo album.
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "saveToPhotoAlbum")]
+            public bool SaveToPhotoAlbum { get; set; }
+
+            /// <summary>
+            /// Ignored
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "correctOrientation")]
+            public bool CorrectOrientation { get; set; }
+
+            
+
+            /// <summary>
+            /// Ignored
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "allowEdit")]
+            public bool AllowEdit { get; set; }
+
+                        /// <summary>
+            /// Height in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "encodingType")]
+            public int EncodingType { get; set; }
+
+                        /// <summary>
+            /// Height in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "mediaType")]
+            public int MediaType { get; set; }
+
+
+            /// <summary>
+            /// Height in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "targetHeight")]
+            public int TargetHeight { get; set; }
+
+
+            /// <summary>
+            /// Width in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "targetWidth")]
+            public int TargetWidth { get; set; }
+
+            /// <summary>
+            /// Creates options object with default parameters
+            /// </summary>
+            public CameraOptions()
+            {
+                this.SetDefaultValues(new StreamingContext());
+            }
+
+            /// <summary>
+            /// Initializes default values for class fields.
+            /// Implemented in separate method because default constructor is not invoked during deserialization.
+            /// </summary>
+            /// <param name="context"></param>
+            [OnDeserializing()]
+            public void SetDefaultValues(StreamingContext context)
+            {
+                PictureSourceType = CAMERA;
+                DestinationType = FILE_URI;
+                Quality = 80;
+                TargetHeight = -1;
+                TargetWidth = -1;
+                SaveToPhotoAlbum = false;
+                CorrectOrientation = true;
+                AllowEdit = false;
+                MediaType = -1;
+                EncodingType = -1;
+            }
+        }
+
+        /// <summary>
+        /// Used to open photo library
+        /// </summary>
+        PhotoChooserTask photoChooserTask;
+
+        /// <summary>
+        /// Used to open camera application
+        /// </summary>
+        CameraCaptureTask cameraTask;
+
+        /// <summary>
+        /// Camera options
+        /// </summary>
+        CameraOptions cameraOptions;
+
+        public void takePicture(string options)
+        {
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+                // ["quality", "destinationType", "sourceType", "targetWidth", "targetHeight", "encodingType",
+                //     "mediaType", "allowEdit", "correctOrientation", "saveToPhotoAlbum" ]
+                this.cameraOptions = new CameraOptions();
+                this.cameraOptions.Quality = int.Parse(args[0]);
+                this.cameraOptions.DestinationType = int.Parse(args[1]);
+                this.cameraOptions.PictureSourceType = int.Parse(args[2]);
+                this.cameraOptions.TargetWidth = int.Parse(args[3]);
+                this.cameraOptions.TargetHeight = int.Parse(args[4]);
+                this.cameraOptions.EncodingType = int.Parse(args[5]);
+                this.cameraOptions.MediaType = int.Parse(args[6]);
+                this.cameraOptions.AllowEdit = bool.Parse(args[7]);
+                this.cameraOptions.CorrectOrientation = bool.Parse(args[8]);
+                this.cameraOptions.SaveToPhotoAlbum = bool.Parse(args[9]);
+                
+                //this.cameraOptions = String.IsNullOrEmpty(options) ?
+                //        new CameraOptions() : JSON.JsonHelper.Deserialize<CameraOptions>(options);
+            }
+            catch (Exception ex)
+            {
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                return;
+            }
+
+            //TODO Check if all the options are acceptable
+
+
+            if (cameraOptions.PictureSourceType == CAMERA)
+            {
+                cameraTask = new CameraCaptureTask();
+                cameraTask.Completed += onCameraTaskCompleted;
+                cameraTask.Show();
+            }
+            else
+            {
+                if ((cameraOptions.PictureSourceType == PHOTOLIBRARY) || (cameraOptions.PictureSourceType == SAVEDPHOTOALBUM))
+                {
+                    photoChooserTask = new PhotoChooserTask();
+                    photoChooserTask.Completed += onPickerTaskCompleted;
+                    photoChooserTask.Show();
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
+                }
+            }
+
+        }
+
+        public void onCameraTaskCompleted(object sender, PhotoResult e)
+        {
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        string imagePathOrContent = string.Empty;
+
+                        if (cameraOptions.DestinationType == FILE_URI)
+                        {
+                            // Save image in media library
+                            if (cameraOptions.SaveToPhotoAlbum)
+                            {
+                                MediaLibrary library = new MediaLibrary();
+                                Picture pict = library.SavePicture(e.OriginalFileName, e.ChosenPhoto); // to save to photo-roll ...
+                            }
+
+                            int orient = ImageExifHelper.getImageOrientationFromStream(e.ChosenPhoto);
+                            int newAngle = 0;
+                            switch (orient)
+                            {
+                                case ImageExifOrientation.LandscapeLeft:
+                                    newAngle = 90;
+                                    break;
+                                case ImageExifOrientation.PortraitUpsideDown:
+                                    newAngle = 180;
+                                    break;
+                                case ImageExifOrientation.LandscapeRight:
+                                    newAngle = 270;
+                                    break;
+                                case ImageExifOrientation.Portrait:
+                                default: break; // 0 default already set
+                            }
+
+                            Stream rotImageStream = ImageExifHelper.RotateStream(e.ChosenPhoto, newAngle);
+
+                            // we should return stream position back after saving stream to media library
+                            rotImageStream.Seek(0, SeekOrigin.Begin);
+
+                            WriteableBitmap image = PictureDecoder.DecodeJpeg(rotImageStream);
+
+                            imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
+
+
+                        }
+                        else if (cameraOptions.DestinationType == DATA_URL)
+                        {
+                            imagePathOrContent = this.GetImageContent(e.ChosenPhoto);
+                        }
+                        else
+                        {
+                            // TODO: shouldn't this happen before we launch the camera-picker?
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
+                            return;
+                        }
+
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
+
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
+                    break;
+
+                default:
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
+                    break;
+            }
+
+        }
+
+        public void onPickerTaskCompleted(object sender, PhotoResult e)
+        {
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        string imagePathOrContent = string.Empty;
+
+                        if (cameraOptions.DestinationType == FILE_URI)
+                        {
+                            WriteableBitmap image = PictureDecoder.DecodeJpeg(e.ChosenPhoto);
+                            imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
+                        }
+                        else if (cameraOptions.DestinationType == DATA_URL)
+                        {
+                            imagePathOrContent = this.GetImageContent(e.ChosenPhoto);
+
+                        }
+                        else
+                        {
+                            // TODO: shouldn't this happen before we launch the camera-picker?
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
+                            return;
+                        }
+
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
+
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
+                    break;
+
+                default:
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Returns image content in a form of base64 string
+        /// </summary>
+        /// <param name="stream">Image stream</param>
+        /// <returns>Base64 representation of the image</returns>
+        private string GetImageContent(Stream stream)
+        {
+            int streamLength = (int)stream.Length;
+            byte[] fileData = new byte[streamLength + 1];
+            stream.Read(fileData, 0, streamLength);
+
+            //use photo's actual width & height if user doesn't provide width & height
+            if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
+            {
+                stream.Close();
+                return Convert.ToBase64String(fileData);
+            }
+            else
+            {
+                // resize photo
+                byte[] resizedFile = ResizePhoto(stream, fileData);
+                stream.Close();
+                return Convert.ToBase64String(resizedFile);
+            }
+        }
+
+        /// <summary>
+        /// Resize image
+        /// </summary>
+        /// <param name="stream">Image stream</param>
+        /// <param name="fileData">File data</param>
+        /// <returns>resized image</returns>
+        private byte[] ResizePhoto(Stream stream, byte[] fileData)
+        {
+            int streamLength = (int)stream.Length;
+            int intResult = 0;
+
+            byte[] resizedFile;
+
+            stream.Read(fileData, 0, streamLength);
+
+            BitmapImage objBitmap = new BitmapImage();
+            MemoryStream objBitmapStream = new MemoryStream(fileData);
+            MemoryStream objBitmapStreamResized = new MemoryStream();
+            WriteableBitmap objWB;
+            objBitmap.SetSource(stream);
+            objWB = new WriteableBitmap(objBitmap);
+
+            // resize the photo with user defined TargetWidth & TargetHeight
+            Extensions.SaveJpeg(objWB, objBitmapStreamResized, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
+
+            //Convert the resized stream to a byte array. 
+            streamLength = (int)objBitmapStreamResized.Length;
+            resizedFile = new Byte[streamLength]; //-1 
+            objBitmapStreamResized.Position = 0;
+            //for some reason we have to set Position to zero, but we don't have to earlier when we get the bytes from the chosen photo... 
+            intResult = objBitmapStreamResized.Read(resizedFile, 0, streamLength);
+
+            return resizedFile;
+        }
+
+        /// <summary>
+        /// Saves captured image in isolated storage
+        /// </summary>
+        /// <param name="imageFileName">image file name</param>
+        /// <returns>Image path</returns>
+        private string SaveImageToLocalStorage(WriteableBitmap image, string imageFileName)
+        {
+
+            if (image == null)
+            {
+                throw new ArgumentNullException("imageBytes");
+            }
+            try
+            {
+
+
+                var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+
+                if (!isoFile.DirectoryExists(isoFolder))
+                {
+                    isoFile.CreateDirectory(isoFolder);
+                }
+
+                string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName);
+
+                using (var stream = isoFile.CreateFile(filePath))
+                {
+                    // resize image if Height and Width defined via options 
+                    if (cameraOptions.TargetHeight > 0 && cameraOptions.TargetWidth > 0)
+                    {
+                        image.SaveJpeg(stream, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
+                    }
+                    else
+                    {
+                        image.SaveJpeg(stream, image.PixelWidth, image.PixelHeight, 0, cameraOptions.Quality);
+                    }
+                }
+
+                return new Uri(filePath, UriKind.Relative).ToString();
+            }
+            catch (Exception)
+            {
+                //TODO: log or do something else
+                throw;
+            }
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp7/templates/standalone/Plugins/Capture.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp7/templates/standalone/Plugins/Capture.cs b/lib/cordova-wp7/templates/standalone/Plugins/Capture.cs
new file mode 100644
index 0000000..5e14a16
--- /dev/null
+++ b/lib/cordova-wp7/templates/standalone/Plugins/Capture.cs
@@ -0,0 +1,736 @@
+/*  
+	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.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Runtime.Serialization;
+using System.Windows.Media.Imaging;
+using Microsoft.Phone;
+using Microsoft.Phone.Tasks;
+using Microsoft.Xna.Framework.Media;
+using WPCordovaClassLib.Cordova.UI;
+using AudioResult = WPCordovaClassLib.Cordova.UI.AudioCaptureTask.AudioResult;
+using VideoResult = WPCordovaClassLib.Cordova.UI.VideoCaptureTask.VideoResult;
+using System.Windows;
+using System.Diagnostics;
+using Microsoft.Phone.Controls;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Provides access to the audio, image, and video capture capabilities of the device
+    /// </summary>
+    public class Capture : BaseCommand
+    {
+        #region Internal classes (options and resultant objects)
+
+        /// <summary>
+        /// Represents captureImage action options.
+        /// </summary>
+        [DataContract]
+        public class CaptureImageOptions
+        {
+            /// <summary>
+            /// The maximum number of images the device user can capture in a single capture operation. The value must be greater than or equal to 1 (defaults to 1).
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "limit")]
+            public int Limit { get; set; }
+
+            public static CaptureImageOptions Default
+            {
+                get { return new CaptureImageOptions() { Limit = 1 }; }
+            }
+        }
+
+        /// <summary>
+        /// Represents captureAudio action options.
+        /// </summary>
+        [DataContract]
+        public class CaptureAudioOptions
+        {
+            /// <summary>
+            /// The maximum number of audio files the device user can capture in a single capture operation. The value must be greater than or equal to 1 (defaults to 1).
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "limit")]
+            public int Limit { get; set; }
+
+            public static CaptureAudioOptions Default
+            {
+                get { return new CaptureAudioOptions() { Limit = 1 }; }
+            }
+        }
+
+        /// <summary>
+        /// Represents captureVideo action options.
+        /// </summary>
+        [DataContract]
+        public class CaptureVideoOptions
+        {
+            /// <summary>
+            /// The maximum number of video files the device user can capture in a single capture operation. The value must be greater than or equal to 1 (defaults to 1).
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "limit")]
+            public int Limit { get; set; }
+
+            public static CaptureVideoOptions Default
+            {
+                get { return new CaptureVideoOptions() { Limit = 1 }; }
+            }
+        }
+
+        /// <summary>
+        /// Represents getFormatData action options.
+        /// </summary>
+        [DataContract]
+        public class MediaFormatOptions
+        {
+            /// <summary>
+            /// File path
+            /// </summary>
+            [DataMember(IsRequired = true, Name = "fullPath")]
+            public string FullPath { get; set; }
+
+            /// <summary>
+            /// File mime type
+            /// </summary>
+            [DataMember(Name = "type")]
+            public string Type { get; set; }
+
+        }
+
+        /// <summary>
+        /// Stores image info
+        /// </summary>
+        [DataContract]
+        public class MediaFile
+        {
+
+            [DataMember(Name = "name")]
+            public string FileName { get; set; }
+
+            [DataMember(Name = "fullPath")]
+            public string FilePath { get; set; }
+
+            [DataMember(Name = "type")]
+            public string Type { get; set; }
+
+            [DataMember(Name = "lastModifiedDate")]
+            public string LastModifiedDate { get; set; }
+
+            [DataMember(Name = "size")]
+            public long Size { get; set; }
+
+            public MediaFile(string filePath, Picture image)
+            {
+                this.FilePath = filePath;
+                this.FileName = System.IO.Path.GetFileName(this.FilePath);
+                this.Type = MimeTypeMapper.GetMimeType(FileName);
+                this.Size = image.GetImage().Length;
+
+                using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    this.LastModifiedDate = storage.GetLastWriteTime(filePath).DateTime.ToString();
+                }
+
+            }
+
+            public MediaFile(string filePath, Stream stream)
+            {
+                this.FilePath = filePath;
+                this.FileName = System.IO.Path.GetFileName(this.FilePath);
+                this.Type = MimeTypeMapper.GetMimeType(FileName);
+                this.Size = stream.Length;
+
+                using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    this.LastModifiedDate = storage.GetLastWriteTime(filePath).DateTime.ToString();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Stores additional media file data
+        /// </summary>
+        [DataContract]
+        public class MediaFileData
+        {
+            [DataMember(Name = "height")]
+            public int Height { get; set; }
+
+            [DataMember(Name = "width")]
+            public int Width { get; set; }
+
+            [DataMember(Name = "bitrate")]
+            public int Bitrate { get; set; }
+
+            [DataMember(Name = "duration")]
+            public int Duration { get; set; }
+
+            [DataMember(Name = "codecs")]
+            public string Codecs { get; set; }
+
+            public MediaFileData(WriteableBitmap image)
+            {
+                this.Height = image.PixelHeight;
+                this.Width = image.PixelWidth;
+                this.Bitrate = 0;
+                this.Duration = 0;
+                this.Codecs = "";
+            }
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Folder to store captured images
+        /// </summary>
+        private string isoFolder = "CapturedImagesCache";
+
+        /// <summary>
+        /// Capture Image options
+        /// </summary>
+        protected CaptureImageOptions captureImageOptions;
+
+        /// <summary>
+        /// Capture Audio options
+        /// </summary>
+        protected CaptureAudioOptions captureAudioOptions;
+
+        /// <summary>
+        /// Capture Video options
+        /// </summary>
+        protected CaptureVideoOptions captureVideoOptions;
+
+        /// <summary>
+        /// Used to open camera application
+        /// </summary>
+        private CameraCaptureTask cameraTask;
+
+        /// <summary>
+        /// Used for audio recording
+        /// </summary>
+        private AudioCaptureTask audioCaptureTask;
+
+        /// <summary>
+        /// Used for video recording
+        /// </summary>
+        private VideoCaptureTask videoCaptureTask;
+
+        /// <summary>
+        /// Stores information about captured files
+        /// </summary>
+        List<MediaFile> files = new List<MediaFile>();
+
+        /// <summary>
+        /// Launches default camera application to capture image
+        /// </summary>
+        /// <param name="options">may contains limit or mode parameters</param>
+        public void captureImage(string options)
+        {
+            try
+            {
+                try
+                {
+
+                    string args = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                    this.captureImageOptions = String.IsNullOrEmpty(args) ? CaptureImageOptions.Default : JSON.JsonHelper.Deserialize<CaptureImageOptions>(args);
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+
+                cameraTask = new CameraCaptureTask();
+                cameraTask.Completed += this.cameraTask_Completed;
+                cameraTask.Show();
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Launches our own audio recording control to capture audio
+        /// </summary>
+        /// <param name="options">may contains additional parameters</param>
+        public void captureAudio(string options)
+        {
+            try
+            {
+                try
+                {
+                    string args = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                    this.captureAudioOptions = String.IsNullOrEmpty(args) ? CaptureAudioOptions.Default : JSON.JsonHelper.Deserialize<CaptureAudioOptions>(args);
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                audioCaptureTask = new AudioCaptureTask();
+                audioCaptureTask.Completed += audioRecordingTask_Completed;
+                audioCaptureTask.Show();
+
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Launches our own video recording control to capture video
+        /// </summary>
+        /// <param name="options">may contains additional parameters</param>
+        public void captureVideo(string options)
+        {
+            try
+            {
+                try
+                {
+                    string args = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                    this.captureVideoOptions = String.IsNullOrEmpty(args) ? CaptureVideoOptions.Default : JSON.JsonHelper.Deserialize<CaptureVideoOptions>(args);
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                videoCaptureTask = new VideoCaptureTask();
+                videoCaptureTask.Completed += videoRecordingTask_Completed;
+                videoCaptureTask.Show();
+
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Retrieves the format information of the media file.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getFormatData(string options)
+        {
+            try
+            {
+                MediaFormatOptions mediaFormatOptions;
+                try
+                {
+                    mediaFormatOptions = new MediaFormatOptions();
+                    string[] optionStrings = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaFormatOptions.FullPath = optionStrings[0];
+                    mediaFormatOptions.Type = optionStrings[1];
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                if (string.IsNullOrEmpty(mediaFormatOptions.FullPath))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                }
+
+                string mimeType = mediaFormatOptions.Type;
+
+                if (string.IsNullOrEmpty(mimeType))
+                {
+                    mimeType = MimeTypeMapper.GetMimeType(mediaFormatOptions.FullPath);
+                }
+
+                if (mimeType.Equals("image/jpeg"))
+                {
+                    Deployment.Current.Dispatcher.BeginInvoke(() =>
+                    {
+                        WriteableBitmap image = ExtractImageFromLocalStorage(mediaFormatOptions.FullPath);
+
+                        if (image == null)
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "File not found"));
+                            return;
+                        }
+
+                        MediaFileData mediaData = new MediaFileData(image);
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, mediaData));
+                    });
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                }
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+            }
+        }
+
+        /// <summary>
+        /// Opens specified file in media player
+        /// </summary>
+        /// <param name="options">MediaFile to play</param>
+        public void play(string options)
+        {
+            try
+            {
+                MediaFile file;
+
+                try
+                {
+                    file = String.IsNullOrEmpty(options) ? null : JSON.JsonHelper.Deserialize<MediaFile[]>(options)[0];
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                if (file == null || String.IsNullOrEmpty(file.FilePath))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "File path is missing"));
+                    return;
+                }
+
+                // if url starts with '/' media player throws FileNotFound exception
+                Uri fileUri = new Uri(file.FilePath.TrimStart(new char[] { '/', '\\' }), UriKind.Relative);
+
+                MediaPlayerLauncher player = new MediaPlayerLauncher();
+                player.Media = fileUri;
+                player.Location = MediaLocationType.Data;
+                player.Show();
+
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+
+        /// <summary>
+        /// Handles result of capture to save image information 
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e">stores information about current captured image</param>
+        private void cameraTask_Completed(object sender, PhotoResult e)
+        {
+
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        string fileName = System.IO.Path.GetFileName(e.OriginalFileName);
+
+                        // Save image in media library
+                        MediaLibrary library = new MediaLibrary();
+                        Picture image = library.SavePicture(fileName, e.ChosenPhoto);
+
+                        int orient = ImageExifHelper.getImageOrientationFromStream(e.ChosenPhoto);
+                        int newAngle = 0;
+                        switch (orient)
+                        {
+                            case ImageExifOrientation.LandscapeLeft:
+                                newAngle = 90;
+                                break;
+                            case ImageExifOrientation.PortraitUpsideDown:
+                                newAngle = 180;
+                                break;
+                            case ImageExifOrientation.LandscapeRight:
+                                newAngle = 270;
+                                break;
+                            case ImageExifOrientation.Portrait:
+                            default: break; // 0 default already set
+                        }
+
+                        Stream rotImageStream = ImageExifHelper.RotateStream(e.ChosenPhoto, newAngle);
+
+                        // Save image in isolated storage    
+
+                        // we should return stream position back after saving stream to media library
+                        rotImageStream.Seek(0, SeekOrigin.Begin);
+
+                        byte[] imageBytes = new byte[rotImageStream.Length];
+                        rotImageStream.Read(imageBytes, 0, imageBytes.Length);
+                        rotImageStream.Dispose();
+                        string pathLocalStorage = this.SaveImageToLocalStorage(fileName, isoFolder, imageBytes);
+                        imageBytes = null;
+                        // Get image data
+                        MediaFile data = new MediaFile(pathLocalStorage, image);
+
+                        this.files.Add(data);
+
+                        if (files.Count < this.captureImageOptions.Limit)
+                        {
+                            cameraTask.Show();
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                            files.Clear();
+                        }
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error capturing image."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    if (files.Count > 0)
+                    {
+                        // User canceled operation, but some images were made
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Canceled."));
+                    }
+                    break;
+
+                default:
+                    if (files.Count > 0)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Did not complete!"));
+                    }
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Handles result of audio recording tasks 
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e">stores information about current captured audio</param>
+        private void audioRecordingTask_Completed(object sender, AudioResult e)
+        {
+
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        // Get image data
+                        MediaFile data = new MediaFile(e.AudioFileName, e.AudioFile);
+
+                        this.files.Add(data);
+
+                        if (files.Count < this.captureAudioOptions.Limit)
+                        {
+                            audioCaptureTask.Show();
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                            files.Clear();
+                        }
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error capturing audio."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    if (files.Count > 0)
+                    {
+                        // User canceled operation, but some audio clips were made
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Canceled."));
+                    }
+                    break;
+
+                default:
+                    if (files.Count > 0)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Did not complete!"));
+                    }
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Handles result of video recording tasks 
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e">stores information about current captured video</param>
+        private void videoRecordingTask_Completed(object sender, VideoResult e)
+        {
+
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        // Get image data
+                        MediaFile data = new MediaFile(e.VideoFileName, e.VideoFile);
+
+                        this.files.Add(data);
+
+                        if (files.Count < this.captureVideoOptions.Limit)
+                        {
+                            videoCaptureTask.Show();
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                            files.Clear();
+                        }
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error capturing video."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    if (files.Count > 0)
+                    {
+                        // User canceled operation, but some video clips were made
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Canceled."));
+                    }
+                    break;
+
+                default:
+                    if (files.Count > 0)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Did not complete!"));
+                    }
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Extract file from Isolated Storage as WriteableBitmap object
+        /// </summary>
+        /// <param name="filePath"></param>
+        /// <returns></returns>
+        private WriteableBitmap ExtractImageFromLocalStorage(string filePath)
+        {
+            try
+            {
+
+                var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+
+                using (var imageStream = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read))
+                {
+                    var imageSource = PictureDecoder.DecodeJpeg(imageStream);
+                    return imageSource;
+                }
+            }
+            catch (Exception)
+            {
+                return null;
+            }
+        }
+
+
+        /// <summary>
+        /// Saves captured image in isolated storage
+        /// </summary>
+        /// <param name="imageFileName">image file name</param>
+        /// <param name="imageFolder">folder to store images</param>
+        /// <returns>Image path</returns>
+        private string SaveImageToLocalStorage(string imageFileName, string imageFolder, byte[] imageBytes)
+        {
+            if (imageBytes == null)
+            {
+                throw new ArgumentNullException("imageBytes");
+            }
+            try
+            {
+                var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+
+                if (!isoFile.DirectoryExists(imageFolder))
+                {
+                    isoFile.CreateDirectory(imageFolder);
+                }
+                string filePath = System.IO.Path.Combine("/" + imageFolder + "/", imageFileName);
+
+                using (IsolatedStorageFileStream stream = isoFile.CreateFile(filePath))
+                {
+                    stream.Write(imageBytes, 0, imageBytes.Length);
+                }
+
+                return filePath;
+            }
+            catch (Exception)
+            {
+                //TODO: log or do something else
+                throw;
+            }
+        }
+
+
+    }
+}
\ No newline at end of file


[02/50] Reorganize specs into cordova-cli/ and platform-script/

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/create.spec.js
----------------------------------------------------------------------
diff --git a/spec/create.spec.js b/spec/create.spec.js
deleted file mode 100644
index fcb3e76..0000000
--- a/spec/create.spec.js
+++ /dev/null
@@ -1,68 +0,0 @@
-var cordova = require('../cordova'),
-    path    = require('path'),
-    shell   = require('shelljs'),
-    fs      = require('fs'),
-    tempDir = path.join(__dirname, '..', 'temp');
-
-describe('create command', function () {
-    beforeEach(function() {
-        shell.rm('-rf', tempDir);
-    });
-
-    it('should print out help txt if no directory is provided', function() {
-        expect(cordova.create()).toMatch(/synopsis/i);
-    });
-    it('should create a cordova project in the specified directory if parameter is provided', function() {
-        cordova.create(tempDir);
-        var dotc = path.join(tempDir, '.cordova', 'config.json');
-        expect(fs.lstatSync(dotc).isFile()).toBe(true);
-        expect(JSON.parse(fs.readFileSync(dotc, 'utf8')).name).toBe("HelloCordova");
-        var hooks = path.join(tempDir, '.cordova', 'hooks');
-        expect(fs.existsSync(hooks)).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'before_platform_add'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'before_prepare'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'before_compile'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'after_platform_add'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'before_platform_rm'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'after_platform_rm'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'before_platform_ls'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'after_platform_ls'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'before_plugin_add'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'after_plugin_add'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'before_plugin_rm'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'after_plugin_rm'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'before_plugin_ls'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'after_plugin_ls'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'after_prepare'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'after_compile'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'before_build'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'after_build'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'before_emulate'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'after_emulate'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'before_docs'))).toBe(true);
-        expect(fs.existsSync(path.join(hooks, 'after_docs'))).toBe(true);
-    });
-    it('should throw if the directory is already a cordova project', function() {
-        shell.mkdir('-p', path.join(tempDir, '.cordova'));
-        
-        expect(function() {
-            cordova.create(tempDir);
-        }).toThrow();
-    });
-    it('should create a cordova project in the specified dir with specified name if provided', function() {
-        cordova.create(tempDir, "balls");
-
-        expect(fs.lstatSync(path.join(tempDir, '.cordova', 'config.json')).isFile()).toBe(true);
-
-        expect(fs.readFileSync(path.join(tempDir, 'www', 'config.xml')).toString('utf8')).toMatch(/<name>balls<\/name>/);
-    });
-    it('should create a cordova project in the specified dir with specified name and id if provided', function() {
-        cordova.create(tempDir, "birdy.nam.nam", "numnum");
-
-        expect(fs.lstatSync(path.join(tempDir, '.cordova', 'config.json')).isFile()).toBe(true);
-
-        var config = fs.readFileSync(path.join(tempDir, 'www', 'config.xml')).toString('utf8');
-        expect(config).toMatch(/<name>numnum<\/name>/);
-        expect(config).toMatch(/id="birdy\.nam\.nam"/);
-    });
-});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/emulate.spec.js
----------------------------------------------------------------------
diff --git a/spec/emulate.spec.js b/spec/emulate.spec.js
deleted file mode 100644
index 3a4d1a4..0000000
--- a/spec/emulate.spec.js
+++ /dev/null
@@ -1,191 +0,0 @@
-/**
-    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'),
-    et = require('elementtree'),
-    shell = require('shelljs'),
-    path = require('path'),
-    fs = require('fs'),
-    config_parser = require('../src/config_parser'),
-    android_parser = require('../src/metadata/android_parser'),
-    ios_parser = require('../src/metadata/ios_parser'),
-    blackberry_parser = require('../src/metadata/blackberry_parser'),
-    hooker = require('../src/hooker'),
-    fixtures = path.join(__dirname, 'fixtures'),
-    hooks = path.join(fixtures, 'hooks'),
-    tempDir = path.join(__dirname, '..', 'temp'),
-    cordova_project = path.join(fixtures, 'projects', 'cordova');
-
-var cwd = process.cwd();
-
-describe('emulate command', function() {
-    beforeEach(function() {
-        shell.rm('-rf', tempDir);
-        shell.mkdir('-p', tempDir);
-    });
-
-    it('should not run inside a Cordova-based project with no added platforms', function() {
-        this.after(function() {
-            process.chdir(cwd);
-        });
-
-        cordova.create(tempDir);
-        process.chdir(tempDir);
-        expect(function() {
-            cordova.emulate();
-        }).toThrow();
-    });
-    
-    it('should run inside a Cordova-based project with at least one added platform', function() {
-        shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
-        shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
-        this.after(function() {
-            process.chdir(cwd);
-            shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
-            shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
-        });
-
-        process.chdir(cordova_project);
-
-        var s = spyOn(shell, 'exec');
-        var a_spy = spyOn(android_parser.prototype, 'update_project');
-        expect(function() {
-            cordova.emulate();
-            a_spy.mostRecentCall.args[1](); // fake out android parser
-            expect(s).toHaveBeenCalled();
-        }).not.toThrow();
-    });
-    it('should not run outside of a Cordova-based project', function() {
-        this.after(function() {
-            process.chdir(cwd);
-        });
-
-        shell.mkdir('-p', tempDir);
-        process.chdir(tempDir);
-
-        expect(function() {
-            cordova.emulate();
-        }).toThrow();
-    });
-    describe('per platform', function() {
-        beforeEach(function() {
-            process.chdir(cordova_project);
-        });
-
-        afterEach(function() {
-            process.chdir(cwd);
-        });
-       
-        describe('Android', function() {
-            var s;
-            beforeEach(function() {
-                s = spyOn(require('shelljs'), 'exec');
-            });
-            it('should shell out to run command on Android', function() {
-                cordova.emulate('android');
-                expect(s.mostRecentCall.args[0].match(/\/cordova\/run/)).not.toBeNull();
-            });
-            it('should call android_parser\'s update_project', function() {
-                var spy = spyOn(android_parser.prototype, 'update_project');
-                cordova.emulate('android');
-                expect(spy).toHaveBeenCalled();
-            });
-        });
-        describe('iOS', function() {
-            it('should shell out to emulate command on iOS', function() {
-                var s = spyOn(require('shelljs'), 'exec');
-                var proj_spy = spyOn(ios_parser.prototype, 'update_project');
-                cordova.emulate('ios');
-                proj_spy.mostRecentCall.args[1]();
-                expect(s).toHaveBeenCalled();
-                expect(s.mostRecentCall.args[0].match(/\/cordova\/emulate/)).not.toBeNull();
-            });
-            it('should call ios_parser\'s update_project', function() {
-                var s = spyOn(ios_parser.prototype, 'update_project');
-                cordova.emulate('ios');
-                expect(s).toHaveBeenCalled();
-            });
-        });
-        describe('BlackBerry', function() {
-            it('should shell out to ant command on blackberry', function() {
-                var proj_spy = spyOn(blackberry_parser.prototype, 'update_project');
-                var s = spyOn(require('shelljs'), 'exec');
-                cordova.emulate('blackberry');
-                proj_spy.mostRecentCall.args[1](); // update_project fake
-                expect(s).toHaveBeenCalled();
-                expect(s.mostRecentCall.args[0]).toMatch(/ant -f .*build\.xml" qnx load-simulator/);
-            });
-            it('should call blackberry_parser\'s update_project', function() {
-                var s = spyOn(blackberry_parser.prototype, 'update_project');
-                cordova.emulate('blackberry');
-                expect(s).toHaveBeenCalled();
-            });
-        });
-    });
-
-    describe('hooks', function() {
-        var s, sh, ap;
-        beforeEach(function() {
-            s = spyOn(hooker.prototype, 'fire').andReturn(true);
-        });
-
-        describe('when platforms are added', function() {
-            beforeEach(function() {
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
-                sh = spyOn(shell, 'exec');
-                ap = spyOn(android_parser.prototype, 'update_project');
-                process.chdir(cordova_project);
-            });
-            afterEach(function() {
-                shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
-                shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
-                process.chdir(cwd);
-            });
-
-            it('should fire before hooks through the hooker module', function() {
-                cordova.emulate();
-                expect(s).toHaveBeenCalledWith('before_emulate');
-            });
-            it('should fire after hooks through the hooker module', function() {
-                cordova.emulate();
-                ap.mostRecentCall.args[1](); // fake parser call
-                sh.mostRecentCall.args[2](0); //fake shell call
-                expect(s).toHaveBeenCalledWith('after_emulate');
-            });
-        });
-
-        describe('with no platforms added', function() {
-            beforeEach(function() {
-                cordova.create(tempDir);
-                process.chdir(tempDir);
-            });
-            afterEach(function() {
-                process.chdir(cwd);
-            });
-            it('should not fire the hooker', function() {
-                spyOn(shell, 'exec');
-                expect(function() {
-                    cordova.emulate();
-                }).toThrow();
-                expect(s).not.toHaveBeenCalledWith('before_emulate');
-                expect(s).not.toHaveBeenCalledWith('after_emulate');
-            });
-        });
-    });
-});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/helper.js
----------------------------------------------------------------------
diff --git a/spec/helper.js b/spec/helper.js
deleted file mode 100644
index 2c4f331..0000000
--- a/spec/helper.js
+++ /dev/null
@@ -1,20 +0,0 @@
-
-/**
-    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.
-*/
-jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/hooker.spec.js
----------------------------------------------------------------------
diff --git a/spec/hooker.spec.js b/spec/hooker.spec.js
deleted file mode 100644
index 4a0ca7d..0000000
--- a/spec/hooker.spec.js
+++ /dev/null
@@ -1,137 +0,0 @@
-/**
-    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 hooker = require('../src/hooker'),
-    shell  = require('shelljs'),
-    path   = require('path'),
-    fs     = require('fs'),
-    tempDir= path.join(__dirname, '..', 'temp'),
-    hooks  = path.join(__dirname, 'fixtures', 'hooks'),
-    cordova= require('../cordova');
-
-var cwd = process.cwd();
-
-describe('hooker', function() {
-    it('should throw if provided directory is not a cordova project', function() {
-        shell.rm('-rf', tempDir);
-        shell.mkdir('-p', tempDir); 
-        this.after(function() {
-            shell.rm('-rf', tempDir);
-        });
-
-        expect(function() {
-            var h = new hooker(tempDir);
-        }).toThrow();
-    });
-    it('should not throw if provided directory is a cordova project', function() {
-        cordova.create(tempDir);
-        this.after(function() {
-            shell.rm('-rf', tempDir);
-        });
-
-        expect(function() {
-            var h = new hooker(tempDir);
-        }).not.toThrow();
-    });
-
-    describe('fire method', function() {
-        var h;
-
-        beforeEach(function() {
-            cordova.create(tempDir);
-            h = new hooker(tempDir);
-        });
-        afterEach(function() {
-            shell.rm('-rf', tempDir);
-        });
-
-        describe('failure', function() {
-            it('should not throw if the hook is unrecognized', function() {
-                expect(function() {
-                    h.fire('CLEAN YOUR SHORTS GODDAMNIT LIKE A BIG BOY!');
-                }).not.toThrow();
-            });
-            it('should throw if any script exits with non-zero code', function() {
-                var script = path.join(tempDir, '.cordova', 'hooks', 'before_build', 'fail.sh');
-                shell.cp(path.join(hooks, 'fail', 'fail.sh'), script);
-                fs.chmodSync(script, '754');
-                expect(function() {
-                    h.fire('before_build');
-                }).toThrow();
-            });
-        });
-
-        describe('success', function() {
-            it('should execute all scripts in order and return true', function() {
-                var hook = path.join(tempDir, '.cordova', 'hooks', 'before_build');
-                shell.cp(path.join(hooks, 'test', '*'), path.join(hook, '.'));
-                fs.readdirSync(hook).forEach(function(script) {
-                    fs.chmodSync(path.join(hook, script), '754');
-                });
-                var returnValue;
-                var s = spyOn(shell, 'exec').andReturn({code:0});
-                expect(function() {
-                    returnValue = h.fire('before_build');
-                }).not.toThrow();
-                expect(returnValue).toBe(true);
-                expect(s.calls[0].args[0]).toMatch(/0.sh/);
-                expect(s.calls[1].args[0]).toMatch(/1.sh/);
-            });
-            it('should pass the project root folder as parameter into the project-level hooks', function() {
-                var hook = path.join(tempDir, '.cordova', 'hooks', 'before_build');
-                shell.cp(path.join(hooks, 'test', '0.sh'), path.join(hook, '.'));
-                fs.readdirSync(hook).forEach(function(script) {
-                    fs.chmodSync(path.join(hook, script), '754');
-                });
-                var returnValue;
-                var s = spyOn(shell, 'exec').andReturn({code:0});
-                expect(function() {
-                    returnValue = h.fire('before_build');
-                }).not.toThrow();
-                expect(returnValue).toBe(true);
-                var paramRegex = new RegExp('0.sh "'+tempDir+'"$');
-                expect(s.calls[0].args[0]).toMatch(paramRegex);
-            });
-            describe('module-level hooks', function() {
-                var handler = jasmine.createSpy();
-                var test_event = 'before_build';
-                afterEach(function() {
-                    cordova.off(test_event, handler);
-                    handler.reset();
-                });
-
-                it('should fire handlers using cordova.on', function() {
-                    cordova.on(test_event, handler);
-                    h.fire(test_event);
-                    expect(handler).toHaveBeenCalled();
-                });
-                it('should pass the project root folder as parameter into the module-level handlers', function() {
-                    cordova.on(test_event, handler);
-                    h.fire('before_build');
-                    expect(handler).toHaveBeenCalledWith(tempDir);
-                });
-                it('should be able to stop listening to events using cordova.off', function() {
-                    cordova.on(test_event, handler);
-                    cordova.off(test_event, handler);
-                    h.fire('before_build');
-                    expect(handler).not.toHaveBeenCalled();
-                });
-            });
-        });
-    });
-});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/693521da/spec/metadata/android_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/metadata/android_parser.spec.js b/spec/metadata/android_parser.spec.js
deleted file mode 100644
index d051c6f..0000000
--- a/spec/metadata/android_parser.spec.js
+++ /dev/null
@@ -1,220 +0,0 @@
-
-/**
-    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 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 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');
-                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 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');
-                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/693521da/spec/metadata/blackberry_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/metadata/blackberry_parser.spec.js b/spec/metadata/blackberry_parser.spec.js
deleted file mode 100644
index 12d5294..0000000
--- a/spec/metadata/blackberry_parser.spec.js
+++ /dev/null
@@ -1,249 +0,0 @@
-
-/**
-    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 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');
-                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');
-                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 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');
-                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/693521da/spec/metadata/ios_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/metadata/ios_parser.spec.js b/spec/metadata/ios_parser.spec.js
deleted file mode 100644
index dbd4816..0000000
--- a/spec/metadata/ios_parser.spec.js
+++ /dev/null
@@ -1,218 +0,0 @@
-/**
- 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'),
-    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 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');
-                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 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');
-                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/693521da/spec/platform-script/android/android_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/android/android_parser.spec.js b/spec/platform-script/android/android_parser.spec.js
new file mode 100644
index 0000000..d051c6f
--- /dev/null
+++ b/spec/platform-script/android/android_parser.spec.js
@@ -0,0 +1,220 @@
+
+/**
+    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 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 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');
+                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 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');
+                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/693521da/spec/platform-script/blackberry/blackberry_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/blackberry/blackberry_parser.spec.js b/spec/platform-script/blackberry/blackberry_parser.spec.js
new file mode 100644
index 0000000..12d5294
--- /dev/null
+++ b/spec/platform-script/blackberry/blackberry_parser.spec.js
@@ -0,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 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');
+                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');
+                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 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');
+                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/693521da/spec/platform-script/ios/ios_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/ios/ios_parser.spec.js b/spec/platform-script/ios/ios_parser.spec.js
new file mode 100644
index 0000000..dbd4816
--- /dev/null
+++ b/spec/platform-script/ios/ios_parser.spec.js
@@ -0,0 +1,218 @@
+/**
+ 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'),
+    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 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');
+                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 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');
+                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();
+                });
+            });
+        });
+    });
+});


[07/50] git commit: Add WP7 and WP8 support to cordova-cli.

Posted by br...@apache.org.
Add WP7 and WP8 support to cordova-cli.


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

Branch: refs/heads/master2
Commit: 0fd190306eb17f49e9ec6a8162cf204f0ef0414d
Parents: 0fea2dd
Author: Benn Mapes <be...@gmail.com>
Authored: Tue May 14 15:27:19 2013 -0700
Committer: Ian Clelland <ic...@chromium.org>
Committed: Thu May 23 16:04:02 2013 -0400

----------------------------------------------------------------------
 bootstrap.js                                       |   13 +-
 package.json                                       |    1 +
 platforms.js                                       |    2 +-
 spec/cordova-cli/compile.spec.js                   |  106 +++----
 spec/cordova-cli/config_parser.spec.js             |    9 +-
 spec/cordova-cli/create.spec.js                    |    9 +-
 spec/cordova-cli/emulate.spec.js                   |  106 ++-----
 spec/cordova-cli/hooker.spec.js                    |   54 +++-
 spec/cordova-cli/platform.spec.js                  |  139 ++-------
 spec/cordova-cli/plugin.spec.js                    |    8 +-
 spec/cordova-cli/plugin_parser.spec.js             |    4 +-
 spec/cordova-cli/prepare.spec.js                   |   44 +--
 spec/cordova-cli/serve.spec.js                     |   16 +-
 spec/cordova-cli/util.spec.js                      |   34 ++
 spec/platform-script/android/android.spec.js       |   98 ++++++
 .../platform-script/android/android_parser.spec.js |   10 +-
 spec/platform-script/blackberry/blackberry.spec.js |  105 ++++++
 .../blackberry/blackberry_parser.spec.js           |   10 +-
 spec/platform-script/ios/ios.spec.js               |   99 ++++++
 spec/platform-script/ios/ios_parser.spec.js        |   13 +-
 spec/platform-script/wp7/wp7.spec.js               |   99 ++++++
 spec/platform-script/wp7/wp7_parser.spec.js        |  249 +++++++++++++++
 spec/platform-script/wp8/wp8.spec.js               |   99 ++++++
 spec/platform-script/wp8/wp8_parser.spec.js        |  249 +++++++++++++++
 src/compile.js                                     |    5 +-
 src/create.js                                      |    2 +-
 src/emulate.js                                     |   10 +-
 src/metadata/wp7_parser.js                         |  160 +++++++++
 src/metadata/wp8_parser.js                         |  158 +++++++++
 src/platform.js                                    |    6 +-
 src/plugin.js                                      |    1 +
 src/plugin_loader.js                               |    9 +
 src/util.js                                        |    6 +
 33 files changed, 1578 insertions(+), 355 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/bootstrap.js
----------------------------------------------------------------------
diff --git a/bootstrap.js b/bootstrap.js
index f96df76..8cf0135 100644
--- a/bootstrap.js
+++ b/bootstrap.js
@@ -25,6 +25,8 @@ var util      = require('./src/util'),
     a_parser  = require('./src/metadata/android_parser'),
     b_parser  = require('./src/metadata/blackberry_parser'),
     i_parser  = require('./src/metadata/ios_parser'),
+    wp7_parser= require('./src/metadata/wp7_parser'),
+    wp8_parser= require('./src/metadata/wp8_parser'),
     n         = require('ncallbacks'),
     path      = require('path'),
     fs        = require('fs'),
@@ -35,7 +37,9 @@ var util      = require('./src/util'),
 var min_reqs = {
     "android":a_parser.check_requirements,
     "ios":i_parser.check_requirements,
-    "blackberry":b_parser.check_requirements
+    "blackberry":b_parser.check_requirements,
+    "wp7":wp7_parser.check_requirements,
+    "wp8":wp7_parser.check_requirements
 }
 
 // Create native projects using bin/create
@@ -68,16 +72,15 @@ var end = n(platforms.length, function() {
 platforms.forEach(function(platform) {
     min_reqs[platform](function(err) {
         if (err) {
-            console.error('WARNING: Your system does not meet requirements to create ' + platform + 'projects. See error output below.');
+            console.error('WARNING: Your system does not meet requirements to create ' + platform + ' projects. See error output below.');
             console.error(err);
             console.error('SKIPPING ' + platform + ' bootstrap.');
         } else {
             console.log('SUCCESS: Minimum requirements for ' + platform + ' met.');
             var fix_path = path.join(tempDir, platform + '_fixture');
-            var create = path.join(util.libDirectory, 'cordova-' + platform, 'bin', 'create'); 
+            var create = path.join(util.libDirectory, 'cordova-' + platform, 'bin', 'create');
             console.log('BOOTSTRAPPING ' + platform + '...');
             var cmd = create + ' "' + fix_path + '" org.apache.cordova.cordovaExample cordovaExample';
-            if (platform == 'blackberry') cmd = create + ' "' + fix_path + '" cordovaExample';
             shell.exec(cmd, {silent:true, async:true}, function(code, output) {
                 if (code > 0) {
                     console.error('ERROR! Could not create a native ' + platform + ' project test fixture. See below for error output.');
@@ -109,4 +112,4 @@ platforms.forEach(function(platform) {
             });
         }
     });
-});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index 96e53bd..b706c93 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
     {"name": "Braden Shepherdson", "email":"braden@chromium.org"},
     {"name": "Gord Tanner", "email":"gtanner@gmail.com"},
     {"name": "Tim Kim", "email": "timk@adobe.com"},
+    {"name": "Benn Mapes", "email": "Benn.Mapes@gmail.com"},
     {"name": "Michael Wolf", "email": "Michael.Wolf@Cynergy.com"}
   ],
   "license": "Apache version 2.0"

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/platforms.js
----------------------------------------------------------------------
diff --git a/platforms.js b/platforms.js
index be295a8..542f734 100644
--- a/platforms.js
+++ b/platforms.js
@@ -17,4 +17,4 @@
     under the License.
 */
 
-module.exports = ['ios', 'android', 'blackberry'];
+module.exports = ['ios', 'android', 'blackberry', 'wp7', 'wp8'];

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/cordova-cli/compile.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/compile.spec.js b/spec/cordova-cli/compile.spec.js
index f13792a..814d2ab 100644
--- a/spec/cordova-cli/compile.spec.js
+++ b/spec/cordova-cli/compile.spec.js
@@ -16,22 +16,19 @@
     specific language governing permissions and limitations
     under the License.
 */
-var cordova = require('../cordova'),
-    et = require('elementtree'),
+var cordova = require('../../cordova'),
     shell = require('shelljs'),
     path = require('path'),
     fs = require('fs'),
-    config_parser = require('../src/config_parser'),
-    android_parser = require('../src/metadata/android_parser'),
-    ios_parser = require('../src/metadata/ios_parser'),
-    blackberry_parser = require('../src/metadata/blackberry_parser'),
-    hooker = require('../src/hooker'),
-    fixtures = path.join(__dirname, 'fixtures'),
+    events = require('../../src/events'),
+    hooker = require('../../src/hooker'),
+    fixtures = path.join(__dirname, '..', 'fixtures'),
     hooks = path.join(fixtures, 'hooks'),
-    tempDir = path.join(__dirname, '..', 'temp'),
+    tempDir = path.join(__dirname, '..', '..', 'temp'),
     cordova_project = path.join(fixtures, 'projects', 'cordova');
 
 var cwd = process.cwd();
+
 describe('compile command', function() {
     beforeEach(function() {
         shell.rm('-rf', tempDir);
@@ -53,12 +50,12 @@ describe('compile command', function() {
     it('should run inside a Cordova-based project with at least one added platform', function() {
         // move platform project fixtures over to fake cordova into thinking platforms were added
         // TODO: possibly add this to helper?
-        shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
-        shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
+        // Just make a folder instead of moving the whole platform? 
+        shell.mkdir('-p', tempDir);
+        shell.mv('-f', path.join(cordova_project, 'platforms', 'android'), path.join(tempDir));
         this.after(function() {
             process.chdir(cwd);
-            shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
-            shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+            shell.mv('-f', path.join(tempDir, 'android'), path.join(cordova_project, 'platforms', 'android'));
         });
 
         process.chdir(cordova_project);
@@ -78,10 +75,29 @@ describe('compile command', function() {
         shell.mkdir('-p', tempDir);
         process.chdir(tempDir);
 
+        // we don't actually want it building the project (if it does somehow exist)
+        var sh_spy = spyOn(shell, 'exec');
+
         expect(function() {
             cordova.compile();
         }).toThrow();
     });
+    /* Is this a repeat of the util.spec.js test? */
+    it('should not treat a .gitignore file as a platform', function() {
+        var gitignore = path.join(cordova_project, 'platforms', '.gitignore');
+        fs.writeFileSync(gitignore, 'somethinghere', 'utf-8');
+        this.after(function() {
+            shell.rm('-f', gitignore);
+            process.chdir(cwd);
+        });
+
+        var s = spyOn(shell, 'exec');
+        process.chdir(cordova_project);
+        cordova.compile();
+        for (call in s.calls) {
+            expect(s.calls[call].args[0]).not.toMatch(/\.gitignore/);
+        }
+    });
 
     describe('hooks', function() {
         var s;
@@ -91,13 +107,11 @@ describe('compile command', function() {
 
         describe('when platforms are added', function() {
             beforeEach(function() {
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
+                shell.mv('-f', path.join(cordova_project, 'platforms', 'android'), path.join(tempDir));
                 process.chdir(cordova_project);
             });
             afterEach(function() {
-                shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
-                shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+                shell.mv('-f', path.join(tempDir, 'android'), path.join(cordova_project, 'platforms', 'android'));
                 process.chdir(cwd);
             });
 
@@ -106,11 +120,14 @@ describe('compile command', function() {
                 cordova.compile();
                 expect(s).toHaveBeenCalledWith('before_compile');
             });
-            it('should fire after hooks through the hooker module', function() {
-                var sh_spy = spyOn(shell, 'exec');
-                cordova.compile();
-                sh_spy.mostRecentCall.args[2](0); // shell cb
-                expect(s).toHaveBeenCalledWith('after_compile');
+            it('should fire after hooks through the hooker module', function(done) {
+                spyOn(shell, 'exec').andCallFake(function(cmd, options, callback) {
+                    callback(0, 'fucking eh');
+                });
+                cordova.compile('android', function() {
+                     expect(hooker.prototype.fire).toHaveBeenCalledWith('after_compile');
+                     done();
+                });
             });
         });
 
@@ -131,49 +148,4 @@ describe('compile command', function() {
             });
         });
     });
-    describe('per platform', function() {
-        beforeEach(function() {
-            process.chdir(cordova_project);
-        });
-
-        afterEach(function() {
-            process.chdir(cwd);
-        });
-       
-        describe('Android', function() {
-            it('should shell out to build command on Android', function() {
-                var s = spyOn(require('shelljs'), 'exec').andReturn({code:0});
-                cordova.compile('android');
-                expect(s.mostRecentCall.args[0].match(/\/cordova\/build/)).not.toBeNull();
-            });
-        });
-        describe('iOS', function() {
-            it('should shell out to build command on iOS', function() {
-                var s = spyOn(require('shelljs'), 'exec');
-                cordova.compile('ios');
-                expect(s).toHaveBeenCalled();
-                expect(s.mostRecentCall.args[0].match(/\/cordova\/build/)).not.toBeNull();
-            });
-        });
-        describe('BlackBerry', function() {
-            it('should shell out to ant command on blackberry', function() {
-                var s = spyOn(shell, 'exec');
-                cordova.compile('blackberry');
-                expect(s).toHaveBeenCalled();
-                expect(s.mostRecentCall.args[0]).toMatch(/ant -f .*build\.xml" qnx load-device/);
-            });
-        });
-        it('should not treat a .gitignore file as a platform', function() {
-            var gitignore = path.join(cordova_project, 'platforms', '.gitignore');
-            fs.writeFileSync(gitignore, 'somethinghere', 'utf-8');
-            this.after(function() {
-                shell.rm('-f', gitignore);
-            });
-            var s = spyOn(shell, 'exec');
-            cordova.compile();
-            expect(s.calls[0].args[0]).not.toMatch(/\.gitignore/);
-            expect(s.calls[1].args[0]).not.toMatch(/\.gitignore/);
-            expect(s.calls[1].args[0]).not.toMatch(/\.gitignore/);
-        });
-    });
 });

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/cordova-cli/config_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/config_parser.spec.js b/spec/cordova-cli/config_parser.spec.js
index 99bc717..77658fb 100644
--- a/spec/cordova-cli/config_parser.spec.js
+++ b/spec/cordova-cli/config_parser.spec.js
@@ -17,14 +17,15 @@
     specific language governing permissions and limitations
     under the License.
 */
-var cordova = require('../cordova'),
+var cordova = require('../../cordova'),
     path = require('path'),
     fs = require('fs'),
     shell = require('shelljs'),
-    config_parser = require('../src/config_parser'),
-    tempDir = path.join(__dirname, '..', 'temp'),
+    config_parser = require('../../src/config_parser'),
+    tempDir = path.join(__dirname, '..', '..', 'temp'),
     et = require('elementtree'),
-    xml = path.join(tempDir, 'www', 'config.xml');
+    util = require('../../src/util'),
+    xml = util.projectConfig(tempDir);
 
 
 describe('config.xml parser', function () {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/cordova-cli/create.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/create.spec.js b/spec/cordova-cli/create.spec.js
index fcb3e76..8a31ed6 100644
--- a/spec/cordova-cli/create.spec.js
+++ b/spec/cordova-cli/create.spec.js
@@ -1,8 +1,9 @@
-var cordova = require('../cordova'),
+var cordova = require('../../cordova'),
     path    = require('path'),
     shell   = require('shelljs'),
     fs      = require('fs'),
-    tempDir = path.join(__dirname, '..', 'temp');
+    util    = require('../../src/util'),
+    tempDir = path.join(__dirname, '..', '..', 'temp');
 
 describe('create command', function () {
     beforeEach(function() {
@@ -54,14 +55,14 @@ describe('create command', function () {
 
         expect(fs.lstatSync(path.join(tempDir, '.cordova', 'config.json')).isFile()).toBe(true);
 
-        expect(fs.readFileSync(path.join(tempDir, 'www', 'config.xml')).toString('utf8')).toMatch(/<name>balls<\/name>/);
+        expect(fs.readFileSync(util.projectConfig(tempDir)).toString('utf8')).toMatch(/<name>balls<\/name>/);
     });
     it('should create a cordova project in the specified dir with specified name and id if provided', function() {
         cordova.create(tempDir, "birdy.nam.nam", "numnum");
 
         expect(fs.lstatSync(path.join(tempDir, '.cordova', 'config.json')).isFile()).toBe(true);
 
-        var config = fs.readFileSync(path.join(tempDir, 'www', 'config.xml')).toString('utf8');
+        var config = fs.readFileSync(util.projectConfig(tempDir)).toString('utf8');
         expect(config).toMatch(/<name>numnum<\/name>/);
         expect(config).toMatch(/id="birdy\.nam\.nam"/);
     });

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/cordova-cli/emulate.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/emulate.spec.js b/spec/cordova-cli/emulate.spec.js
index 3a4d1a4..a3e5136 100644
--- a/spec/cordova-cli/emulate.spec.js
+++ b/spec/cordova-cli/emulate.spec.js
@@ -16,19 +16,17 @@
     specific language governing permissions and limitations
     under the License.
 */
-var cordova = require('../cordova'),
+var cordova = require('../../cordova'),
     et = require('elementtree'),
     shell = require('shelljs'),
     path = require('path'),
     fs = require('fs'),
-    config_parser = require('../src/config_parser'),
-    android_parser = require('../src/metadata/android_parser'),
-    ios_parser = require('../src/metadata/ios_parser'),
-    blackberry_parser = require('../src/metadata/blackberry_parser'),
-    hooker = require('../src/hooker'),
-    fixtures = path.join(__dirname, 'fixtures'),
+    config_parser = require('../../src/config_parser'),
+    android_parser = require('../../src/metadata/android_parser'),
+    hooker = require('../../src/hooker'),
+    fixtures = path.join(__dirname, '..', 'fixtures'),
     hooks = path.join(fixtures, 'hooks'),
-    tempDir = path.join(__dirname, '..', 'temp'),
+    tempDir = path.join(__dirname, '..', '..', 'temp'),
     cordova_project = path.join(fixtures, 'projects', 'cordova');
 
 var cwd = process.cwd();
@@ -36,7 +34,7 @@ var cwd = process.cwd();
 describe('emulate command', function() {
     beforeEach(function() {
         shell.rm('-rf', tempDir);
-        shell.mkdir('-p', tempDir);
+        cordova.create(tempDir);
     });
 
     it('should not run inside a Cordova-based project with no added platforms', function() {
@@ -44,7 +42,6 @@ describe('emulate command', function() {
             process.chdir(cwd);
         });
 
-        cordova.create(tempDir);
         process.chdir(tempDir);
         expect(function() {
             cordova.emulate();
@@ -52,19 +49,15 @@ describe('emulate command', function() {
     });
     
     it('should run inside a Cordova-based project with at least one added platform', function() {
-        shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
-        shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
         this.after(function() {
             process.chdir(cwd);
-            shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
-            shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
         });
 
-        process.chdir(cordova_project);
-
         var s = spyOn(shell, 'exec');
         var a_spy = spyOn(android_parser.prototype, 'update_project');
         expect(function() {
+            shell.cp('-Rf', path.join(cordova_project, 'platforms', 'android'), path.join(tempDir, 'platforms'));
+            process.chdir(tempDir);
             cordova.emulate();
             a_spy.mostRecentCall.args[1](); // fake out android parser
             expect(s).toHaveBeenCalled();
@@ -82,97 +75,40 @@ describe('emulate command', function() {
             cordova.emulate();
         }).toThrow();
     });
-    describe('per platform', function() {
-        beforeEach(function() {
-            process.chdir(cordova_project);
-        });
-
-        afterEach(function() {
-            process.chdir(cwd);
-        });
-       
-        describe('Android', function() {
-            var s;
-            beforeEach(function() {
-                s = spyOn(require('shelljs'), 'exec');
-            });
-            it('should shell out to run command on Android', function() {
-                cordova.emulate('android');
-                expect(s.mostRecentCall.args[0].match(/\/cordova\/run/)).not.toBeNull();
-            });
-            it('should call android_parser\'s update_project', function() {
-                var spy = spyOn(android_parser.prototype, 'update_project');
-                cordova.emulate('android');
-                expect(spy).toHaveBeenCalled();
-            });
-        });
-        describe('iOS', function() {
-            it('should shell out to emulate command on iOS', function() {
-                var s = spyOn(require('shelljs'), 'exec');
-                var proj_spy = spyOn(ios_parser.prototype, 'update_project');
-                cordova.emulate('ios');
-                proj_spy.mostRecentCall.args[1]();
-                expect(s).toHaveBeenCalled();
-                expect(s.mostRecentCall.args[0].match(/\/cordova\/emulate/)).not.toBeNull();
-            });
-            it('should call ios_parser\'s update_project', function() {
-                var s = spyOn(ios_parser.prototype, 'update_project');
-                cordova.emulate('ios');
-                expect(s).toHaveBeenCalled();
-            });
-        });
-        describe('BlackBerry', function() {
-            it('should shell out to ant command on blackberry', function() {
-                var proj_spy = spyOn(blackberry_parser.prototype, 'update_project');
-                var s = spyOn(require('shelljs'), 'exec');
-                cordova.emulate('blackberry');
-                proj_spy.mostRecentCall.args[1](); // update_project fake
-                expect(s).toHaveBeenCalled();
-                expect(s.mostRecentCall.args[0]).toMatch(/ant -f .*build\.xml" qnx load-simulator/);
-            });
-            it('should call blackberry_parser\'s update_project', function() {
-                var s = spyOn(blackberry_parser.prototype, 'update_project');
-                cordova.emulate('blackberry');
-                expect(s).toHaveBeenCalled();
-            });
-        });
-    });
 
     describe('hooks', function() {
-        var s, sh, ap;
+        var s;
         beforeEach(function() {
             s = spyOn(hooker.prototype, 'fire').andReturn(true);
         });
 
         describe('when platforms are added', function() {
             beforeEach(function() {
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
-                sh = spyOn(shell, 'exec');
-                ap = spyOn(android_parser.prototype, 'update_project');
-                process.chdir(cordova_project);
+                shell.cp('-Rf', path.join(cordova_project, 'platforms', 'android'), path.join(tempDir, 'platforms'));
+                process.chdir(tempDir);
             });
             afterEach(function() {
-                shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
-                shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
                 process.chdir(cwd);
             });
 
             it('should fire before hooks through the hooker module', function() {
+
+                spyOn(shell, 'exec');
                 cordova.emulate();
-                expect(s).toHaveBeenCalledWith('before_emulate');
+                expect(hooker.prototype.fire).toHaveBeenCalledWith('before_emulate');
             });
             it('should fire after hooks through the hooker module', function() {
-                cordova.emulate();
-                ap.mostRecentCall.args[1](); // fake parser call
-                sh.mostRecentCall.args[2](0); //fake shell call
-                expect(s).toHaveBeenCalledWith('after_emulate');
+                spyOn(shell, 'exec').andCallFake(function(cmd, options, callback) {
+                    callback(0, 'fucking eh');
+                });
+                cordova.emulate('android', function() {
+                     expect(hooker.prototype.fire).toHaveBeenCalledWith('after_emulate');
+                });
             });
         });
 
         describe('with no platforms added', function() {
             beforeEach(function() {
-                cordova.create(tempDir);
                 process.chdir(tempDir);
             });
             afterEach(function() {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/cordova-cli/hooker.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/hooker.spec.js b/spec/cordova-cli/hooker.spec.js
index 4a0ca7d..ff92f43 100644
--- a/spec/cordova-cli/hooker.spec.js
+++ b/spec/cordova-cli/hooker.spec.js
@@ -1,4 +1,4 @@
-/**
+ /**
     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
@@ -16,14 +16,16 @@
     specific language governing permissions and limitations
     under the License.
 */
-var hooker = require('../src/hooker'),
+var hooker = require('../../src/hooker'),
     shell  = require('shelljs'),
     path   = require('path'),
     fs     = require('fs'),
-    tempDir= path.join(__dirname, '..', 'temp'),
-    hooks  = path.join(__dirname, 'fixtures', 'hooks'),
-    cordova= require('../cordova');
+    os     = require('os'),
+    tempDir= path.join(__dirname, '..', '..', 'temp'),
+    hooks  = path.join(__dirname, '..', 'fixtures', 'hooks'),
+    cordova= require('../../cordova');
 
+var platform = os.platform();
 var cwd = process.cwd();
 
 describe('hooker', function() {
@@ -67,8 +69,14 @@ describe('hooker', function() {
                 }).not.toThrow();
             });
             it('should throw if any script exits with non-zero code', function() {
-                var script = path.join(tempDir, '.cordova', 'hooks', 'before_build', 'fail.sh');
-                shell.cp(path.join(hooks, 'fail', 'fail.sh'), script);
+                var script;
+                if (platform.match(/(win32|win64)/)) {
+                    script = path.join(tempDir, '.cordova', 'hooks', 'before_build', 'fail.bat');
+                    shell.cp(path.join(hooks, 'fail', 'fail.bat'), script);
+                } else {
+                    script = path.join(tempDir, '.cordova', 'hooks', 'before_build', 'fail.sh');
+                    shell.cp(path.join(hooks, 'fail', 'fail.sh'), script);
+                }
                 fs.chmodSync(script, '754');
                 expect(function() {
                     h.fire('before_build');
@@ -79,7 +87,13 @@ describe('hooker', function() {
         describe('success', function() {
             it('should execute all scripts in order and return true', function() {
                 var hook = path.join(tempDir, '.cordova', 'hooks', 'before_build');
-                shell.cp(path.join(hooks, 'test', '*'), path.join(hook, '.'));
+                if (platform.match(/(win32|win64)/)) {
+                    shell.cp(path.join(hooks, 'test', '0.bat'), hook);
+                    shell.cp(path.join(hooks, 'test', '1.bat'), hook);
+                } else {
+                    shell.cp(path.join(hooks, 'test', '0.sh'), hook);
+                    shell.cp(path.join(hooks, 'test', '1.sh'), hook);
+                }
                 fs.readdirSync(hook).forEach(function(script) {
                     fs.chmodSync(path.join(hook, script), '754');
                 });
@@ -89,12 +103,21 @@ describe('hooker', function() {
                     returnValue = h.fire('before_build');
                 }).not.toThrow();
                 expect(returnValue).toBe(true);
-                expect(s.calls[0].args[0]).toMatch(/0.sh/);
-                expect(s.calls[1].args[0]).toMatch(/1.sh/);
+                if (platform.match(/(win32|win64)/)) {
+                    expect(s.calls[0].args[0]).toMatch(/0.bat/);
+                    expect(s.calls[1].args[0]).toMatch(/1.bat/);
+                } else {
+                    expect(s.calls[0].args[0]).toMatch(/0.sh/);
+                    expect(s.calls[1].args[0]).toMatch(/1.sh/);
+                }
             });
             it('should pass the project root folder as parameter into the project-level hooks', function() {
                 var hook = path.join(tempDir, '.cordova', 'hooks', 'before_build');
-                shell.cp(path.join(hooks, 'test', '0.sh'), path.join(hook, '.'));
+                if (platform.match(/(win32|win64)/)) {
+                    shell.cp(path.join(hooks, 'test', '0.bat'), hook);
+                } else {
+                    shell.cp(path.join(hooks, 'test', '0.sh'), hook);
+                }
                 fs.readdirSync(hook).forEach(function(script) {
                     fs.chmodSync(path.join(hook, script), '754');
                 });
@@ -104,8 +127,13 @@ describe('hooker', function() {
                     returnValue = h.fire('before_build');
                 }).not.toThrow();
                 expect(returnValue).toBe(true);
-                var paramRegex = new RegExp('0.sh "'+tempDir+'"$');
-                expect(s.calls[0].args[0]).toMatch(paramRegex);
+                var param_str;
+                if (platform.match(/(win32|win64)/)) {
+                    param_str = '0.bat "'+tempDir+'"';
+                } else { 
+                    param_str = '0.sh "'+tempDir+'"'; 
+                }
+                expect(s.calls[0].args[0].indexOf(param_str)).not.toEqual(-1);
             });
             describe('module-level hooks', function() {
                 var handler = jasmine.createSpy();

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/cordova-cli/platform.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/platform.spec.js b/spec/cordova-cli/platform.spec.js
index 7138c7e..bbf3959 100644
--- a/spec/cordova-cli/platform.spec.js
+++ b/spec/cordova-cli/platform.spec.js
@@ -16,22 +16,21 @@
     specific language governing permissions and limitations
     under the License.
 */
-var cordova = require('../cordova'),
+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'),
+    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'),
-    ios_parser = require('../src/metadata/ios_parser'),
-    cordova_project = path.join(__dirname, 'fixtures', 'projects', 'cordova'),
-    blackberry_parser = require('../src/metadata/blackberry_parser');
+    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();
 
@@ -84,7 +83,11 @@ describe('platform command', function() {
         });
 
         it('should list out added platforms in a project', function() {
-            expect(cordova.platform('list').length).toEqual(3);
+            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);
         });
     });
 
@@ -97,109 +100,23 @@ describe('platform command', function() {
         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(path.join(cordova_project, 'www', 'config.xml'), 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(path.join(cordova_project, 'www', 'config.xml'), 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 irc = spyOn(ios_parser, 'check_requirements');
+            var brc = spyOn(blackberry_parser, 'check_requirements');
             var sh = spyOn(shell, 'exec');
-            cordova.platform('add', ['android', 'ios']);
+            cordova.platform('add', ['android', 'blackberry']);
             arc.mostRecentCall.args[0](false);
-            irc.mostRecentCall.args[0](false);
-            expect(sh.argsForCall[0][0]).toMatch(/android\/bin\/create/);
-            expect(sh.argsForCall[1][0]).toMatch(/ios\/bin\/create/);
+            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() { 
+    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);
@@ -212,11 +129,11 @@ describe('platform command', function() {
 
         it('should remove a supported and added platform', function() {
             cordova.platform('remove', 'android');
-            expect(cordova.platform('ls').length).toEqual(2);
+            expect(cordova.platform('ls').length).toEqual(num_platforms - 1);
         });
         it('should be able to remove multiple platforms', function() {
-            cordova.platform('remove', ['android','ios']);
-            expect(cordova.platform('ls').length).toEqual(1);
+            cordova.platform('remove', ['android','blackberry']);
+            expect(cordova.platform('ls').length).toEqual(num_platforms - 2);
         });
     });
 
@@ -280,7 +197,7 @@ describe('platform command', function() {
 });
 
 describe('platform.supports(name, callback)', function() {
-    var androidParser = require('../src/metadata/android_parser');
+    var androidParser = require('../../src/metadata/android_parser');
 
     beforeEach(function() {
         spyOn(androidParser, 'check_requirements');

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/cordova-cli/plugin.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/plugin.spec.js b/spec/cordova-cli/plugin.spec.js
index 3d370e7..061738f 100644
--- a/spec/cordova-cli/plugin.spec.js
+++ b/spec/cordova-cli/plugin.spec.js
@@ -16,13 +16,13 @@
     specific language governing permissions and limitations
     under the License.
 */
-var cordova = require('../cordova'),
+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'),
+    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');

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/cordova-cli/plugin_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/plugin_parser.spec.js b/spec/cordova-cli/plugin_parser.spec.js
index 4391003..15b5993 100644
--- a/spec/cordova-cli/plugin_parser.spec.js
+++ b/spec/cordova-cli/plugin_parser.spec.js
@@ -17,10 +17,10 @@
     specific language governing permissions and limitations
     under the License.
 */
-var cordova = require('../cordova'),
+var cordova = require('../../cordova'),
     path = require('path'),
     fs = require('fs'),
-    plugin_parser = require('../src/plugin_parser'),
+    plugin_parser = require('../../src/plugin_parser'),
     et = require('elementtree'),
     xml = path.join(__dirname, '..', 'fixtures', 'plugins', 'test', 'plugin.xml');
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/cordova-cli/prepare.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/prepare.spec.js b/spec/cordova-cli/prepare.spec.js
index f232b3e..da77935 100644
--- a/spec/cordova-cli/prepare.spec.js
+++ b/spec/cordova-cli/prepare.spec.js
@@ -16,19 +16,16 @@
     specific language governing permissions and limitations
     under the License.
 */
-var cordova = require('../cordova'),
+var cordova = require('../../cordova'),
     et = require('elementtree'),
     shell = require('shelljs'),
     path = require('path'),
     fs = require('fs'),
-    config_parser = require('../src/config_parser'),
-    android_parser = require('../src/metadata/android_parser'),
-    ios_parser = require('../src/metadata/ios_parser'),
-    blackberry_parser = require('../src/metadata/blackberry_parser'),
-    hooker = require('../src/hooker'),
-    fixtures = path.join(__dirname, 'fixtures'),
+    config_parser = require('../../src/config_parser'),
+    hooker = require('../../src/hooker'),
+    fixtures = path.join(__dirname, '..', 'fixtures'),
     hooks = path.join(fixtures, 'hooks'),
-    tempDir = path.join(__dirname, '..', 'temp'),
+    tempDir = path.join(__dirname, '..', '..', 'temp'),
     cordova_project = path.join(fixtures, 'projects', 'cordova');
 
 var cwd = process.cwd();
@@ -36,7 +33,7 @@ var cwd = process.cwd();
 describe('prepare command', function() {
     beforeEach(function() {
         shell.rm('-rf', tempDir);
-        shell.mkdir('-p', tempDir);
+        cordova.create(tempDir);
     });
 
     it('should not run inside a Cordova-based project with no added platforms', function() {
@@ -44,7 +41,6 @@ describe('prepare command', function() {
             process.chdir(cwd);
         });
 
-        cordova.create(tempDir);
         process.chdir(tempDir);
         expect(function() {
             cordova.prepare();
@@ -54,20 +50,17 @@ describe('prepare command', function() {
     it('should run inside a Cordova-based project with at least one added platform', function() {
         // move platform project fixtures over to fake cordova into thinking platforms were added
         // TODO: possibly add this to helper?
-        shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
         this.after(function() {
             process.chdir(cwd);
-            shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
         });
 
-        process.chdir(cordova_project);
-
-        var a_parser_spy = spyOn(android_parser.prototype, 'update_project');
-        var i_parser_spy = spyOn(ios_parser.prototype, 'update_project');
+        spyOn(shell, 'exec');
         expect(function() {
+            shell.cp('-Rf', path.join(cordova_project, 'platforms', 'android'), path.join(tempDir, 'platforms'));
+            process.chdir(tempDir);
+            var a_parser_spy = spyOn(android_parser.prototype, 'update_project');
             cordova.prepare();
             expect(a_parser_spy).toHaveBeenCalled();
-            expect(i_parser_spy).toHaveBeenCalled();
         }).not.toThrow();
     });
     it('should not run outside of a Cordova-based project', function() {
@@ -91,13 +84,11 @@ describe('prepare command', function() {
 
         describe('when platforms are added', function() {
             beforeEach(function() {
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
-                shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
-                process.chdir(cordova_project);
+                shell.cp('-rf', path.join(cordova_project, 'platforms', 'android'), path.join(tempDir, 'platforms'));
+                process.chdir(tempDir);
             });
             afterEach(function() {
-                shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
-                shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+                shell.rm('-rf', path.join(tempDir, 'platforms', 'android'));
                 process.chdir(cwd);
             });
 
@@ -106,15 +97,16 @@ describe('prepare command', function() {
                 expect(s).toHaveBeenCalledWith('before_prepare');
             });
             it('should fire after hooks through the hooker module', function() {
-                var parser_spy = spyOn(android_parser.prototype, 'update_project');
-                cordova.prepare();
-                parser_spy.mostRecentCall.args[1](); // parser cb
-                expect(s).toHaveBeenCalledWith('after_prepare');
+                spyOn(shell, 'exec');
+                cordova.prepare('android', function() {
+                     expect(hooker.prototype.fire).toHaveBeenCalledWith('after_prepare');
+                });
             });
         });
 
         describe('with no platforms added', function() {
             beforeEach(function() {
+                shell.rm('-rf', tempDir);
                 cordova.create(tempDir);
                 process.chdir(tempDir);
             });

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/cordova-cli/serve.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/serve.spec.js b/spec/cordova-cli/serve.spec.js
index 8d00cab..2e61212 100644
--- a/spec/cordova-cli/serve.spec.js
+++ b/spec/cordova-cli/serve.spec.js
@@ -17,18 +17,20 @@
     specific language governing permissions and limitations
     under the License.
 */
-var cordova = require('../cordova'),
+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'),
+    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');
+    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();
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/cordova-cli/util.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova-cli/util.spec.js b/spec/cordova-cli/util.spec.js
new file mode 100644
index 0000000..787b00f
--- /dev/null
+++ b/spec/cordova-cli/util.spec.js
@@ -0,0 +1,34 @@
+var cordova = require('../../cordova'),
+    shell = require('shelljs'),
+    path = require('path'),
+    fs = require('fs'),
+    cordova_util = require('../../src/util'),
+    fixtures = path.join(__dirname, '..', 'fixtures'),
+    cordova_project = path.join(fixtures, 'projects', 'cordova');
+
+
+var cwd = process.cwd();
+
+describe('util command', function() {
+    beforeEach(function() {
+        process.chdir(cordova_project);
+    });
+    afterEach(function() {
+        process.chdir(cwd);
+    });
+    describe('listPlatforms', function() {
+        it('should not treat a .gitignore file as a platform', function() {
+            var gitignore = path.join(cordova_project, 'platforms', '.gitignore');
+            fs.writeFileSync(gitignore, 'somethinghere', 'utf-8');
+            this.after(function() {
+                shell.rm('-f', gitignore);
+            });
+
+            var s = spyOn(shell, 'exec');
+            var platforms = cordova_util.listPlatforms(cordova_project);
+            platforms.forEach(function(platform) {
+                expect(platform).not.toMatch(/\.gitignore/);
+            }); 
+        });
+    });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/platform-script/android/android.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/android/android.spec.js b/spec/platform-script/android/android.spec.js
new file mode 100644
index 0000000..8c39af0
--- /dev/null
+++ b/spec/platform-script/android/android.spec.js
@@ -0,0 +1,98 @@
+var cordova = require('../../../cordova'),
+    shell = require('shelljs'),
+    path = require('path'),
+    fs = require('fs'),
+    android_parser = require('../../../src/metadata/android_parser'),
+    tempDir = path.join(__dirname, '..', '..', '..', 'temp'),
+    fixtures = path.join(__dirname, '..', '..', 'fixtures'),
+    cordova_project = path.join(fixtures, 'projects', 'cordova');
+
+var cwd = process.cwd();
+
+describe('Test:', function() {
+    afterEach(function() {
+        process.chdir(cwd);
+    });
+
+    describe('\'platform add android\'', function() {
+        var sh, cr;
+        var fake_reqs_check = function() {
+            expect(cr.mostRecentCall.args).toBeDefined();
+            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');
+            shell.rm('-rf', tempDir);
+            cordova.create(tempDir);
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        it('should shell out to android /bin/create', function() {
+            cordova.platform('add', 'android');
+            fake_reqs_check();
+            var shell_cmd = sh.mostRecentCall.args[0];
+            var create_cmd = path.join('android', 'bin', 'create');
+            expect(shell_cmd).toContain(create_cmd);
+        });
+        it('should call android_parser\'s update_project', function() {
+            spyOn(android_parser.prototype, 'update_project');
+            cordova.platform('add', 'android');
+            fake_reqs_check();
+            fake_create(path.join(tempDir, 'platforms', 'android'));
+            expect(android_parser.prototype.update_project).toHaveBeenCalled();
+        });
+    });
+
+    describe('\'emulate android\'', function() {
+        beforeEach(function() {
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        shell.rm('-rf', tempDir);
+        cordova.create(tempDir);
+        shell.cp('-rf', path.join(cordova_project, 'platforms', 'android'), path.join(tempDir, 'platforms'));
+        it('should shell out to run command on Android', function() {
+            var proj_spy = spyOn(android_parser.prototype, 'update_project');
+            var s = spyOn(require('shelljs'), 'exec');
+            cordova.emulate('android');
+            proj_spy.mostRecentCall.args[1](); // update_project fake
+            expect(s).toHaveBeenCalled();
+            var emulate_cmd = path.join('android', 'cordova', 'run');
+            expect(s.mostRecentCall.args[0]).toContain(emulate_cmd);
+        });
+        it('should call android_parser\'s update_project', function() {
+            spyOn(require('shelljs'), 'exec');
+            spyOn(android_parser.prototype, 'update_project');
+            cordova.emulate('android');
+            expect(android_parser.prototype.update_project).toHaveBeenCalled();
+        });
+    });
+
+    describe('\'compile android\'', function() {
+        beforeEach(function() {
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        shell.rm('-rf', tempDir);
+        cordova.create(tempDir);
+        shell.cp('-rf', path.join(cordova_project, 'platforms', 'android'), path.join(tempDir, 'platforms'));
+        it('should shell out to build command', function() {
+            var build_cmd = path.join('android', 'cordova', 'build');
+            var s = spyOn(require('shelljs'), 'exec').andReturn({code:0});
+            cordova.compile('android');
+            expect(s.mostRecentCall.args[0]).toContain(build_cmd);
+        });
+    });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/platform-script/android/android_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/android/android_parser.spec.js b/spec/platform-script/android/android_parser.spec.js
index d051c6f..84c7903 100644
--- a/spec/platform-script/android/android_parser.spec.js
+++ b/spec/platform-script/android/android_parser.spec.js
@@ -17,15 +17,15 @@
     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'),
+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'),
+    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');

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/platform-script/blackberry/blackberry.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/blackberry/blackberry.spec.js b/spec/platform-script/blackberry/blackberry.spec.js
new file mode 100644
index 0000000..ff997c1
--- /dev/null
+++ b/spec/platform-script/blackberry/blackberry.spec.js
@@ -0,0 +1,105 @@
+var cordova = require('../../../cordova'),
+    shell = require('shelljs'),
+    path = require('path'),
+    fs = require('fs'),
+    blackberry_parser = require('../../../src/metadata/blackberry_parser'),
+    tempDir = path.join(__dirname, '..', '..', '..', 'temp'),
+    fixtures = path.join(__dirname, '..', '..', 'fixtures'),
+    cordova_project = path.join(fixtures, 'projects', 'cordova');
+
+var cwd = process.cwd();
+
+describe('Test:', function() {
+
+    afterEach(function() {
+        process.chdir(cwd);
+    });
+
+    describe('\'platform add blackberry\'', function() {
+        var sh, cr;
+        var fake_reqs_check = function() {
+            expect(cr.mostRecentCall.args).toBeDefined();
+            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('-rf', path.join(cordova_project, 'platforms', 'blackberry', 'www', 'config.xml'), path.join(a_path, 'www'));
+            sh.mostRecentCall.args[2](0, '');
+        };
+        beforeEach(function() {
+            sh = spyOn(shell, 'exec');
+            cr = spyOn(blackberry_parser, 'check_requirements');
+            shell.rm('-rf', tempDir);
+            cordova.create(tempDir);
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        it('should check requirements when adding', function() {
+            cordova.platform('add', 'blackberry');
+            expect(blackberry_parser.check_requirements).toHaveBeenCalled();
+        });
+        it('should shell out to blackberry bin/create', function() {
+            cordova.platform('add', 'blackberry');
+            fake_reqs_check();
+            var shell_cmd = sh.mostRecentCall.args[0];
+            var create_cmd = path.join('blackberry', 'bin', 'create');
+            expect(shell_cmd).toContain(create_cmd);
+        });
+        it('should call blackberry_parser\'s update_project', function() {
+            spyOn(blackberry_parser.prototype, 'update_project');
+            cordova.platform('add', 'blackberry');
+            fake_reqs_check();
+            fake_create(path.join(tempDir, 'platforms', 'blackberry'));
+            expect(blackberry_parser.prototype.update_project).toHaveBeenCalled();
+        });
+    });
+
+    describe('\'emulate blackberry\'', function() {
+        beforeEach(function() {
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        shell.rm('-rf', tempDir);
+        cordova.create(tempDir);
+        shell.cp('-rf', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir, 'platforms'));
+        it('should shell out to ant command on blackberry', function() {
+            var proj_spy = spyOn(blackberry_parser.prototype, 'update_project');
+            var s = spyOn(require('shelljs'), 'exec');
+            cordova.emulate('blackberry');
+            proj_spy.mostRecentCall.args[1](); // update_project fake
+            expect(s).toHaveBeenCalled();
+            var emulate_cmd = 'ant -f .*build\.xml" qnx load-simulator';
+            expect(s.mostRecentCall.args[0]).toMatch(emulate_cmd);
+        });
+        it('should call blackberry_parser\'s update_project', function() {
+            spyOn(require('shelljs'), 'exec');
+            spyOn(blackberry_parser.prototype, 'update_project');
+            cordova.emulate('blackberry');
+            expect(blackberry_parser.prototype.update_project).toHaveBeenCalled();
+        });
+    });
+
+    describe('\'compile blackberry\'', function() {
+        beforeEach(function() {
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        shell.rm('-rf', tempDir);
+        cordova.create(tempDir);
+        shell.cp('-rf', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir, 'platforms'));
+        it('should shell out to build command', function() {
+            var build_cmd = 'build.xml" qnx load-device';
+            var s = spyOn(require('shelljs'), 'exec').andReturn({code:0});
+            cordova.compile('blackberry');
+            expect(s.mostRecentCall.args[0]).toContain(build_cmd);
+        });
+    });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/platform-script/blackberry/blackberry_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/blackberry/blackberry_parser.spec.js b/spec/platform-script/blackberry/blackberry_parser.spec.js
index 12d5294..d183f7f 100644
--- a/spec/platform-script/blackberry/blackberry_parser.spec.js
+++ b/spec/platform-script/blackberry/blackberry_parser.spec.js
@@ -17,15 +17,15 @@
     specific language governing permissions and limitations
     under the License.
 */
-var blackberry_parser = require('../../src/metadata/blackberry_parser'),
-    config_parser = require('../../src/config_parser'),
+var blackberry_parser = require('../../../src/metadata/blackberry_parser'),
+    config_parser = require('../../../src/config_parser'),
     path = require('path'),
-    util = require('../../src/util'),
+    util = require('../../../src/util'),
     et = require('elementtree'),
     shell = require('shelljs'),
-    cordova = require('../../cordova'),
+    cordova = require('../../../cordova'),
     fs = require('fs'),
-    projects_path = path.join(__dirname, '..', 'fixtures', 'projects'),
+    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');

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/platform-script/ios/ios.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/ios/ios.spec.js b/spec/platform-script/ios/ios.spec.js
new file mode 100644
index 0000000..0fecd75
--- /dev/null
+++ b/spec/platform-script/ios/ios.spec.js
@@ -0,0 +1,99 @@
+var cordova = require('../../../cordova'),
+    shell = require('shelljs'),
+    path = require('path'),
+    fs = require('fs'),
+    ios_parser = require('../../../src/metadata/ios_parser'),
+    tempDir = path.join(__dirname, '..', '..', '..', 'temp'),
+    fixtures = path.join(__dirname, '..', '..', 'fixtures'),
+    cordova_project = path.join(fixtures, 'projects', 'cordova');
+
+var cwd = process.cwd();
+
+describe('Test:', function() {
+    afterEach(function() {
+        process.chdir(cwd);
+    });
+
+    describe('\'platform add 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(path.join(cordova_project, 'www', 'config.xml'), path.join(a_path, 'poo', 'config.xml'));
+            sh.mostRecentCall.args[2](0, '');
+        };
+        beforeEach(function() {
+            sh = spyOn(shell, 'exec');
+            cr = spyOn(ios_parser, 'check_requirements');
+            shell.rm('-rf', tempDir);
+            cordova.create(tempDir);
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        it('should shell out to ios /bin/create', function() {
+            cordova.platform('add', 'ios');
+            fake_reqs_check();
+            var shell_cmd = sh.mostRecentCall.args[0];
+            var create_cmd = path.join('ios', 'bin', 'create');
+            expect(shell_cmd).toContain(create_cmd);
+        });
+        it('should call ios_parser\'s update_project', function() {
+            spyOn(ios_parser.prototype, 'update_project');
+            cordova.platform('add', 'ios');
+            fake_reqs_check();
+            fake_create(path.join(tempDir, 'platforms', 'ios'));
+            expect(ios_parser.prototype.update_project).toHaveBeenCalled();
+        });
+    });
+
+    describe('\'emulate ios\'', function() {
+        beforeEach(function() {
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        shell.rm('-rf', tempDir);
+        cordova.create(tempDir);
+        shell.cp('-rf', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir, 'platforms'));
+        it('should shell out to run command on ios', function() {
+            var proj_spy = spyOn(ios_parser.prototype, 'update_project');
+            var s = spyOn(require('shelljs'), 'exec');
+            cordova.emulate('ios');
+            proj_spy.mostRecentCall.args[1](); // update_project fake
+            expect(s).toHaveBeenCalled();
+            var emulate_cmd = path.join('ios', 'cordova', 'run');
+            expect(s.mostRecentCall.args[0]).toContain(emulate_cmd);
+        });
+        it('should call ios_parser\'s update_project', function() {
+            spyOn(require('shelljs'), 'exec');
+            spyOn(ios_parser.prototype, 'update_project');
+            cordova.emulate('ios');
+            expect(ios_parser.prototype.update_project).toHaveBeenCalled();
+        });
+    });
+
+    describe('\'compile ios\'', function() {
+        beforeEach(function() {
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        shell.rm('-rf', tempDir);
+        cordova.create(tempDir);
+        shell.cp('-rf', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir, 'platforms'));
+        it('should shell out to build command', function() {
+            var build_cmd = path.join('ios', 'cordova', 'build');
+            var s = spyOn(require('shelljs'), 'exec').andReturn({code:0});
+            cordova.compile('ios');
+            expect(s.mostRecentCall.args[0]).toContain(build_cmd);
+        });
+    });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/platform-script/ios/ios_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/ios/ios_parser.spec.js b/spec/platform-script/ios/ios_parser.spec.js
index dbd4816..b0c2aeb 100644
--- a/spec/platform-script/ios/ios_parser.spec.js
+++ b/spec/platform-script/ios/ios_parser.spec.js
@@ -17,16 +17,17 @@
  under the License.
  */
 
-var ios_parser = require('../../src/metadata/ios_parser'),
-    config_parser = require('../../src/config_parser'),
-    cordova = require('../../cordova'),
-    util = require('../../src/util'),
+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'),
+    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');
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/platform-script/wp7/wp7.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/wp7/wp7.spec.js b/spec/platform-script/wp7/wp7.spec.js
new file mode 100644
index 0000000..6b7f3b4
--- /dev/null
+++ b/spec/platform-script/wp7/wp7.spec.js
@@ -0,0 +1,99 @@
+var cordova = require('../../../cordova'),
+    shell = require('shelljs'),
+    path = require('path'),
+    fs = require('fs'),
+    wp7_parser = require('../../../src/metadata/wp7_parser'),
+    tempDir = path.join(__dirname, '..', '..', '..', 'temp'),
+    fixtures = path.join(__dirname, '..', '..', 'fixtures'),
+    cordova_project = path.join(fixtures, 'projects', 'cordova');
+
+var cwd = process.cwd();
+
+describe('Test:', function() {
+    afterEach(function() {
+        process.chdir(cwd);
+    });
+
+    describe('\'platform add wp7\'', function() {
+        var sh, cr;
+        var fake_reqs_check = function() {
+            expect(cr.mostRecentCall.args).toBeDefined();
+            cr.mostRecentCall.args[0](false);
+        };
+        var fake_create = function(a_path) {
+            shell.mkdir('-p', a_path);
+            fs.writeFileSync(path.join(a_path, 'wp7Project.csproj'), 'hi', 'utf-8');
+            fs.writeFileSync(path.join(a_path, 'wp7Project.sln'), 'hi', 'utf-8');
+            sh.mostRecentCall.args[2](0, '');
+        };
+        beforeEach(function() {
+            sh = spyOn(shell, 'exec');
+            cr = spyOn(wp7_parser, 'check_requirements');
+            shell.rm('-rf', tempDir);
+            cordova.create(tempDir);
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        it('should shell out to wp7 /bin/create', function() {
+            cordova.platform('add', 'wp7');
+            fake_reqs_check();
+            var shell_cmd = sh.mostRecentCall.args[0];
+            var create_cmd = path.join('wp7', 'bin', 'create');
+            expect(shell_cmd).toContain(create_cmd);
+        });
+        it('should call wp7_parser\'s update_project', function() {
+            spyOn(wp7_parser.prototype, 'update_project');
+            cordova.platform('add', 'wp7');
+            fake_reqs_check();
+            fake_create(path.join(tempDir, 'platforms', 'wp7'));
+            expect(wp7_parser.prototype.update_project).toHaveBeenCalled();
+        });
+    });
+
+    describe('\'emulate wp7\'', function() {
+        beforeEach(function() {
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        shell.rm('-rf', tempDir);
+        cordova.create(tempDir);
+        shell.cp('-rf', path.join(cordova_project, 'platforms', 'wp7'), path.join(tempDir, 'platforms'));
+        it('should shell out to run command on wp7', function() {
+            var proj_spy = spyOn(wp7_parser.prototype, 'update_project');
+            var s = spyOn(require('shelljs'), 'exec');
+            cordova.emulate('wp7');
+            proj_spy.mostRecentCall.args[1](); // update_project fake
+            expect(s).toHaveBeenCalled();
+            var emulate_cmd = path.join('wp7', 'cordova', 'run');
+            expect(s.mostRecentCall.args[0]).toContain(emulate_cmd);
+        });
+        it('should call wp7_parser\'s update_project', function() {
+            spyOn(require('shelljs'), 'exec');
+            spyOn(wp7_parser.prototype, 'update_project');
+            cordova.emulate('wp7');
+            expect(wp7_parser.prototype.update_project).toHaveBeenCalled();
+        });
+    });
+
+    describe('\'compile wp7\'', function() {
+        beforeEach(function() {
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        shell.rm('-rf', tempDir);
+        cordova.create(tempDir);
+        shell.cp('-rf', path.join(cordova_project, 'platforms', 'wp7'), path.join(tempDir, 'platforms'));
+        it('should shell out to build command', function() {
+            var build_cmd = path.join('wp7', 'cordova', 'build');
+            var s = spyOn(require('shelljs'), 'exec').andReturn({code:0});
+            cordova.compile('wp7');
+            expect(s.mostRecentCall.args[0]).toContain(build_cmd);
+        });
+    });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/platform-script/wp7/wp7_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/wp7/wp7_parser.spec.js b/spec/platform-script/wp7/wp7_parser.spec.js
new file mode 100644
index 0000000..7840bff
--- /dev/null
+++ b/spec/platform-script/wp7/wp7_parser.spec.js
@@ -0,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 wp7_parser = require('../../../src/metadata/wp7_parser'),
+    config_parser = require('../../../src/config_parser'),
+    util = require('../../../src/util'),
+    path = require('path'),
+    shell = require('shelljs'),
+    fs = require('fs'),
+    os = require('os'),
+    et = require('elementtree'),
+    cordova = require('../../../cordova'),
+    projects_path = path.join(__dirname, '..', '..', 'fixtures', 'projects'),
+    wp7_path = path.join(projects_path, 'native', 'wp7_fixture'),
+    project_path = path.join(projects_path, 'cordova'),
+    wp7_project_path = path.join(project_path, 'platforms', 'wp7');
+
+var www_config = util.projectConfig(project_path);
+var original_www_config = fs.readFileSync(www_config, 'utf-8');
+
+describe('wp7 project parser', function() {
+    it('should throw an exception with a path that is not a native wp7 project', function() {
+        expect(function() {
+            var project = new wp7_parser(process.cwd());
+        }).toThrow();
+    });
+    it('should accept a proper native wp7 project path as construction parameter', function() {
+        expect(function() {
+            var project = new wp7_parser(wp7_path);
+            expect(project).toBeDefined();
+        }).not.toThrow();
+    });
+
+    describe('update_from_config method', function() {
+        var config;
+        var project = new wp7_parser(wp7_path);
+
+        var manifest_path  = path.join(wp7_path, 'Properties', 'WMAppManifest.xml');
+        var csproj_path    = project.csproj_path;
+        var sln_path       = project.sln_path;
+        var app_xaml_path  = path.join(wp7_path, 'App.xaml');
+        var app_cs_path    = path.join(wp7_path, 'App.xaml.cs');
+        var main_xaml_path = path.join(wp7_path, 'MainPage.xaml');
+        var main_cs_path   = path.join(wp7_path, 'MainPage.xaml.cs');
+
+
+        var original_manifest  = fs.readFileSync(manifest_path, 'utf-8');
+        var original_csproj    = fs.readFileSync(csproj_path, 'utf-8');
+        var original_sln       = fs.readFileSync(sln_path, 'utf-8');
+        var original_app_xaml  = fs.readFileSync(app_xaml_path, 'utf-8');
+        var original_app_cs    = fs.readFileSync(app_cs_path, 'utf-8');
+        var original_main_xaml = fs.readFileSync(main_xaml_path, 'utf-8');
+        var original_main_cs   = fs.readFileSync(main_cs_path, 'utf-8');
+
+        beforeEach(function() {
+            project = new wp7_parser(wp7_path);
+            config = new config_parser(www_config);
+        });
+        afterEach(function() {
+            fs.writeFileSync(manifest_path, original_manifest, 'utf-8');
+            // csproj file changes name if app changes name
+            fs.unlinkSync(project.csproj_path);
+            fs.unlinkSync(project.sln_path);
+            fs.writeFileSync(csproj_path, original_csproj, 'utf-8');
+            fs.writeFileSync(sln_path, original_sln, 'utf-8');
+            fs.writeFileSync(app_xaml_path, original_app_xaml, 'utf-8');
+            fs.writeFileSync(app_cs_path, original_app_cs, 'utf-8');
+            fs.writeFileSync(main_xaml_path, original_main_xaml, 'utf-8');
+            fs.writeFileSync(main_cs_path, original_main_cs, '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() {
+            var test_name = 'bond. james bond.';
+            config.name(test_name);
+            project.update_from_config(config);
+            var raw_manifest = fs.readFileSync(manifest_path, 'utf-8');
+            //Strip three bytes that windows adds (http://www.multiasking.com/2012/11/851)
+            var cleaned_manifest = raw_manifest.replace('\ufeff', '');
+            var manifest = new et.ElementTree(et.XML(cleaned_manifest));
+            var app_name = manifest.find('.//App[@Title]')['attrib']['Title'];
+            expect(app_name).toBe(test_name);
+
+            //check for the proper name of csproj and solution files
+            test_name = test_name.replace(/(\.\s|\s\.|\s+|\.+)/g, '_'); //make it a ligitamate name
+            expect(project.csproj_path).toContain(test_name);
+            expect(project.sln_path).toContain(test_name);
+        });
+        it('should update the application package name properly', function() {
+            var test_package = 'ca.filmaj.dewd'
+            config.packageName(test_package);
+            project.update_from_config(config);
+
+            // check csproj file (use regex instead of elementtree?)
+            var raw_csproj = fs.readFileSync(project.csproj_path, 'utf-8');
+            var cleaned_csproj = raw_csproj.replace(/^\uFEFF/i, '');
+            var csproj = new et.ElementTree(et.XML(cleaned_csproj));
+            expect(csproj.find('.//RootNamespace').text).toEqual(test_package);
+            expect(csproj.find('.//AssemblyName').text).toEqual(test_package);
+            expect(csproj.find('.//XapFilename').text).toEqual(test_package + '.xap');
+            expect(csproj.find('.//SilverlightAppEntry').text).toEqual(test_package + '.App');
+
+            // check app.xaml (use regex instead of elementtree?)
+            var new_app_xaml = fs.readFileSync(app_xaml_path, 'utf-8');
+            var cleaned_app_xaml = new_app_xaml.replace(/^\uFEFF/i, '');
+            var app_xaml = new et.ElementTree(et.XML(cleaned_app_xaml));
+            expect(app_xaml._root.attrib['x:Class']).toEqual(test_package + '.App');
+
+            // check app.xaml.cs
+            var new_app_cs = fs.readFileSync(app_cs_path, 'utf-8');
+            expect(new_app_cs).toContain('namespace ' + test_package);
+
+            // check MainPage.xaml (use regex instead of elementtree?)
+            var new_main_xaml = fs.readFileSync(main_xaml_path, 'utf-8');
+            var cleaned_main_xaml = new_main_xaml.replace(/^\uFEFF/i, '');
+            var main_xaml = new et.ElementTree(et.XML(cleaned_main_xaml));
+            expect(main_xaml._root.attrib['x:Class']).toEqual(test_package + '.MainPage');
+
+            //check MainPage.xaml.cs
+            var new_main_cs = fs.readFileSync(main_cs_path, 'utf-8');
+            expect(new_main_cs).toContain('namespace ' + test_package);
+        });
+        xdescribe('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');*/
+
+                // TODO : figure out if this is supported
+                //expect(true).toBe(false);
+            });
+            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');*/
+
+                // TODO : figure out if this is supported
+                //expect(true).toBe(false);
+            });
+        });
+    });
+
+    describe('cross-platform project level methods', function() {
+        var parser, config;
+
+        beforeEach(function() {
+            parser = new wp7_parser(wp7_project_path);
+            config = new config_parser(www_config);
+        });
+        afterEach(function() {
+        });
+        describe('update_www method', function() {
+            it('should update all www assets', function() {
+                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(wp7_project_path, 'www', 'somescript.js'))).toBe(true);
+            });
+            it('should write out windows-phone js to cordova.js', function() {
+                parser.update_www();
+                var raw_version = fs.readFileSync(path.join(util.libDirectory, 'cordova-wp7', 'VERSION'), 'utf-8');
+                var wp7_version = raw_version.replace(/\r\n/,'').replace(/\n/,'');
+                expect(fs.readFileSync(path.join(wp7_project_path, 'www', 'cordova.js'),'utf-8')).toEqual(fs.readFileSync(path.join(util.libDirectory, 'cordova-wp7', 'templates', 'standalone', 'www', 'cordova-' + wp7_version + '.js'), 'utf-8'));
+            });
+        });
+
+        xdescribe('update_overrides method',function() {
+            /*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(wp7_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(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(wp7_project_path, 'assets', 'www', 'merge.js'))).toBe(true);
+                expect(fs.readFileSync(path.join(wp7_project_path, 'assets', 'www', 'merge.js'),'utf-8')).toEqual('alert("sup");');
+            });*/
+
+            // TODO : figure out if this is supported
+            //expect(true).toBe(false);
+        });
+
+        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();
+            });
+        });
+    });
+});
\ No newline at end of file


[47/50] Fix bootstrap, app/ dir is fully reverted now.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/android_mdpi_portrait.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/android_mdpi_portrait.png b/templates/www/res/screen/android_mdpi_portrait.png
new file mode 100644
index 0000000..ea15693
Binary files /dev/null and b/templates/www/res/screen/android_mdpi_portrait.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/android_xhdpi_landscape.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/android_xhdpi_landscape.png b/templates/www/res/screen/android_xhdpi_landscape.png
new file mode 100644
index 0000000..79f2f09
Binary files /dev/null and b/templates/www/res/screen/android_xhdpi_landscape.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/android_xhdpi_portrait.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/android_xhdpi_portrait.png b/templates/www/res/screen/android_xhdpi_portrait.png
new file mode 100644
index 0000000..c2e8042
Binary files /dev/null and b/templates/www/res/screen/android_xhdpi_portrait.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/blackberry_transparent_300.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/blackberry_transparent_300.png b/templates/www/res/screen/blackberry_transparent_300.png
new file mode 100644
index 0000000..b548bdc
Binary files /dev/null and b/templates/www/res/screen/blackberry_transparent_300.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/blackberry_transparent_400.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/blackberry_transparent_400.png b/templates/www/res/screen/blackberry_transparent_400.png
new file mode 100644
index 0000000..3facdf9
Binary files /dev/null and b/templates/www/res/screen/blackberry_transparent_400.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/ipad_landscape.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/ipad_landscape.png b/templates/www/res/screen/ipad_landscape.png
new file mode 100644
index 0000000..04be5ac
Binary files /dev/null and b/templates/www/res/screen/ipad_landscape.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/ipad_portrait.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/ipad_portrait.png b/templates/www/res/screen/ipad_portrait.png
new file mode 100644
index 0000000..41e839d
Binary files /dev/null and b/templates/www/res/screen/ipad_portrait.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/ipad_retina_landscape.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/ipad_retina_landscape.png b/templates/www/res/screen/ipad_retina_landscape.png
new file mode 100644
index 0000000..95c542d
Binary files /dev/null and b/templates/www/res/screen/ipad_retina_landscape.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/ipad_retina_portrait.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/ipad_retina_portrait.png b/templates/www/res/screen/ipad_retina_portrait.png
new file mode 100644
index 0000000..aae1862
Binary files /dev/null and b/templates/www/res/screen/ipad_retina_portrait.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/iphone_landscape.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/iphone_landscape.png b/templates/www/res/screen/iphone_landscape.png
new file mode 100644
index 0000000..d154883
Binary files /dev/null and b/templates/www/res/screen/iphone_landscape.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/iphone_portrait.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/iphone_portrait.png b/templates/www/res/screen/iphone_portrait.png
new file mode 100644
index 0000000..6fcba56
Binary files /dev/null and b/templates/www/res/screen/iphone_portrait.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/iphone_retina_landscape.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/iphone_retina_landscape.png b/templates/www/res/screen/iphone_retina_landscape.png
new file mode 100644
index 0000000..0165669
Binary files /dev/null and b/templates/www/res/screen/iphone_retina_landscape.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/iphone_retina_portrait.png
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/iphone_retina_portrait.png b/templates/www/res/screen/iphone_retina_portrait.png
new file mode 100644
index 0000000..bd24886
Binary files /dev/null and b/templates/www/res/screen/iphone_retina_portrait.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/res/screen/windows_phone_portrait.jpg
----------------------------------------------------------------------
diff --git a/templates/www/res/screen/windows_phone_portrait.jpg b/templates/www/res/screen/windows_phone_portrait.jpg
new file mode 100644
index 0000000..9f95387
Binary files /dev/null and b/templates/www/res/screen/windows_phone_portrait.jpg differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/spec.html
----------------------------------------------------------------------
diff --git a/templates/www/spec.html b/templates/www/spec.html
new file mode 100644
index 0000000..83d7d2e
--- /dev/null
+++ b/templates/www/spec.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/spec/helper.js
----------------------------------------------------------------------
diff --git a/templates/www/spec/helper.js b/templates/www/spec/helper.js
new file mode 100644
index 0000000..9f99445
--- /dev/null
+++ b/templates/www/spec/helper.js
@@ -0,0 +1,11 @@
+afterEach(function() {
+    document.getElementById('stage').innerHTML = '';
+});
+
+var helper = {
+    trigger: function(obj, name) {
+        var e = document.createEvent('Event');
+        e.initEvent(name, true, true);
+        obj.dispatchEvent(e);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/spec/index.js
----------------------------------------------------------------------
diff --git a/templates/www/spec/index.js b/templates/www/spec/index.js
new file mode 100644
index 0000000..121cf63
--- /dev/null
+++ b/templates/www/spec/index.js
@@ -0,0 +1,49 @@
+describe('app', function() {
+    describe('initialize', function() {
+        it('should bind deviceready', function() {
+            runs(function() {
+                spyOn(app, 'deviceready');
+                app.initialize();
+                helper.trigger(window.document, 'deviceready');
+            });
+
+            waitsFor(function() {
+                return (app.deviceready.calls.length > 0);
+            }, 'deviceready should be called once', 500);
+
+            runs(function() {
+                expect(app.deviceready).toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('deviceready', function() {
+        it('should report that it fired', function() {
+            spyOn(app, 'report');
+            app.deviceready();
+            expect(app.report).toHaveBeenCalledWith('deviceready');
+        });
+    });
+
+    describe('report', function() {
+        beforeEach(function() {
+            var el = document.getElementById('stage');
+            el.innerHTML = ['<div id="deviceready">',
+                            '    <p class="status pending">Pending</p>',
+                            '    <p class="status complete hide">Complete</p>',
+                            '</div>'].join('\n');
+        });
+
+        it('should show the completion state', function() {
+            app.report('deviceready');
+            var el = document.querySelector('#deviceready .complete:not(.hide)');
+            expect(el).toBeTruthy();
+        });
+
+        it('should hide the pending state', function() {
+            app.report('deviceready');
+            var el = document.querySelector('#deviceready .pending.hide');
+            expect(el).toBeTruthy();
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/spec/lib/jasmine-1.2.0/MIT.LICENSE
----------------------------------------------------------------------
diff --git a/templates/www/spec/lib/jasmine-1.2.0/MIT.LICENSE b/templates/www/spec/lib/jasmine-1.2.0/MIT.LICENSE
new file mode 100644
index 0000000..7c435ba
--- /dev/null
+++ b/templates/www/spec/lib/jasmine-1.2.0/MIT.LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008-2011 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/spec/lib/jasmine-1.2.0/jasmine-html.js
----------------------------------------------------------------------
diff --git a/templates/www/spec/lib/jasmine-1.2.0/jasmine-html.js b/templates/www/spec/lib/jasmine-1.2.0/jasmine-html.js
new file mode 100644
index 0000000..a0b0639
--- /dev/null
+++ b/templates/www/spec/lib/jasmine-1.2.0/jasmine-html.js
@@ -0,0 +1,616 @@
+jasmine.HtmlReporterHelpers = {};
+
+jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
+  var el = document.createElement(type);
+
+  for (var i = 2; i < arguments.length; i++) {
+    var child = arguments[i];
+
+    if (typeof child === 'string') {
+      el.appendChild(document.createTextNode(child));
+    } else {
+      if (child) {
+        el.appendChild(child);
+      }
+    }
+  }
+
+  for (var attr in attrs) {
+    if (attr == "className") {
+      el[attr] = attrs[attr];
+    } else {
+      el.setAttribute(attr, attrs[attr]);
+    }
+  }
+
+  return el;
+};
+
+jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
+  var results = child.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.skipped) {
+    status = 'skipped';
+  }
+
+  return status;
+};
+
+jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
+  var parentDiv = this.dom.summary;
+  var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
+  var parent = child[parentSuite];
+
+  if (parent) {
+    if (typeof this.views.suites[parent.id] == 'undefined') {
+      this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
+    }
+    parentDiv = this.views.suites[parent.id].element;
+  }
+
+  parentDiv.appendChild(childElement);
+};
+
+
+jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
+  for(var fn in jasmine.HtmlReporterHelpers) {
+    ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
+  }
+};
+
+jasmine.HtmlReporter = function(_doc) {
+  var self = this;
+  var doc = _doc || window.document;
+
+  var reporterView;
+
+  var dom = {};
+
+  // Jasmine Reporter Public Interface
+  self.logRunningSpecs = false;
+
+  self.reportRunnerStarting = function(runner) {
+    var specs = runner.specs() || [];
+
+    if (specs.length == 0) {
+      return;
+    }
+
+    createReporterDom(runner.env.versionString());
+    doc.body.appendChild(dom.reporter);
+
+    reporterView = new jasmine.HtmlReporter.ReporterView(dom);
+    reporterView.addSpecs(specs, self.specFilter);
+  };
+
+  self.reportRunnerResults = function(runner) {
+    reporterView && reporterView.complete();
+  };
+
+  self.reportSuiteResults = function(suite) {
+    reporterView.suiteComplete(suite);
+  };
+
+  self.reportSpecStarting = function(spec) {
+    if (self.logRunningSpecs) {
+      self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+    }
+  };
+
+  self.reportSpecResults = function(spec) {
+    reporterView.specComplete(spec);
+  };
+
+  self.log = function() {
+    var console = jasmine.getGlobal().console;
+    if (console && console.log) {
+      if (console.log.apply) {
+        console.log.apply(console, arguments);
+      } else {
+        console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+      }
+    }
+  };
+
+  self.specFilter = function(spec) {
+    if (!focusedSpecName()) {
+      return true;
+    }
+
+    return spec.getFullName().indexOf(focusedSpecName()) === 0;
+  };
+
+  return self;
+
+  function focusedSpecName() {
+    var specName;
+
+    (function memoizeFocusedSpec() {
+      if (specName) {
+        return;
+      }
+
+      var paramMap = [];
+      var params = doc.location.search.substring(1).split('&');
+
+      for (var i = 0; i < params.length; i++) {
+        var p = params[i].split('=');
+        paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
+      }
+
+      specName = paramMap.spec;
+    })();
+
+    return specName;
+  }
+
+  function createReporterDom(version) {
+    dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
+      dom.banner = self.createDom('div', { className: 'banner' },
+        self.createDom('span', { className: 'title' }, "Jasmine "),
+        self.createDom('span', { className: 'version' }, version)),
+
+      dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
+      dom.alert = self.createDom('div', {className: 'alert'}),
+      dom.results = self.createDom('div', {className: 'results'},
+        dom.summary = self.createDom('div', { className: 'summary' }),
+        dom.details = self.createDom('div', { id: 'details' }))
+    );
+  }
+};
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) {
+  this.startedAt = new Date();
+  this.runningSpecCount = 0;
+  this.completeSpecCount = 0;
+  this.passedCount = 0;
+  this.failedCount = 0;
+  this.skippedCount = 0;
+
+  this.createResultsMenu = function() {
+    this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
+      this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
+      ' | ',
+      this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
+
+    this.summaryMenuItem.onclick = function() {
+      dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
+    };
+
+    this.detailsMenuItem.onclick = function() {
+      showDetails();
+    };
+  };
+
+  this.addSpecs = function(specs, specFilter) {
+    this.totalSpecCount = specs.length;
+
+    this.views = {
+      specs: {},
+      suites: {}
+    };
+
+    for (var i = 0; i < specs.length; i++) {
+      var spec = specs[i];
+      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
+      if (specFilter(spec)) {
+        this.runningSpecCount++;
+      }
+    }
+  };
+
+  this.specComplete = function(spec) {
+    this.completeSpecCount++;
+
+    if (isUndefined(this.views.specs[spec.id])) {
+      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
+    }
+
+    var specView = this.views.specs[spec.id];
+
+    switch (specView.status()) {
+      case 'passed':
+        this.passedCount++;
+        break;
+
+      case 'failed':
+        this.failedCount++;
+        break;
+
+      case 'skipped':
+        this.skippedCount++;
+        break;
+    }
+
+    specView.refresh();
+    this.refresh();
+  };
+
+  this.suiteComplete = function(suite) {
+    var suiteView = this.views.suites[suite.id];
+    if (isUndefined(suiteView)) {
+      return;
+    }
+    suiteView.refresh();
+  };
+
+  this.refresh = function() {
+
+    if (isUndefined(this.resultsMenu)) {
+      this.createResultsMenu();
+    }
+
+    // currently running UI
+    if (isUndefined(this.runningAlert)) {
+      this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
+      dom.alert.appendChild(this.runningAlert);
+    }
+    this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
+
+    // skipped specs UI
+    if (isUndefined(this.skippedAlert)) {
+      this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
+    }
+
+    this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
+
+    if (this.skippedCount === 1 && isDefined(dom.alert)) {
+      dom.alert.appendChild(this.skippedAlert);
+    }
+
+    // passing specs UI
+    if (isUndefined(this.passedAlert)) {
+      this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
+    }
+    this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
+
+    // failing specs UI
+    if (isUndefined(this.failedAlert)) {
+      this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
+    }
+    this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
+
+    if (this.failedCount === 1 && isDefined(dom.alert)) {
+      dom.alert.appendChild(this.failedAlert);
+      dom.alert.appendChild(this.resultsMenu);
+    }
+
+    // summary info
+    this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
+    this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
+  };
+
+  this.complete = function() {
+    dom.alert.removeChild(this.runningAlert);
+
+    this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
+
+    if (this.failedCount === 0) {
+      dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
+    } else {
+      showDetails();
+    }
+
+    dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
+  };
+
+  return this;
+
+  function showDetails() {
+    if (dom.reporter.className.search(/showDetails/) === -1) {
+      dom.reporter.className += " showDetails";
+    }
+  }
+
+  function isUndefined(obj) {
+    return typeof obj === 'undefined';
+  }
+
+  function isDefined(obj) {
+    return !isUndefined(obj);
+  }
+
+  function specPluralizedFor(count) {
+    var str = count + " spec";
+    if (count > 1) {
+      str += "s"
+    }
+    return str;
+  }
+
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
+
+
+jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
+  this.spec = spec;
+  this.dom = dom;
+  this.views = views;
+
+  this.symbol = this.createDom('li', { className: 'pending' });
+  this.dom.symbolSummary.appendChild(this.symbol);
+
+  this.summary = this.createDom('div', { className: 'specSummary' },
+      this.createDom('a', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
+        title: this.spec.getFullName()
+      }, this.spec.description)
+  );
+
+  this.detail = this.createDom('div', { className: 'specDetail' },
+      this.createDom('a', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
+        title: this.spec.getFullName()
+      }, this.spec.getFullName())
+  );
+};
+
+jasmine.HtmlReporter.SpecView.prototype.status = function() {
+  return this.getSpecStatus(this.spec);
+};
+
+jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
+  this.symbol.className = this.status();
+
+  switch (this.status()) {
+    case 'skipped':
+      break;
+
+    case 'passed':
+      this.appendSummaryToSuiteDiv();
+      break;
+
+    case 'failed':
+      this.appendSummaryToSuiteDiv();
+      this.appendFailureDetail();
+      break;
+  }
+};
+
+jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
+  this.summary.className += ' ' + this.status();
+  this.appendToSummary(this.spec, this.summary);
+};
+
+jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
+  this.detail.className += ' ' + this.status();
+
+  var resultItems = this.spec.results().getItems();
+  var messagesDiv = this.createDom('div', { className: 'messages' });
+
+  for (var i = 0; i < resultItems.length; i++) {
+    var result = resultItems[i];
+
+    if (result.type == 'log') {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+    } else if (result.type == 'expect' && result.passed && !result.passed()) {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+      if (result.trace.stack) {
+        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+      }
+    }
+  }
+
+  if (messagesDiv.childNodes.length > 0) {
+    this.detail.appendChild(messagesDiv);
+    this.dom.details.appendChild(this.detail);
+  }
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
+  this.suite = suite;
+  this.dom = dom;
+  this.views = views;
+
+  this.element = this.createDom('div', { className: 'suite' },
+      this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
+  );
+
+  this.appendToSummary(this.suite, this.element);
+};
+
+jasmine.HtmlReporter.SuiteView.prototype.status = function() {
+  return this.getSpecStatus(this.suite);
+};
+
+jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
+  this.element.className += " " + this.status();
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
+
+/* @deprecated Use jasmine.HtmlReporter instead
+ */
+jasmine.TrivialReporter = function(doc) {
+  this.document = doc || document;
+  this.suiteDivs = {};
+  this.logRunningSpecs = false;
+};
+
+jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
+  var el = document.createElement(type);
+
+  for (var i = 2; i < arguments.length; i++) {
+    var child = arguments[i];
+
+    if (typeof child === 'string') {
+      el.appendChild(document.createTextNode(child));
+    } else {
+      if (child) { el.appendChild(child); }
+    }
+  }
+
+  for (var attr in attrs) {
+    if (attr == "className") {
+      el[attr] = attrs[attr];
+    } else {
+      el.setAttribute(attr, attrs[attr]);
+    }
+  }
+
+  return el;
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
+  var showPassed, showSkipped;
+
+  this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
+      this.createDom('div', { className: 'banner' },
+        this.createDom('div', { className: 'logo' },
+            this.createDom('span', { className: 'title' }, "Jasmine"),
+            this.createDom('span', { className: 'version' }, runner.env.versionString())),
+        this.createDom('div', { className: 'options' },
+            "Show ",
+            showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
+            showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
+            )
+          ),
+
+      this.runnerDiv = this.createDom('div', { className: 'runner running' },
+          this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
+          this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
+          this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
+      );
+
+  this.document.body.appendChild(this.outerDiv);
+
+  var suites = runner.suites();
+  for (var i = 0; i < suites.length; i++) {
+    var suite = suites[i];
+    var suiteDiv = this.createDom('div', { className: 'suite' },
+        this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
+        this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
+    this.suiteDivs[suite.id] = suiteDiv;
+    var parentDiv = this.outerDiv;
+    if (suite.parentSuite) {
+      parentDiv = this.suiteDivs[suite.parentSuite.id];
+    }
+    parentDiv.appendChild(suiteDiv);
+  }
+
+  this.startedAt = new Date();
+
+  var self = this;
+  showPassed.onclick = function(evt) {
+    if (showPassed.checked) {
+      self.outerDiv.className += ' show-passed';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
+    }
+  };
+
+  showSkipped.onclick = function(evt) {
+    if (showSkipped.checked) {
+      self.outerDiv.className += ' show-skipped';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
+    }
+  };
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
+  var results = runner.results();
+  var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
+  this.runnerDiv.setAttribute("class", className);
+  //do it twice for IE
+  this.runnerDiv.setAttribute("className", className);
+  var specs = runner.specs();
+  var specCount = 0;
+  for (var i = 0; i < specs.length; i++) {
+    if (this.specFilter(specs[i])) {
+      specCount++;
+    }
+  }
+  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
+  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
+  this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
+
+  this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
+};
+
+jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
+  var results = suite.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.totalCount === 0) { // todo: change this to check results.skipped
+    status = 'skipped';
+  }
+  this.suiteDivs[suite.id].className += " " + status;
+};
+
+jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
+  if (this.logRunningSpecs) {
+    this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+  }
+};
+
+jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
+  var results = spec.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.skipped) {
+    status = 'skipped';
+  }
+  var specDiv = this.createDom('div', { className: 'spec '  + status },
+      this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
+      this.createDom('a', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(spec.getFullName()),
+        title: spec.getFullName()
+      }, spec.description));
+
+
+  var resultItems = results.getItems();
+  var messagesDiv = this.createDom('div', { className: 'messages' });
+  for (var i = 0; i < resultItems.length; i++) {
+    var result = resultItems[i];
+
+    if (result.type == 'log') {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+    } else if (result.type == 'expect' && result.passed && !result.passed()) {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+      if (result.trace.stack) {
+        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+      }
+    }
+  }
+
+  if (messagesDiv.childNodes.length > 0) {
+    specDiv.appendChild(messagesDiv);
+  }
+
+  this.suiteDivs[spec.suite.id].appendChild(specDiv);
+};
+
+jasmine.TrivialReporter.prototype.log = function() {
+  var console = jasmine.getGlobal().console;
+  if (console && console.log) {
+    if (console.log.apply) {
+      console.log.apply(console, arguments);
+    } else {
+      console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+    }
+  }
+};
+
+jasmine.TrivialReporter.prototype.getLocation = function() {
+  return this.document.location;
+};
+
+jasmine.TrivialReporter.prototype.specFilter = function(spec) {
+  var paramMap = {};
+  var params = this.getLocation().search.substring(1).split('&');
+  for (var i = 0; i < params.length; i++) {
+    var p = params[i].split('=');
+    paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
+  }
+
+  if (!paramMap.spec) {
+    return true;
+  }
+  return spec.getFullName().indexOf(paramMap.spec) === 0;
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/8dd2c2e3/templates/www/spec/lib/jasmine-1.2.0/jasmine.css
----------------------------------------------------------------------
diff --git a/templates/www/spec/lib/jasmine-1.2.0/jasmine.css b/templates/www/spec/lib/jasmine-1.2.0/jasmine.css
new file mode 100644
index 0000000..826e575
--- /dev/null
+++ b/templates/www/spec/lib/jasmine-1.2.0/jasmine.css
@@ -0,0 +1,81 @@
+body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
+
+#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
+#HTMLReporter a { text-decoration: none; }
+#HTMLReporter a:hover { text-decoration: underline; }
+#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
+#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
+#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
+#HTMLReporter .version { color: #aaaaaa; }
+#HTMLReporter .banner { margin-top: 14px; }
+#HTMLReporter .duration { color: #aaaaaa; float: right; }
+#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
+#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
+#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
+#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
+#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
+#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
+#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
+#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
+#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
+#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
+#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
+#HTMLReporter .runningAlert { background-color: #666666; }
+#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
+#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
+#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
+#HTMLReporter .passingAlert { background-color: #a6b779; }
+#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
+#HTMLReporter .failingAlert { background-color: #cf867e; }
+#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
+#HTMLReporter .results { margin-top: 14px; }
+#HTMLReporter #details { display: none; }
+#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
+#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
+#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
+#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
+#HTMLReporter.showDetails .summary { display: none; }
+#HTMLReporter.showDetails #details { display: block; }
+#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
+#HTMLReporter .summary { margin-top: 14px; }
+#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
+#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
+#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
+#HTMLReporter .description + .suite { margin-top: 0; }
+#HTMLReporter .suite { margin-top: 14px; }
+#HTMLReporter .suite a { color: #333333; }
+#HTMLReporter #details .specDetail { margin-bottom: 28px; }
+#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
+#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
+#HTMLReporter .resultMessage span.result { display: block; }
+#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
+
+#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
+#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
+#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
+#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
+#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
+#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
+#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
+#TrivialReporter .runner.running { background-color: yellow; }
+#TrivialReporter .options { text-align: right; font-size: .8em; }
+#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
+#TrivialReporter .suite .suite { margin: 5px; }
+#TrivialReporter .suite.passed { background-color: #dfd; }
+#TrivialReporter .suite.failed { background-color: #fdd; }
+#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
+#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
+#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
+#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
+#TrivialReporter .spec.skipped { background-color: #bbb; }
+#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
+#TrivialReporter .passed { background-color: #cfc; display: none; }
+#TrivialReporter .failed { background-color: #fbb; }
+#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
+#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
+#TrivialReporter .resultMessage .mismatch { color: black; }
+#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
+#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
+#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
+#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
+#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }


[17/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/templates/standalone/www/cordova-2.7.0.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/www/cordova-2.7.0.js b/lib/cordova-wp8/templates/standalone/www/cordova-2.7.0.js
new file mode 100644
index 0000000..199ba93
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/www/cordova-2.7.0.js
@@ -0,0 +1,6700 @@
+// Platform: windowsphone
+
+// commit cd29cf0f224ccf25e9d422a33fd02ef67d3a78f4
+
+// File generated at :: Fri Apr 26 2013 14:56:24 GMT-0700 (Pacific Daylight Time)
+
+/*
+ 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.
+*/
+
+;(function() {
+
+// file: lib\scripts\require.js
+
+var require,
+    define;
+
+(function () {
+    var modules = {};
+    // Stack of moduleIds currently being built.
+    var requireStack = [];
+    // Map of module ID -> index into requireStack of modules currently being built.
+    var inProgressModules = {};
+
+    function build(module) {
+        var factory = module.factory;
+        module.exports = {};
+        delete module.factory;
+        factory(require, module.exports, module);
+        return module.exports;
+    }
+
+    require = function (id) {
+        if (!modules[id]) {
+            throw "module " + id + " not found";
+        } else if (id in inProgressModules) {
+            var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;
+            throw "Cycle in require graph: " + cycle;
+        }
+        if (modules[id].factory) {
+            try {
+                inProgressModules[id] = requireStack.length;
+                requireStack.push(id);
+                return build(modules[id]);
+            } finally {
+                delete inProgressModules[id];
+                requireStack.pop();
+            }
+        }
+        return modules[id].exports;
+    };
+
+    define = function (id, factory) {
+        if (modules[id]) {
+            throw "module " + id + " already defined";
+        }
+
+        modules[id] = {
+            id: id,
+            factory: factory
+        };
+    };
+
+    define.remove = function (id) {
+        delete modules[id];
+    };
+
+    define.moduleMap = modules;
+})();
+
+//Export for use in node
+if (typeof module === "object" && typeof require === "function") {
+    module.exports.require = require;
+    module.exports.define = define;
+}
+
+// file: lib/cordova.js
+define("cordova", function(require, exports, module) {
+
+
+var channel = require('cordova/channel');
+
+/**
+ * Listen for DOMContentLoaded and notify our channel subscribers.
+ */
+document.addEventListener('DOMContentLoaded', function() {
+    channel.onDOMContentLoaded.fire();
+}, false);
+if (document.readyState == 'complete' || document.readyState == 'interactive') {
+    channel.onDOMContentLoaded.fire();
+}
+
+/**
+ * Intercept calls to addEventListener + removeEventListener and handle deviceready,
+ * resume, and pause events.
+ */
+var m_document_addEventListener = document.addEventListener;
+var m_document_removeEventListener = document.removeEventListener;
+var m_window_addEventListener = window.addEventListener;
+var m_window_removeEventListener = window.removeEventListener;
+
+/**
+ * Houses custom event handlers to intercept on document + window event listeners.
+ */
+var documentEventHandlers = {},
+    windowEventHandlers = {};
+
+document.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    if (typeof documentEventHandlers[e] != 'undefined') {
+        documentEventHandlers[e].subscribe(handler);
+    } else {
+        m_document_addEventListener.call(document, evt, handler, capture);
+    }
+};
+
+window.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    if (typeof windowEventHandlers[e] != 'undefined') {
+        windowEventHandlers[e].subscribe(handler);
+    } else {
+        m_window_addEventListener.call(window, evt, handler, capture);
+    }
+};
+
+document.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    // If unsubscribing from an event that is handled by a plugin
+    if (typeof documentEventHandlers[e] != "undefined") {
+        documentEventHandlers[e].unsubscribe(handler);
+    } else {
+        m_document_removeEventListener.call(document, evt, handler, capture);
+    }
+};
+
+window.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    // If unsubscribing from an event that is handled by a plugin
+    if (typeof windowEventHandlers[e] != "undefined") {
+        windowEventHandlers[e].unsubscribe(handler);
+    } else {
+        m_window_removeEventListener.call(window, evt, handler, capture);
+    }
+};
+
+function createEvent(type, data) {
+    var event = document.createEvent('Events');
+    event.initEvent(type, false, false);
+    if (data) {
+        for (var i in data) {
+            if (data.hasOwnProperty(i)) {
+                event[i] = data[i];
+            }
+        }
+    }
+    return event;
+}
+
+if(typeof window.console === "undefined") {
+    window.console = {
+        log:function(){}
+    };
+}
+
+var cordova = {
+    define:define,
+    require:require,
+    /**
+     * Methods to add/remove your own addEventListener hijacking on document + window.
+     */
+    addWindowEventHandler:function(event) {
+        return (windowEventHandlers[event] = channel.create(event));
+    },
+    addStickyDocumentEventHandler:function(event) {
+        return (documentEventHandlers[event] = channel.createSticky(event));
+    },
+    addDocumentEventHandler:function(event) {
+        return (documentEventHandlers[event] = channel.create(event));
+    },
+    removeWindowEventHandler:function(event) {
+        delete windowEventHandlers[event];
+    },
+    removeDocumentEventHandler:function(event) {
+        delete documentEventHandlers[event];
+    },
+    /**
+     * Retrieve original event handlers that were replaced by Cordova
+     *
+     * @return object
+     */
+    getOriginalHandlers: function() {
+        return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
+        'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
+    },
+    /**
+     * Method to fire event from native code
+     * bNoDetach is required for events which cause an exception which needs to be caught in native code
+     */
+    fireDocumentEvent: function(type, data, bNoDetach) {
+        var evt = createEvent(type, data);
+        if (typeof documentEventHandlers[type] != 'undefined') {
+            if( bNoDetach ) {
+              documentEventHandlers[type].fire(evt);
+            }
+            else {
+              setTimeout(function() {
+                  // Fire deviceready on listeners that were registered before cordova.js was loaded.
+                  if (type == 'deviceready') {
+                      document.dispatchEvent(evt);
+                  }
+                  documentEventHandlers[type].fire(evt);
+              }, 0);
+            }
+        } else {
+            document.dispatchEvent(evt);
+        }
+    },
+    fireWindowEvent: function(type, data) {
+        var evt = createEvent(type,data);
+        if (typeof windowEventHandlers[type] != 'undefined') {
+            setTimeout(function() {
+                windowEventHandlers[type].fire(evt);
+            }, 0);
+        } else {
+            window.dispatchEvent(evt);
+        }
+    },
+
+    /**
+     * Plugin callback mechanism.
+     */
+    // Randomize the starting callbackId to avoid collisions after refreshing or navigating.
+    // This way, it's very unlikely that any new callback would get the same callbackId as an old callback.
+    callbackId: Math.floor(Math.random() * 2000000000),
+    callbacks:  {},
+    callbackStatus: {
+        NO_RESULT: 0,
+        OK: 1,
+        CLASS_NOT_FOUND_EXCEPTION: 2,
+        ILLEGAL_ACCESS_EXCEPTION: 3,
+        INSTANTIATION_EXCEPTION: 4,
+        MALFORMED_URL_EXCEPTION: 5,
+        IO_EXCEPTION: 6,
+        INVALID_ACTION: 7,
+        JSON_EXCEPTION: 8,
+        ERROR: 9
+    },
+
+    /**
+     * Called by native code when returning successful result from an action.
+     */
+    callbackSuccess: function(callbackId, args) {
+        try {
+            cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
+        } catch (e) {
+            console.log("Error in error callback: " + callbackId + " = "+e);
+        }
+    },
+
+    /**
+     * Called by native code when returning error result from an action.
+     */
+    callbackError: function(callbackId, args) {
+        // TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative.
+        // Derive success from status.
+        try {
+            cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
+        } catch (e) {
+            console.log("Error in error callback: " + callbackId + " = "+e);
+        }
+    },
+
+    /**
+     * Called by native code when returning the result from an action.
+     */
+    callbackFromNative: function(callbackId, success, status, args, keepCallback) {
+        var callback = cordova.callbacks[callbackId];
+        if (callback) {
+            if (success && status == cordova.callbackStatus.OK) {
+                callback.success && callback.success.apply(null, args);
+            } else if (!success) {
+                callback.fail && callback.fail.apply(null, args);
+            }
+
+            // Clear callback if not expecting any more results
+            if (!keepCallback) {
+                delete cordova.callbacks[callbackId];
+            }
+        }
+    },
+    addConstructor: function(func) {
+        channel.onCordovaReady.subscribe(function() {
+            try {
+                func();
+            } catch(e) {
+                console.log("Failed to run constructor: " + e);
+            }
+        });
+    }
+};
+
+// Register pause, resume and deviceready channels as events on document.
+channel.onPause = cordova.addDocumentEventHandler('pause');
+channel.onResume = cordova.addDocumentEventHandler('resume');
+channel.onDeviceReady = cordova.addStickyDocumentEventHandler('deviceready');
+
+module.exports = cordova;
+
+});
+
+// file: lib\common\argscheck.js
+define("cordova/argscheck", function(require, exports, module) {
+
+var exec = require('cordova/exec');
+var utils = require('cordova/utils');
+
+var moduleExports = module.exports;
+
+var typeMap = {
+    'A': 'Array',
+    'D': 'Date',
+    'N': 'Number',
+    'S': 'String',
+    'F': 'Function',
+    'O': 'Object'
+};
+
+function extractParamName(callee, argIndex) {
+  return (/.*?\((.*?)\)/).exec(callee)[1].split(', ')[argIndex];
+}
+
+function checkArgs(spec, functionName, args, opt_callee) {
+    if (!moduleExports.enableChecks) {
+        return;
+    }
+    var errMsg = null;
+    var typeName;
+    for (var i = 0; i < spec.length; ++i) {
+        var c = spec.charAt(i),
+            cUpper = c.toUpperCase(),
+            arg = args[i];
+        // Asterix means allow anything.
+        if (c == '*') {
+            continue;
+        }
+        typeName = utils.typeName(arg);
+        if ((arg === null || arg === undefined) && c == cUpper) {
+            continue;
+        }
+        if (typeName != typeMap[cUpper]) {
+            errMsg = 'Expected ' + typeMap[cUpper];
+            break;
+        }
+    }
+    if (errMsg) {
+        errMsg += ', but got ' + typeName + '.';
+        errMsg = 'Wrong type for parameter "' + extractParamName(opt_callee || args.callee, i) + '" of ' + functionName + ': ' + errMsg;
+        // Don't log when running jake test.
+        if (typeof jasmine == 'undefined') {
+            console.error(errMsg);
+        }
+        throw TypeError(errMsg);
+    }
+}
+
+function getValue(value, defaultValue) {
+    return value === undefined ? defaultValue : value;
+}
+
+moduleExports.checkArgs = checkArgs;
+moduleExports.getValue = getValue;
+moduleExports.enableChecks = true;
+
+
+});
+
+// file: lib\common\builder.js
+define("cordova/builder", function(require, exports, module) {
+
+var utils = require('cordova/utils');
+
+function each(objects, func, context) {
+    for (var prop in objects) {
+        if (objects.hasOwnProperty(prop)) {
+            func.apply(context, [objects[prop], prop]);
+        }
+    }
+}
+
+function clobber(obj, key, value) {
+    exports.replaceHookForTesting(obj, key);
+    obj[key] = value;
+    // Getters can only be overridden by getters.
+    if (obj[key] !== value) {
+        utils.defineGetter(obj, key, function() {
+            return value;
+        });
+    }
+}
+
+function assignOrWrapInDeprecateGetter(obj, key, value, message) {
+    if (message) {
+        utils.defineGetter(obj, key, function() {
+            console.log(message);
+            delete obj[key];
+            clobber(obj, key, value);
+            return value;
+        });
+    } else {
+        clobber(obj, key, value);
+    }
+}
+
+function include(parent, objects, clobber, merge) {
+    each(objects, function (obj, key) {
+        try {
+          var result = obj.path ? require(obj.path) : {};
+
+          if (clobber) {
+              // Clobber if it doesn't exist.
+              if (typeof parent[key] === 'undefined') {
+                  assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+              } else if (typeof obj.path !== 'undefined') {
+                  // If merging, merge properties onto parent, otherwise, clobber.
+                  if (merge) {
+                      recursiveMerge(parent[key], result);
+                  } else {
+                      assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+                  }
+              }
+              result = parent[key];
+          } else {
+            // Overwrite if not currently defined.
+            if (typeof parent[key] == 'undefined') {
+              assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
+            } else {
+              // Set result to what already exists, so we can build children into it if they exist.
+              result = parent[key];
+            }
+          }
+
+          if (obj.children) {
+            include(result, obj.children, clobber, merge);
+          }
+        } catch(e) {
+          utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"');
+        }
+    });
+}
+
+/**
+ * Merge properties from one object onto another recursively.  Properties from
+ * the src object will overwrite existing target property.
+ *
+ * @param target Object to merge properties into.
+ * @param src Object to merge properties from.
+ */
+function recursiveMerge(target, src) {
+    for (var prop in src) {
+        if (src.hasOwnProperty(prop)) {
+            if (target.prototype && target.prototype.constructor === target) {
+                // If the target object is a constructor override off prototype.
+                clobber(target.prototype, prop, src[prop]);
+            } else {
+                if (typeof src[prop] === 'object' && typeof target[prop] === 'object') {
+                    recursiveMerge(target[prop], src[prop]);
+                } else {
+                    clobber(target, prop, src[prop]);
+                }
+            }
+        }
+    }
+}
+
+exports.buildIntoButDoNotClobber = function(objects, target) {
+    include(target, objects, false, false);
+};
+exports.buildIntoAndClobber = function(objects, target) {
+    include(target, objects, true, false);
+};
+exports.buildIntoAndMerge = function(objects, target) {
+    include(target, objects, true, true);
+};
+exports.recursiveMerge = recursiveMerge;
+exports.assignOrWrapInDeprecateGetter = assignOrWrapInDeprecateGetter;
+exports.replaceHookForTesting = function() {};
+
+});
+
+// file: lib\common\channel.js
+define("cordova/channel", function(require, exports, module) {
+
+var utils = require('cordova/utils'),
+    nextGuid = 1;
+
+/**
+ * Custom pub-sub "channel" that can have functions subscribed to it
+ * This object is used to define and control firing of events for
+ * cordova initialization, as well as for custom events thereafter.
+ *
+ * The order of events during page load and Cordova startup is as follows:
+ *
+ * onDOMContentLoaded*         Internal event that is received when the web page is loaded and parsed.
+ * onNativeReady*              Internal event that indicates the Cordova native side is ready.
+ * onCordovaReady*             Internal event fired when all Cordova JavaScript objects have been created.
+ * onCordovaInfoReady*         Internal event fired when device properties are available.
+ * onCordovaConnectionReady*   Internal event fired when the connection property has been set.
+ * onDeviceReady*              User event fired to indicate that Cordova is ready
+ * onResume                    User event fired to indicate a start/resume lifecycle event
+ * onPause                     User event fired to indicate a pause lifecycle event
+ * onDestroy*                  Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
+ *
+ * The events marked with an * are sticky. Once they have fired, they will stay in the fired state.
+ * All listeners that subscribe after the event is fired will be executed right away.
+ *
+ * The only Cordova events that user code should register for are:
+ *      deviceready           Cordova native code is initialized and Cordova APIs can be called from JavaScript
+ *      pause                 App has moved to background
+ *      resume                App has returned to foreground
+ *
+ * Listeners can be registered as:
+ *      document.addEventListener("deviceready", myDeviceReadyListener, false);
+ *      document.addEventListener("resume", myResumeListener, false);
+ *      document.addEventListener("pause", myPauseListener, false);
+ *
+ * The DOM lifecycle events should be used for saving and restoring state
+ *      window.onload
+ *      window.onunload
+ *
+ */
+
+/**
+ * Channel
+ * @constructor
+ * @param type  String the channel name
+ */
+var Channel = function(type, sticky) {
+    this.type = type;
+    // Map of guid -> function.
+    this.handlers = {};
+    // 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
+    this.state = sticky ? 1 : 0;
+    // Used in sticky mode to remember args passed to fire().
+    this.fireArgs = null;
+    // Used by onHasSubscribersChange to know if there are any listeners.
+    this.numHandlers = 0;
+    // Function that is called when the first listener is subscribed, or when
+    // the last listener is unsubscribed.
+    this.onHasSubscribersChange = null;
+},
+    channel = {
+        /**
+         * Calls the provided function only after all of the channels specified
+         * have been fired. All channels must be sticky channels.
+         */
+        join: function(h, c) {
+            var len = c.length,
+                i = len,
+                f = function() {
+                    if (!(--i)) h();
+                };
+            for (var j=0; j<len; j++) {
+                if (c[j].state === 0) {
+                    throw Error('Can only use join with sticky channels.');
+                }
+                c[j].subscribe(f);
+            }
+            if (!len) h();
+        },
+        create: function(type) {
+            return channel[type] = new Channel(type, false);
+        },
+        createSticky: function(type) {
+            return channel[type] = new Channel(type, true);
+        },
+
+        /**
+         * cordova Channels that must fire before "deviceready" is fired.
+         */
+        deviceReadyChannelsArray: [],
+        deviceReadyChannelsMap: {},
+
+        /**
+         * Indicate that a feature needs to be initialized before it is ready to be used.
+         * This holds up Cordova's "deviceready" event until the feature has been initialized
+         * and Cordova.initComplete(feature) is called.
+         *
+         * @param feature {String}     The unique feature name
+         */
+        waitForInitialization: function(feature) {
+            if (feature) {
+                var c = channel[feature] || this.createSticky(feature);
+                this.deviceReadyChannelsMap[feature] = c;
+                this.deviceReadyChannelsArray.push(c);
+            }
+        },
+
+        /**
+         * Indicate that initialization code has completed and the feature is ready to be used.
+         *
+         * @param feature {String}     The unique feature name
+         */
+        initializationComplete: function(feature) {
+            var c = this.deviceReadyChannelsMap[feature];
+            if (c) {
+                c.fire();
+            }
+        }
+    };
+
+function forceFunction(f) {
+    if (typeof f != 'function') throw "Function required as first argument!";
+}
+
+/**
+ * Subscribes the given function to the channel. Any time that
+ * Channel.fire is called so too will the function.
+ * Optionally specify an execution context for the function
+ * and a guid that can be used to stop subscribing to the channel.
+ * Returns the guid.
+ */
+Channel.prototype.subscribe = function(f, c) {
+    // need a function to call
+    forceFunction(f);
+    if (this.state == 2) {
+        f.apply(c || this, this.fireArgs);
+        return;
+    }
+
+    var func = f,
+        guid = f.observer_guid;
+    if (typeof c == "object") { func = utils.close(c, f); }
+
+    if (!guid) {
+        // first time any channel has seen this subscriber
+        guid = '' + nextGuid++;
+    }
+    func.observer_guid = guid;
+    f.observer_guid = guid;
+
+    // Don't add the same handler more than once.
+    if (!this.handlers[guid]) {
+        this.handlers[guid] = func;
+        this.numHandlers++;
+        if (this.numHandlers == 1) {
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+/**
+ * Unsubscribes the function with the given guid from the channel.
+ */
+Channel.prototype.unsubscribe = function(f) {
+    // need a function to unsubscribe
+    forceFunction(f);
+
+    var guid = f.observer_guid,
+        handler = this.handlers[guid];
+    if (handler) {
+        delete this.handlers[guid];
+        this.numHandlers--;
+        if (this.numHandlers === 0) {
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+/**
+ * Calls all functions subscribed to this channel.
+ */
+Channel.prototype.fire = function(e) {
+    var fail = false,
+        fireArgs = Array.prototype.slice.call(arguments);
+    // Apply stickiness.
+    if (this.state == 1) {
+        this.state = 2;
+        this.fireArgs = fireArgs;
+    }
+    if (this.numHandlers) {
+        // Copy the values first so that it is safe to modify it from within
+        // callbacks.
+        var toCall = [];
+        for (var item in this.handlers) {
+            toCall.push(this.handlers[item]);
+        }
+        for (var i = 0; i < toCall.length; ++i) {
+            toCall[i].apply(this, fireArgs);
+        }
+        if (this.state == 2 && this.numHandlers) {
+            this.numHandlers = 0;
+            this.handlers = {};
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+
+// defining them here so they are ready super fast!
+// DOM event that is received when the web page is loaded and parsed.
+channel.createSticky('onDOMContentLoaded');
+
+// Event to indicate the Cordova native side is ready.
+channel.createSticky('onNativeReady');
+
+// Event to indicate that all Cordova JavaScript objects have been created
+// and it's time to run plugin constructors.
+channel.createSticky('onCordovaReady');
+
+// Event to indicate that device properties are available
+channel.createSticky('onCordovaInfoReady');
+
+// Event to indicate that the connection property has been set.
+channel.createSticky('onCordovaConnectionReady');
+
+// Event to indicate that all automatically loaded JS plugins are loaded and ready.
+channel.createSticky('onPluginsReady');
+
+// Event to indicate that Cordova is ready
+channel.createSticky('onDeviceReady');
+
+// Event to indicate a resume lifecycle event
+channel.create('onResume');
+
+// Event to indicate a pause lifecycle event
+channel.create('onPause');
+
+// Event to indicate a destroy lifecycle event
+channel.createSticky('onDestroy');
+
+// Channels that must fire before "deviceready" is fired.
+channel.waitForInitialization('onCordovaReady');
+channel.waitForInitialization('onCordovaConnectionReady');
+channel.waitForInitialization('onDOMContentLoaded');
+
+module.exports = channel;
+
+});
+
+// file: lib\common\commandProxy.js
+define("cordova/commandProxy", function(require, exports, module) {
+
+
+// internal map of proxy function
+var CommandProxyMap = {};
+
+module.exports = {
+
+    // example: cordova.commandProxy.add("Accelerometer",{getCurrentAcceleration: function(successCallback, errorCallback, options) {...},...);
+    add:function(id,proxyObj) {
+        console.log("adding proxy for " + id);
+        CommandProxyMap[id] = proxyObj;
+        return proxyObj;
+    },
+
+    // cordova.commandProxy.remove("Accelerometer");
+    remove:function(id) {
+        var proxy = CommandProxyMap[id];
+        delete CommandProxyMap[id];
+        CommandProxyMap[id] = null;
+        return proxy;
+    },
+
+    get:function(service,action) {
+        return ( CommandProxyMap[service] ? CommandProxyMap[service][action] : null );
+    }
+};
+});
+
+// file: lib\windowsphone\exec.js
+define("cordova/exec", function(require, exports, module) {
+
+var cordova = require('cordova');
+
+/**
+ * Execute a cordova command.  It is up to the native side whether this action
+ * is synchronous or asynchronous.  The native side can return:
+ *      Synchronous: PluginResult object as a JSON string
+ *      Asynchronous: Empty string ""
+ * If async, the native side will cordova.callbackSuccess or cordova.callbackError,
+ * depending upon the result of the action.
+ *
+ * @param {Function} success    The success callback
+ * @param {Function} fail       The fail callback
+ * @param {String} service      The name of the service to use
+ * @param {String} action       Action to be run in cordova
+ * @param {String[]} [args]     Zero or more arguments to pass to the method
+
+ */
+
+module.exports = function(success, fail, service, action, args) {
+
+    var callbackId = service + cordova.callbackId++;
+    if (typeof success == "function" || typeof fail == "function") {
+        cordova.callbacks[callbackId] = {success:success, fail:fail};
+    }
+    // generate a new command string, ex. DebugConsole/log/DebugConsole23/["wtf dude?"]
+    for(var n = 0; n < args.length; n++)
+    {
+        if(typeof args[n] !== "string")
+        {
+            args[n] = JSON.stringify(args[n]);
+        }
+    }
+    var command = service + "/" + action + "/" + callbackId + "/" + JSON.stringify(args);
+    // pass it on to Notify
+    try {
+        if(window.external) {
+            window.external.Notify(command);
+        }
+        else {
+            console.log("window.external not available :: command=" + command);
+        }
+    }
+    catch(e) {
+        console.log("Exception calling native with command :: " + command + " :: exception=" + e);
+    }
+};
+
+
+});
+
+// file: lib\common\modulemapper.js
+define("cordova/modulemapper", function(require, exports, module) {
+
+var builder = require('cordova/builder'),
+    moduleMap = define.moduleMap,
+    symbolList,
+    deprecationMap;
+
+exports.reset = function() {
+    symbolList = [];
+    deprecationMap = {};
+};
+
+function addEntry(strategy, moduleName, symbolPath, opt_deprecationMessage) {
+    if (!(moduleName in moduleMap)) {
+        throw new Error('Module ' + moduleName + ' does not exist.');
+    }
+    symbolList.push(strategy, moduleName, symbolPath);
+    if (opt_deprecationMessage) {
+        deprecationMap[symbolPath] = opt_deprecationMessage;
+    }
+}
+
+// Note: Android 2.3 does have Function.bind().
+exports.clobbers = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('c', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+exports.merges = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('m', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+exports.defaults = function(moduleName, symbolPath, opt_deprecationMessage) {
+    addEntry('d', moduleName, symbolPath, opt_deprecationMessage);
+};
+
+function prepareNamespace(symbolPath, context) {
+    if (!symbolPath) {
+        return context;
+    }
+    var parts = symbolPath.split('.');
+    var cur = context;
+    for (var i = 0, part; part = parts[i]; ++i) {
+        cur = cur[part] = cur[part] || {};
+    }
+    return cur;
+}
+
+exports.mapModules = function(context) {
+    var origSymbols = {};
+    context.CDV_origSymbols = origSymbols;
+    for (var i = 0, len = symbolList.length; i < len; i += 3) {
+        var strategy = symbolList[i];
+        var moduleName = symbolList[i + 1];
+        var symbolPath = symbolList[i + 2];
+        var lastDot = symbolPath.lastIndexOf('.');
+        var namespace = symbolPath.substr(0, lastDot);
+        var lastName = symbolPath.substr(lastDot + 1);
+
+        var module = require(moduleName);
+        var deprecationMsg = symbolPath in deprecationMap ? 'Access made to deprecated symbol: ' + symbolPath + '. ' + deprecationMsg : null;
+        var parentObj = prepareNamespace(namespace, context);
+        var target = parentObj[lastName];
+
+        if (strategy == 'm' && target) {
+            builder.recursiveMerge(target, module);
+        } else if ((strategy == 'd' && !target) || (strategy != 'd')) {
+            if (!(symbolPath in origSymbols)) {
+                origSymbols[symbolPath] = target;
+            }
+            builder.assignOrWrapInDeprecateGetter(parentObj, lastName, module, deprecationMsg);
+        }
+    }
+};
+
+exports.getOriginalSymbol = function(context, symbolPath) {
+    var origSymbols = context.CDV_origSymbols;
+    if (origSymbols && (symbolPath in origSymbols)) {
+        return origSymbols[symbolPath];
+    }
+    var parts = symbolPath.split('.');
+    var obj = context;
+    for (var i = 0; i < parts.length; ++i) {
+        obj = obj && obj[parts[i]];
+    }
+    return obj;
+};
+
+exports.loadMatchingModules = function(matchingRegExp) {
+    for (var k in moduleMap) {
+        if (matchingRegExp.exec(k)) {
+            require(k);
+        }
+    }
+};
+
+exports.reset();
+
+
+});
+
+// file: lib\windowsphone\platform.js
+define("cordova/platform", function(require, exports, module) {
+
+var cordova = require('cordova'),
+      exec = require('cordova/exec');
+
+module.exports = {
+    id: "windowsphone",
+    initialize:function() {
+        var modulemapper = require('cordova/modulemapper');
+
+        modulemapper.loadMatchingModules(/cordova.*\/plugininit$/);
+
+        modulemapper.loadMatchingModules(/cordova.*\/symbols$/);
+
+        modulemapper.mapModules(window);
+
+        // Inject a listener for the backbutton, and tell native to override the flag (true/false) when we have 1 or more, or 0, listeners
+        var backButtonChannel = cordova.addDocumentEventHandler('backbutton');
+        backButtonChannel.onHasSubscribersChange = function() {
+            exec(null, null, "CoreEvents", "overridebackbutton", [this.numHandlers == 1]);
+        };
+    }
+};
+
+});
+
+// file: lib\common\plugin\Acceleration.js
+define("cordova/plugin/Acceleration", function(require, exports, module) {
+
+var Acceleration = function(x, y, z, timestamp) {
+    this.x = x;
+    this.y = y;
+    this.z = z;
+    this.timestamp = timestamp || (new Date()).getTime();
+};
+
+module.exports = Acceleration;
+
+});
+
+// file: lib\common\plugin\Camera.js
+define("cordova/plugin/Camera", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    Camera = require('cordova/plugin/CameraConstants'),
+    CameraPopoverHandle = require('cordova/plugin/CameraPopoverHandle');
+
+var cameraExport = {};
+
+// Tack on the Camera Constants to the base camera plugin.
+for (var key in Camera) {
+    cameraExport[key] = Camera[key];
+}
+
+/**
+ * Gets a picture from source defined by "options.sourceType", and returns the
+ * image as defined by the "options.destinationType" option.
+
+ * The defaults are sourceType=CAMERA and destinationType=FILE_URI.
+ *
+ * @param {Function} successCallback
+ * @param {Function} errorCallback
+ * @param {Object} options
+ */
+cameraExport.getPicture = function(successCallback, errorCallback, options) {
+    argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
+    options = options || {};
+    var getValue = argscheck.getValue;
+
+    var quality = getValue(options.quality, 50);
+    var destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
+    var sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
+    var targetWidth = getValue(options.targetWidth, -1);
+    var targetHeight = getValue(options.targetHeight, -1);
+    var encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG);
+    var mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE);
+    var allowEdit = !!options.allowEdit;
+    var correctOrientation = !!options.correctOrientation;
+    var saveToPhotoAlbum = !!options.saveToPhotoAlbum;
+    var popoverOptions = getValue(options.popoverOptions, null);
+    var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
+
+    var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
+                mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
+
+    exec(successCallback, errorCallback, "Camera", "takePicture", args);
+    return new CameraPopoverHandle();
+};
+
+cameraExport.cleanup = function(successCallback, errorCallback) {
+    exec(successCallback, errorCallback, "Camera", "cleanup", []);
+};
+
+module.exports = cameraExport;
+
+});
+
+// file: lib\common\plugin\CameraConstants.js
+define("cordova/plugin/CameraConstants", function(require, exports, module) {
+
+module.exports = {
+  DestinationType:{
+    DATA_URL: 0,         // Return base64 encoded string
+    FILE_URI: 1,         // Return file uri (content://media/external/images/media/2 for Android)
+    NATIVE_URI: 2        // Return native uri (eg. asset-library://... for iOS)
+  },
+  EncodingType:{
+    JPEG: 0,             // Return JPEG encoded image
+    PNG: 1               // Return PNG encoded image
+  },
+  MediaType:{
+    PICTURE: 0,          // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
+    VIDEO: 1,            // allow selection of video only, ONLY RETURNS URL
+    ALLMEDIA : 2         // allow selection from all media types
+  },
+  PictureSourceType:{
+    PHOTOLIBRARY : 0,    // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
+    CAMERA : 1,          // Take picture from camera
+    SAVEDPHOTOALBUM : 2  // Choose image from picture library (same as PHOTOLIBRARY for Android)
+  },
+  PopoverArrowDirection:{
+      ARROW_UP : 1,        // matches iOS UIPopoverArrowDirection constants to specify arrow location on popover
+      ARROW_DOWN : 2,
+      ARROW_LEFT : 4,
+      ARROW_RIGHT : 8,
+      ARROW_ANY : 15
+  },
+  Direction:{
+      BACK: 0,
+      FRONT: 1
+  }
+};
+
+});
+
+// file: lib\common\plugin\CameraPopoverHandle.js
+define("cordova/plugin/CameraPopoverHandle", function(require, exports, module) {
+
+var exec = require('cordova/exec');
+
+/**
+ * A handle to an image picker popover.
+ */
+var CameraPopoverHandle = function() {
+    this.setPosition = function(popoverOptions) {
+        console.log('CameraPopoverHandle.setPosition is only supported on iOS.');
+    };
+};
+
+module.exports = CameraPopoverHandle;
+
+});
+
+// file: lib\common\plugin\CameraPopoverOptions.js
+define("cordova/plugin/CameraPopoverOptions", function(require, exports, module) {
+
+var Camera = require('cordova/plugin/CameraConstants');
+
+/**
+ * Encapsulates options for iOS Popover image picker
+ */
+var CameraPopoverOptions = function(x,y,width,height,arrowDir){
+    // information of rectangle that popover should be anchored to
+    this.x = x || 0;
+    this.y = y || 32;
+    this.width = width || 320;
+    this.height = height || 480;
+    // The direction of the popover arrow
+    this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY;
+};
+
+module.exports = CameraPopoverOptions;
+
+});
+
+// file: lib\common\plugin\CaptureAudioOptions.js
+define("cordova/plugin/CaptureAudioOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all audio capture operation configuration options.
+ */
+var CaptureAudioOptions = function(){
+    // Upper limit of sound clips user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single sound clip in seconds.
+    this.duration = 0;
+    // The selected audio mode. Must match with one of the elements in supportedAudioModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureAudioOptions;
+
+});
+
+// file: lib\common\plugin\CaptureError.js
+define("cordova/plugin/CaptureError", function(require, exports, module) {
+
+/**
+ * The CaptureError interface encapsulates all errors in the Capture API.
+ */
+var CaptureError = function(c) {
+   this.code = c || null;
+};
+
+// Camera or microphone failed to capture image or sound.
+CaptureError.CAPTURE_INTERNAL_ERR = 0;
+// Camera application or audio capture application is currently serving other capture request.
+CaptureError.CAPTURE_APPLICATION_BUSY = 1;
+// Invalid use of the API (e.g. limit parameter has value less than one).
+CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
+// User exited camera application or audio capture application before capturing anything.
+CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
+// The requested capture operation is not supported.
+CaptureError.CAPTURE_NOT_SUPPORTED = 20;
+
+module.exports = CaptureError;
+
+});
+
+// file: lib\common\plugin\CaptureImageOptions.js
+define("cordova/plugin/CaptureImageOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all image capture operation configuration options.
+ */
+var CaptureImageOptions = function(){
+    // Upper limit of images user can take. Value must be equal or greater than 1.
+    this.limit = 1;
+    // The selected image mode. Must match with one of the elements in supportedImageModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureImageOptions;
+
+});
+
+// file: lib\common\plugin\CaptureVideoOptions.js
+define("cordova/plugin/CaptureVideoOptions", function(require, exports, module) {
+
+/**
+ * Encapsulates all video capture operation configuration options.
+ */
+var CaptureVideoOptions = function(){
+    // Upper limit of videos user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single video clip in seconds.
+    this.duration = 0;
+    // The selected video mode. Must match with one of the elements in supportedVideoModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureVideoOptions;
+
+});
+
+// file: lib\common\plugin\CompassError.js
+define("cordova/plugin/CompassError", function(require, exports, module) {
+
+/**
+ *  CompassError.
+ *  An error code assigned by an implementation when an error has occurred
+ * @constructor
+ */
+var CompassError = function(err) {
+    this.code = (err !== undefined ? err : null);
+};
+
+CompassError.COMPASS_INTERNAL_ERR = 0;
+CompassError.COMPASS_NOT_SUPPORTED = 20;
+
+module.exports = CompassError;
+
+});
+
+// file: lib\common\plugin\CompassHeading.js
+define("cordova/plugin/CompassHeading", function(require, exports, module) {
+
+var CompassHeading = function(magneticHeading, trueHeading, headingAccuracy, timestamp) {
+  this.magneticHeading = magneticHeading;
+  this.trueHeading = trueHeading;
+  this.headingAccuracy = headingAccuracy;
+  this.timestamp = timestamp || new Date().getTime();
+};
+
+module.exports = CompassHeading;
+
+});
+
+// file: lib\common\plugin\ConfigurationData.js
+define("cordova/plugin/ConfigurationData", function(require, exports, module) {
+
+/**
+ * Encapsulates a set of parameters that the capture device supports.
+ */
+function ConfigurationData() {
+    // The ASCII-encoded string in lower case representing the media type.
+    this.type = null;
+    // The height attribute represents height of the image or video in pixels.
+    // In the case of a sound clip this attribute has value 0.
+    this.height = 0;
+    // The width attribute represents width of the image or video in pixels.
+    // In the case of a sound clip this attribute has value 0
+    this.width = 0;
+}
+
+module.exports = ConfigurationData;
+
+});
+
+// file: lib\common\plugin\Connection.js
+define("cordova/plugin/Connection", function(require, exports, module) {
+
+/**
+ * Network status
+ */
+module.exports = {
+        UNKNOWN: "unknown",
+        ETHERNET: "ethernet",
+        WIFI: "wifi",
+        CELL_2G: "2g",
+        CELL_3G: "3g",
+        CELL_4G: "4g",
+        CELL:"cellular",
+        NONE: "none"
+};
+
+});
+
+// file: lib\common\plugin\Contact.js
+define("cordova/plugin/Contact", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    ContactError = require('cordova/plugin/ContactError'),
+    utils = require('cordova/utils');
+
+/**
+* Converts primitives into Complex Object
+* Currently only used for Date fields
+*/
+function convertIn(contact) {
+    var value = contact.birthday;
+    try {
+      contact.birthday = new Date(parseFloat(value));
+    } catch (exception){
+      console.log("Cordova Contact convertIn error: exception creating date.");
+    }
+    return contact;
+}
+
+/**
+* Converts Complex objects into primitives
+* Only conversion at present is for Dates.
+**/
+
+function convertOut(contact) {
+    var value = contact.birthday;
+    if (value !== null) {
+        // try to make it a Date object if it is not already
+        if (!utils.isDate(value)){
+            try {
+                value = new Date(value);
+            } catch(exception){
+                value = null;
+            }
+        }
+        if (utils.isDate(value)){
+            value = value.valueOf(); // convert to milliseconds
+        }
+        contact.birthday = value;
+    }
+    return contact;
+}
+
+/**
+* Contains information about a single contact.
+* @constructor
+* @param {DOMString} id unique identifier
+* @param {DOMString} displayName
+* @param {ContactName} name
+* @param {DOMString} nickname
+* @param {Array.<ContactField>} phoneNumbers array of phone numbers
+* @param {Array.<ContactField>} emails array of email addresses
+* @param {Array.<ContactAddress>} addresses array of addresses
+* @param {Array.<ContactField>} ims instant messaging user ids
+* @param {Array.<ContactOrganization>} organizations
+* @param {DOMString} birthday contact's birthday
+* @param {DOMString} note user notes about contact
+* @param {Array.<ContactField>} photos
+* @param {Array.<ContactField>} categories
+* @param {Array.<ContactField>} urls contact's web sites
+*/
+var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
+    ims, organizations, birthday, note, photos, categories, urls) {
+    this.id = id || null;
+    this.rawId = null;
+    this.displayName = displayName || null;
+    this.name = name || null; // ContactName
+    this.nickname = nickname || null;
+    this.phoneNumbers = phoneNumbers || null; // ContactField[]
+    this.emails = emails || null; // ContactField[]
+    this.addresses = addresses || null; // ContactAddress[]
+    this.ims = ims || null; // ContactField[]
+    this.organizations = organizations || null; // ContactOrganization[]
+    this.birthday = birthday || null;
+    this.note = note || null;
+    this.photos = photos || null; // ContactField[]
+    this.categories = categories || null; // ContactField[]
+    this.urls = urls || null; // ContactField[]
+};
+
+/**
+* Removes contact from device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.remove = function(successCB, errorCB) {
+    argscheck.checkArgs('FF', 'Contact.remove', arguments);
+    var fail = errorCB && function(code) {
+        errorCB(new ContactError(code));
+    };
+    if (this.id === null) {
+        fail(ContactError.UNKNOWN_ERROR);
+    }
+    else {
+        exec(successCB, fail, "Contacts", "remove", [this.id]);
+    }
+};
+
+/**
+* Creates a deep copy of this Contact.
+* With the contact ID set to null.
+* @return copy of this Contact
+*/
+Contact.prototype.clone = function() {
+    var clonedContact = utils.clone(this);
+    clonedContact.id = null;
+    clonedContact.rawId = null;
+
+    function nullIds(arr) {
+        if (arr) {
+            for (var i = 0; i < arr.length; ++i) {
+                arr[i].id = null;
+            }
+        }
+    }
+
+    // Loop through and clear out any id's in phones, emails, etc.
+    nullIds(clonedContact.phoneNumbers);
+    nullIds(clonedContact.emails);
+    nullIds(clonedContact.addresses);
+    nullIds(clonedContact.ims);
+    nullIds(clonedContact.organizations);
+    nullIds(clonedContact.categories);
+    nullIds(clonedContact.photos);
+    nullIds(clonedContact.urls);
+    return clonedContact;
+};
+
+/**
+* Persists contact to device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.save = function(successCB, errorCB) {
+    argscheck.checkArgs('FFO', 'Contact.save', arguments);
+    var fail = errorCB && function(code) {
+        errorCB(new ContactError(code));
+    };
+    var success = function(result) {
+        if (result) {
+            if (successCB) {
+                var fullContact = require('cordova/plugin/contacts').create(result);
+                successCB(convertIn(fullContact));
+            }
+        }
+        else {
+            // no Entry object returned
+            fail(ContactError.UNKNOWN_ERROR);
+        }
+    };
+    var dupContact = convertOut(utils.clone(this));
+    exec(success, fail, "Contacts", "save", [dupContact]);
+};
+
+
+module.exports = Contact;
+
+});
+
+// file: lib\common\plugin\ContactAddress.js
+define("cordova/plugin/ContactAddress", function(require, exports, module) {
+
+/**
+* Contact address.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code
+* @param formatted // NOTE: not a W3C standard
+* @param streetAddress
+* @param locality
+* @param region
+* @param postalCode
+* @param country
+*/
+
+var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
+    this.id = null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+    this.type = type || null;
+    this.formatted = formatted || null;
+    this.streetAddress = streetAddress || null;
+    this.locality = locality || null;
+    this.region = region || null;
+    this.postalCode = postalCode || null;
+    this.country = country || null;
+};
+
+module.exports = ContactAddress;
+
+});
+
+// file: lib\common\plugin\ContactError.js
+define("cordova/plugin/ContactError", function(require, exports, module) {
+
+/**
+ *  ContactError.
+ *  An error code assigned by an implementation when an error has occurred
+ * @constructor
+ */
+var ContactError = function(err) {
+    this.code = (typeof err != 'undefined' ? err : null);
+};
+
+/**
+ * Error codes
+ */
+ContactError.UNKNOWN_ERROR = 0;
+ContactError.INVALID_ARGUMENT_ERROR = 1;
+ContactError.TIMEOUT_ERROR = 2;
+ContactError.PENDING_OPERATION_ERROR = 3;
+ContactError.IO_ERROR = 4;
+ContactError.NOT_SUPPORTED_ERROR = 5;
+ContactError.PERMISSION_DENIED_ERROR = 20;
+
+module.exports = ContactError;
+
+});
+
+// file: lib\common\plugin\ContactField.js
+define("cordova/plugin/ContactField", function(require, exports, module) {
+
+/**
+* Generic contact field.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param type
+* @param value
+* @param pref
+*/
+var ContactField = function(type, value, pref) {
+    this.id = null;
+    this.type = (type && type.toString()) || null;
+    this.value = (value && value.toString()) || null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+};
+
+module.exports = ContactField;
+
+});
+
+// file: lib\common\plugin\ContactFindOptions.js
+define("cordova/plugin/ContactFindOptions", function(require, exports, module) {
+
+/**
+ * ContactFindOptions.
+ * @constructor
+ * @param filter used to match contacts against
+ * @param multiple boolean used to determine if more than one contact should be returned
+ */
+
+var ContactFindOptions = function(filter, multiple) {
+    this.filter = filter || '';
+    this.multiple = (typeof multiple != 'undefined' ? multiple : false);
+};
+
+module.exports = ContactFindOptions;
+
+});
+
+// file: lib\common\plugin\ContactName.js
+define("cordova/plugin/ContactName", function(require, exports, module) {
+
+/**
+* Contact name.
+* @constructor
+* @param formatted // NOTE: not part of W3C standard
+* @param familyName
+* @param givenName
+* @param middle
+* @param prefix
+* @param suffix
+*/
+var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
+    this.formatted = formatted || null;
+    this.familyName = familyName || null;
+    this.givenName = givenName || null;
+    this.middleName = middle || null;
+    this.honorificPrefix = prefix || null;
+    this.honorificSuffix = suffix || null;
+};
+
+module.exports = ContactName;
+
+});
+
+// file: lib\common\plugin\ContactOrganization.js
+define("cordova/plugin/ContactOrganization", function(require, exports, module) {
+
+/**
+* Contact organization.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param name
+* @param dept
+* @param title
+* @param startDate
+* @param endDate
+* @param location
+* @param desc
+*/
+
+var ContactOrganization = function(pref, type, name, dept, title) {
+    this.id = null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+    this.type = type || null;
+    this.name = name || null;
+    this.department = dept || null;
+    this.title = title || null;
+};
+
+module.exports = ContactOrganization;
+
+});
+
+// file: lib\common\plugin\Coordinates.js
+define("cordova/plugin/Coordinates", function(require, exports, module) {
+
+/**
+ * This class contains position information.
+ * @param {Object} lat
+ * @param {Object} lng
+ * @param {Object} alt
+ * @param {Object} acc
+ * @param {Object} head
+ * @param {Object} vel
+ * @param {Object} altacc
+ * @constructor
+ */
+var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
+    /**
+     * The latitude of the position.
+     */
+    this.latitude = lat;
+    /**
+     * The longitude of the position,
+     */
+    this.longitude = lng;
+    /**
+     * The accuracy of the position.
+     */
+    this.accuracy = acc;
+    /**
+     * The altitude of the position.
+     */
+    this.altitude = (alt !== undefined ? alt : null);
+    /**
+     * The direction the device is moving at the position.
+     */
+    this.heading = (head !== undefined ? head : null);
+    /**
+     * The velocity with which the device is moving at the position.
+     */
+    this.speed = (vel !== undefined ? vel : null);
+
+    if (this.speed === 0 || this.speed === null) {
+        this.heading = NaN;
+    }
+
+    /**
+     * The altitude accuracy of the position.
+     */
+    this.altitudeAccuracy = (altacc !== undefined) ? altacc : null;
+};
+
+module.exports = Coordinates;
+
+});
+
+// file: lib\common\plugin\DirectoryEntry.js
+define("cordova/plugin/DirectoryEntry", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    Entry = require('cordova/plugin/Entry'),
+    FileError = require('cordova/plugin/FileError'),
+    DirectoryReader = require('cordova/plugin/DirectoryReader');
+
+/**
+ * An interface representing a directory on the file system.
+ *
+ * {boolean} isFile always false (readonly)
+ * {boolean} isDirectory always true (readonly)
+ * {DOMString} name of the directory, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the directory (readonly)
+ * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly)
+ */
+var DirectoryEntry = function(name, fullPath) {
+     DirectoryEntry.__super__.constructor.call(this, false, true, name, fullPath);
+};
+
+utils.extend(DirectoryEntry, Entry);
+
+/**
+ * Creates a new DirectoryReader to read entries from this directory
+ */
+DirectoryEntry.prototype.createReader = function() {
+    return new DirectoryReader(this.fullPath);
+};
+
+/**
+ * Creates or looks up a directory
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
+ * @param {Flags} options to create or exclusively create the directory
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
+    argscheck.checkArgs('sOFF', 'DirectoryEntry.getDirectory', arguments);
+    var win = successCallback && function(result) {
+        var entry = new DirectoryEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]);
+};
+
+/**
+ * Deletes a directory and all of it's contents
+ *
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'DirectoryEntry.removeRecursively', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]);
+};
+
+/**
+ * Creates or looks up a file
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
+ * @param {Flags} options to create or exclusively create the file
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
+    argscheck.checkArgs('sOFF', 'DirectoryEntry.getFile', arguments);
+    var win = successCallback && function(result) {
+        var FileEntry = require('cordova/plugin/FileEntry');
+        var entry = new FileEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getFile", [this.fullPath, path, options]);
+};
+
+module.exports = DirectoryEntry;
+
+});
+
+// file: lib\common\plugin\DirectoryReader.js
+define("cordova/plugin/DirectoryReader", function(require, exports, module) {
+
+var exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError') ;
+
+/**
+ * An interface that lists the files and directories in a directory.
+ */
+function DirectoryReader(path) {
+    this.path = path || null;
+}
+
+/**
+ * Returns a list of entries from a directory.
+ *
+ * @param {Function} successCallback is called with a list of entries
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
+    var win = typeof successCallback !== 'function' ? null : function(result) {
+        var retVal = [];
+        for (var i=0; i<result.length; i++) {
+            var entry = null;
+            if (result[i].isDirectory) {
+                entry = new (require('cordova/plugin/DirectoryEntry'))();
+            }
+            else if (result[i].isFile) {
+                entry = new (require('cordova/plugin/FileEntry'))();
+            }
+            entry.isDirectory = result[i].isDirectory;
+            entry.isFile = result[i].isFile;
+            entry.name = result[i].name;
+            entry.fullPath = result[i].fullPath;
+            retVal.push(entry);
+        }
+        successCallback(retVal);
+    };
+    var fail = typeof errorCallback !== 'function' ? null : function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "readEntries", [this.path]);
+};
+
+module.exports = DirectoryReader;
+
+});
+
+// file: lib\common\plugin\Entry.js
+define("cordova/plugin/Entry", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError'),
+    Metadata = require('cordova/plugin/Metadata');
+
+/**
+ * Represents a file or directory on the local file system.
+ *
+ * @param isFile
+ *            {boolean} true if Entry is a file (readonly)
+ * @param isDirectory
+ *            {boolean} true if Entry is a directory (readonly)
+ * @param name
+ *            {DOMString} name of the file or directory, excluding the path
+ *            leading to it (readonly)
+ * @param fullPath
+ *            {DOMString} the absolute full path to the file or directory
+ *            (readonly)
+ */
+function Entry(isFile, isDirectory, name, fullPath, fileSystem) {
+    this.isFile = !!isFile;
+    this.isDirectory = !!isDirectory;
+    this.name = name || '';
+    this.fullPath = fullPath || '';
+    this.filesystem = fileSystem || null;
+}
+
+/**
+ * Look up the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ */
+Entry.prototype.getMetadata = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.getMetadata', arguments);
+    var success = successCallback && function(lastModified) {
+        var metadata = new Metadata(lastModified);
+        successCallback(metadata);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+
+    exec(success, fail, "File", "getMetadata", [this.fullPath]);
+};
+
+/**
+ * Set the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ * @param metadataObject
+ *            {Object} keys and values to set
+ */
+Entry.prototype.setMetadata = function(successCallback, errorCallback, metadataObject) {
+    argscheck.checkArgs('FFO', 'Entry.setMetadata', arguments);
+    exec(successCallback, errorCallback, "File", "setMetadata", [this.fullPath, metadataObject]);
+};
+
+/**
+ * Move a file or directory to a new location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to move this entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new DirectoryEntry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
+    argscheck.checkArgs('oSFF', 'Entry.moveTo', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    // source path
+    var srcPath = this.fullPath,
+        // entry name
+        name = newName || this.name,
+        success = function(entry) {
+            if (entry) {
+                if (successCallback) {
+                    // create appropriate Entry object
+                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
+                    successCallback(result);
+                }
+            }
+            else {
+                // no Entry object returned
+                fail && fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+
+    // copy
+    exec(success, fail, "File", "moveTo", [srcPath, parent.fullPath, name]);
+};
+
+/**
+ * Copy a directory to a different location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to copy the entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new Entry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
+    argscheck.checkArgs('oSFF', 'Entry.copyTo', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+
+        // source path
+    var srcPath = this.fullPath,
+        // entry name
+        name = newName || this.name,
+        // success callback
+        success = function(entry) {
+            if (entry) {
+                if (successCallback) {
+                    // create appropriate Entry object
+                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
+                    successCallback(result);
+                }
+            }
+            else {
+                // no Entry object returned
+                fail && fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+
+    // copy
+    exec(success, fail, "File", "copyTo", [srcPath, parent.fullPath, name]);
+};
+
+/**
+ * Return a URL that can be used to identify this entry.
+ */
+Entry.prototype.toURL = function() {
+    // fullPath attribute contains the full URL
+    return this.fullPath;
+};
+
+/**
+ * Returns a URI that can be used to identify this entry.
+ *
+ * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
+ * @return uri
+ */
+Entry.prototype.toURI = function(mimeType) {
+    console.log("DEPRECATED: Update your code to use 'toURL'");
+    // fullPath attribute contains the full URI
+    return this.toURL();
+};
+
+/**
+ * Remove a file or directory. It is an error to attempt to delete a
+ * directory that is not empty. It is an error to attempt to delete a
+ * root directory of a file system.
+ *
+ * @param successCallback {Function} called with no parameters
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.remove = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.remove', arguments);
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, "File", "remove", [this.fullPath]);
+};
+
+/**
+ * Look up the parent DirectoryEntry of this entry.
+ *
+ * @param successCallback {Function} called with the parent DirectoryEntry object
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.getParent = function(successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.getParent', arguments);
+    var win = successCallback && function(result) {
+        var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
+        var entry = new DirectoryEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getParent", [this.fullPath]);
+};
+
+module.exports = Entry;
+
+});
+
+// file: lib\common\plugin\File.js
+define("cordova/plugin/File", function(require, exports, module) {
+
+/**
+ * Constructor.
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+
+var File = function(name, fullPath, type, lastModifiedDate, size){
+    this.name = name || '';
+    this.fullPath = fullPath || null;
+    this.type = type || null;
+    this.lastModifiedDate = lastModifiedDate || null;
+    this.size = size || 0;
+
+    // These store the absolute start and end for slicing the file.
+    this.start = 0;
+    this.end = this.size;
+};
+
+/**
+ * Returns a "slice" of the file. Since Cordova Files don't contain the actual
+ * content, this really returns a File with adjusted start and end.
+ * Slices of slices are supported.
+ * start {Number} The index at which to start the slice (inclusive).
+ * end {Number} The index at which to end the slice (exclusive).
+ */
+File.prototype.slice = function(start, end) {
+    var size = this.end - this.start;
+    var newStart = 0;
+    var newEnd = size;
+    if (arguments.length) {
+        if (start < 0) {
+            newStart = Math.max(size + start, 0);
+        } else {
+            newStart = Math.min(size, start);
+        }
+    }
+
+    if (arguments.length >= 2) {
+        if (end < 0) {
+            newEnd = Math.max(size + end, 0);
+        } else {
+            newEnd = Math.min(end, size);
+        }
+    }
+
+    var newFile = new File(this.name, this.fullPath, this.type, this.lastModifiedData, this.size);
+    newFile.start = this.start + newStart;
+    newFile.end = this.start + newEnd;
+    return newFile;
+};
+
+
+module.exports = File;
+
+});
+
+// file: lib\common\plugin\FileEntry.js
+define("cordova/plugin/FileEntry", function(require, exports, module) {
+
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    Entry = require('cordova/plugin/Entry'),
+    FileWriter = require('cordova/plugin/FileWriter'),
+    File = require('cordova/plugin/File'),
+    FileError = require('cordova/plugin/FileError');
+
+/**
+ * An interface representing a file on the file system.
+ *
+ * {boolean} isFile always true (readonly)
+ * {boolean} isDirectory always false (readonly)
+ * {DOMString} name of the file, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the file (readonly)
+ * {FileSystem} filesystem on which the file resides (readonly)
+ */
+var FileEntry = function(name, fullPath) {
+     FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath]);
+};
+
+utils.extend(FileEntry, Entry);
+
+/**
+ * Creates a new FileWriter associated with the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new FileWriter
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.createWriter = function(successCallback, errorCallback) {
+    this.file(function(filePointer) {
+        var writer = new FileWriter(filePointer);
+
+        if (writer.fileName === null || writer.fileName === "") {
+            errorCallback && errorCallback(new FileError(FileError.INVALID_STATE_ERR));
+        } else {
+            successCallback && successCallback(writer);
+        }
+    }, errorCallback);
+};
+
+/**
+ * Returns a File that represents the current state of the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new File object
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.file = function(successCallback, errorCallback) {
+    var win = successCallback && function(f) {
+        var file = new File(f.name, f.fullPath, f.type, f.lastModifiedDate, f.size);
+        successCallback(file);
+    };
+    var fail = errorCallback && function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getFileMetadata", [this.fullPath]);
+};
+
+
+module.exports = FileEntry;
+
+});
+
+// file: lib\common\plugin\FileError.js
+define("cordova/plugin/FileError", function(require, exports, module) {
+
+/**
+ * FileError
+ */
+function FileError(error) {
+  this.code = error || null;
+}
+
+// File error codes
+// Found in DOMException
+FileError.NOT_FOUND_ERR = 1;
+FileError.SECURITY_ERR = 2;
+FileError.ABORT_ERR = 3;
+
+// Added by File API specification
+FileError.NOT_READABLE_ERR = 4;
+FileError.ENCODING_ERR = 5;
+FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
+FileError.INVALID_STATE_ERR = 7;
+FileError.SYNTAX_ERR = 8;
+FileError.INVALID_MODIFICATION_ERR = 9;
+FileError.QUOTA_EXCEEDED_ERR = 10;
+FileError.TYPE_MISMATCH_ERR = 11;
+FileError.PATH_EXISTS_ERR = 12;
+
+module.exports = FileError;
+
+});
+
+// file: lib\common\plugin\FileReader.js
+define("cordova/plugin/FileReader", function(require, exports, module) {
+
+var exec = require('cordova/exec'),
+    modulemapper = require('cordova/modulemapper'),
+    utils = require('cordova/utils'),
+    File = require('cordova/plugin/File'),
+    FileError = require('cordova/plugin/FileError'),
+    ProgressEvent = require('cordova/plugin/ProgressEvent'),
+    origFileReader = modulemapper.getOriginalSymbol(this, 'FileReader');
+
+/**
+ * This class reads the mobile device file system.
+ *
+ * For Android:
+ *      The root directory is the root of the file system.
+ *      To read from the SD card, the file name is "sdcard/my_file.txt"
+ * @constructor
+ */
+var FileReader = function() {
+    this._readyState = 0;
+    this._error = null;
+    this._result = null;
+    this._fileName = '';
+    this._realReader = origFileReader ? new origFileReader() : {};
+};
+
+// States
+FileReader.EMPTY = 0;
+FileReader.LOADING = 1;
+FileReader.DONE = 2;
+
+utils.defineGetter(FileReader.prototype, 'readyState', function() {
+    return this._fileName ? this._readyState : this._realReader.readyState;
+});
+
+utils.defineGetter(FileReader.prototype, 'error', function() {
+    return this._fileName ? this._error: this._realReader.error;
+});
+
+utils.defineGetter(FileReader.prototype, 'result', function() {
+    return this._fileName ? this._result: this._realReader.result;
+});
+
+function defineEvent(eventName) {
+    utils.defineGetterSetter(FileReader.prototype, eventName, function() {
+        return this._realReader[eventName] || null;
+    }, function(value) {
+        this._realReader[eventName] = value;
+    });
+}
+defineEvent('onloadstart');    // When the read starts.
+defineEvent('onprogress');     // While reading (and decoding) file or fileBlob data, and reporting partial file data (progress.loaded/progress.total)
+defineEvent('onload');         // When the read has successfully completed.
+defineEvent('onerror');        // When the read has failed (see errors).
+defineEvent('onloadend');      // When the request has completed (either in success or failure).
+defineEvent('onabort');        // When the read has been aborted. For instance, by invoking the abort() method.
+
+function initRead(reader, file) {
+    // Already loading something
+    if (reader.readyState == FileReader.LOADING) {
+      throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    reader._result = null;
+    reader._error = null;
+    reader._readyState = FileReader.LOADING;
+
+    if (typeof file == 'string') {
+        // Deprecated in Cordova 2.4.
+        console.warn('Using a string argument with FileReader.readAs functions is deprecated.');
+        reader._fileName = file;
+    } else if (typeof file.fullPath == 'string') {
+        reader._fileName = file.fullPath;
+    } else {
+        reader._fileName = '';
+        return true;
+    }
+
+    reader.onloadstart && reader.onloadstart(new ProgressEvent("loadstart", {target:reader}));
+}
+
+/**
+ * Abort reading file.
+ */
+FileReader.prototype.abort = function() {
+    if (origFileReader && !this._fileName) {
+        return this._realReader.abort();
+    }
+    this._result = null;
+
+    if (this._readyState == FileReader.DONE || this._readyState == FileReader.EMPTY) {
+      return;
+    }
+
+    this._readyState = FileReader.DONE;
+
+    // If abort callback
+    if (typeof this.onabort === 'function') {
+        this.onabort(new ProgressEvent('abort', {target:this}));
+    }
+    // If load end callback
+    if (typeof this.onloadend === 'function') {
+        this.onloadend(new ProgressEvent('loadend', {target:this}));
+    }
+};
+
+/**
+ * Read text file.
+ *
+ * @param file          {File} File object containing file properties
+ * @param encoding      [Optional] (see http://www.iana.org/assignments/character-sets)
+ */
+FileReader.prototype.readAsText = function(file, encoding) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsText(file, encoding);
+    }
+
+    // Default encoding is UTF-8
+    var enc = encoding ? encoding : "UTF-8";
+    var me = this;
+    var execArgs = [this._fileName, enc, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // Save result
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // null result
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsText", execArgs);
+};
+
+
+/**
+ * Read file and return data as a base64 encoded data url.
+ * A data url is of the form:
+ *      data:[<mediatype>][;base64],<data>
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsDataURL = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsDataURL(file);
+    }
+
+    var me = this;
+    var execArgs = [this._fileName, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            // Save result
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsDataURL", execArgs);
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsBinaryString = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsBinaryString(file);
+    }
+
+    var me = this;
+    var execArgs = [this._fileName, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsBinaryString", execArgs);
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsArrayBuffer = function(file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsArrayBuffer(file);
+    }
+
+    var me = this;
+    var execArgs = [this._fileName, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsArrayBuffer", execArgs);
+};
+
+module.exports = FileReader;
+
+});
+
+// file: lib\common\plugin\FileSystem.js
+define("cordova/plugin/FileSystem", function(require, exports, module) {
+
+var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
+
+/**
+ * An interface representing a file system
+ *
+ * @constructor
+ * {DOMString} name the unique name of the file system (readonly)
+ * {DirectoryEntry} root directory of the file system (readonly)
+ */
+var FileSystem = function(name, root) {
+    this.name = name || null;
+    if (root) {
+        this.root = new DirectoryEntry(root.name, root.fullPath);
+    }
+};
+
+module.exports = FileSystem;
+
+});
+
+// file: lib\common\plugin\FileTransfer.js
+define("cordova/plugin/FileTransfer", function(require, exports, module) {
+
+var argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec'),
+    FileTransferError = require('cordova/plugin/FileTransferError'),
+    ProgressEvent = require('cordova/plugin/ProgressEvent');
+
+function newProgressEvent(result) {
+    var pe = new ProgressEvent();
+    pe.lengthComputable = result.lengthComputable;
+    pe.loaded = result.loaded;
+    pe.total = result.total;
+    return pe;
+}
+
+function getBasicAuthHeader(urlString) {
+    var header =  null;
+
+    if (window.btoa) {
+        // parse the url using the Location object
+        var url = document.createElement('a');
+        url.href = urlString;
+
+        var credentials = null;
+        var protocol = url.protocol + "//";
+        var origin = protocol + url.host;
+
+        // check whether there are the username:password credentials in the url
+        if (url.href.indexOf(origin) !== 0) { // credentials found
+            var atIndex = url.href.indexOf("@");
+            credentials = url.href.substring(protocol.length, atIndex);
+        }
+
+        if (credentials) {
+            var authHeader = "Authorization";
+            var authHeaderValue = "Basic " + window.btoa(credentials);
+
+            header = {
+                name : authHeader,
+                value : authHeaderValue
+            };
+        }
+    }
+
+    return header;
+}
+
+var idCounter = 0;
+
+/**
+ * FileTransfer uploads a file to a remote server.
+ * @constructor
+ */
+var FileTransfer = function() {
+    this._id = ++idCounter;
+    this.onprogress = null; // optional callback
+};
+
+/**
+* Given an absolute file path, uploads a file on the device to a remote server
+* using a multipart HTTP request.
+* @param filePath {String}           Full path of the file on the device
+* @param server {String}             URL of the server to receive the file
+* @param successCallback (Function}  Callback to be invoked when upload has completed
+* @param errorCallback {Function}    Callback to be invoked upon error
+* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
+* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
+*/
+FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
+    argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments);
+    // check for options
+    var fileKey = null;
+    var fileName = null;
+    var mimeType = null;
+    var params = null;
+    var chunkedMode = true;
+    var headers = null;
+    var httpMethod = null;
+    var basicAuthHeader = getBasicAuthHeader(server);
+    if (basicAuthHeader) {
+        options = options || {};
+        options.headers = options.headers || {};
+        options.headers[basicAuthHeader.name] = basicAuthHeader.value;
+    }
+
+    if (options) {
+        fileKey = options.fileKey;
+        fileName = options.fileName;
+        mimeType = options.mimeType;
+        headers = options.headers;
+        httpMethod = options.httpMethod || "POST";
+        if (httpMethod.toUpperCase() == "PUT"){
+            httpMethod = "PUT";
+        } else {
+            httpMethod = "POST";
+        }
+        if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
+            chunkedMode = options.chunkedMode;
+        }
+        if (options.params) {
+            params = options.params;
+        }
+        else {
+            params = {};
+        }
+    }
+
+    var fail = errorCallback && function(e) {
+        var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
+        errorCallback(error);
+    };
+
+    var self = this;
+    var win = function(result) {
+        if (typeof result.lengthComputable != "undefined") {
+            if (self.onprogress) {
+                self.onprogress(newProgressEvent(result));
+            }
+        } else {
+            successCallback && successCallback(result);
+        }
+    };
+    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
+};
+
+/**
+ * Downloads a file form a given URL and saves it to the specified directory.
+ * @param source {String}          URL of the server to receive the file
+ * @param target {String}         Full path of the file on the device
+ * @param successCallback (Function}  Callback to be invoked when upload has completed
+ * @param errorCallback {Function}    Callback to be invoked upon error
+ * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
+ * @param options {FileDownloadOptions} Optional parameters such as headers
+ */
+FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) {
+    argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments);
+    var self = this;
+
+    var basicAuthHeader = getBasicAuthHeader(source);
+    if (basicAuthHeader) {
+        options = options || {};
+        options.headers = options.headers || {};
+        options.headers[basicAuthHeader.name] = basicAuthHeader.value;
+    }
+
+    var headers = null;
+    if (options) {
+        headers = options.headers || null;
+    }
+
+    var win = function(result) {
+        if (typeof result.lengthComputable != "undefined") {
+            if (self.onprogress) {
+                return self.onprogress(newProgressEvent(result));
+            }
+        } else if (successCallback) {
+            var entry = null;
+            if (result.isDirectory) {
+                entry = new (require('cordova/plugin/DirectoryEntry'))();
+            }
+            else if (result.isFile) {
+                entry = new (require('cordova/plugin/FileEntry'))();
+            }
+            entry.isDirectory = result.isDirectory;
+            entry.isFile = result.isFile;
+            entry.name = result.name;
+            entry.fullPath = result.fullPath;
+            successCallback(entry);
+        }
+    };
+
+    var fail = errorCallback && function(e) {
+        var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
+        errorCallback(error);
+    };
+
+    exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id, headers]);
+};
+
+/**
+ * Aborts the ongoing file transfer on this object. The original error
+ * callback for the file trans

<TRUNCATED>

[14/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/jasmine.js
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/jasmine.js b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/jasmine.js
new file mode 100644
index 0000000..bccb66c
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/jasmine.js
@@ -0,0 +1,2530 @@
+var isCommonJS = typeof window == "undefined";
+
+/**
+ * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
+ *
+ * @namespace
+ */
+var jasmine = {};
+if (isCommonJS) exports.jasmine = jasmine;
+/**
+ * @private
+ */
+jasmine.unimplementedMethod_ = function() {
+  throw new Error("unimplemented method");
+};
+
+/**
+ * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
+ * a plain old variable and may be redefined by somebody else.
+ *
+ * @private
+ */
+jasmine.undefined = jasmine.___undefined___;
+
+/**
+ * Show diagnostic messages in the console if set to true
+ *
+ */
+jasmine.VERBOSE = false;
+
+/**
+ * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
+ *
+ */
+jasmine.DEFAULT_UPDATE_INTERVAL = 250;
+
+/**
+ * Default timeout interval in milliseconds for waitsFor() blocks.
+ */
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
+
+jasmine.getGlobal = function() {
+  function getGlobal() {
+    return this;
+  }
+
+  return getGlobal();
+};
+
+/**
+ * Allows for bound functions to be compared.  Internal use only.
+ *
+ * @ignore
+ * @private
+ * @param base {Object} bound 'this' for the function
+ * @param name {Function} function to find
+ */
+jasmine.bindOriginal_ = function(base, name) {
+  var original = base[name];
+  if (original.apply) {
+    return function() {
+      return original.apply(base, arguments);
+    };
+  } else {
+    // IE support
+    return jasmine.getGlobal()[name];
+  }
+};
+
+jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
+jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
+jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
+jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
+
+jasmine.MessageResult = function(values) {
+  this.type = 'log';
+  this.values = values;
+  this.trace = new Error(); // todo: test better
+};
+
+jasmine.MessageResult.prototype.toString = function() {
+  var text = "";
+  for (var i = 0; i < this.values.length; i++) {
+    if (i > 0) text += " ";
+    if (jasmine.isString_(this.values[i])) {
+      text += this.values[i];
+    } else {
+      text += jasmine.pp(this.values[i]);
+    }
+  }
+  return text;
+};
+
+jasmine.ExpectationResult = function(params) {
+  this.type = 'expect';
+  this.matcherName = params.matcherName;
+  this.passed_ = params.passed;
+  this.expected = params.expected;
+  this.actual = params.actual;
+  this.message = this.passed_ ? 'Passed.' : params.message;
+
+  var trace = (params.trace || new Error(this.message));
+  this.trace = this.passed_ ? '' : trace;
+};
+
+jasmine.ExpectationResult.prototype.toString = function () {
+  return this.message;
+};
+
+jasmine.ExpectationResult.prototype.passed = function () {
+  return this.passed_;
+};
+
+/**
+ * Getter for the Jasmine environment. Ensures one gets created
+ */
+jasmine.getEnv = function() {
+  var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
+  return env;
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isArray_ = function(value) {
+  return jasmine.isA_("Array", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isString_ = function(value) {
+  return jasmine.isA_("String", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isNumber_ = function(value) {
+  return jasmine.isA_("Number", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param {String} typeName
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isA_ = function(typeName, value) {
+  return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
+};
+
+/**
+ * Pretty printer for expecations.  Takes any object and turns it into a human-readable string.
+ *
+ * @param value {Object} an object to be outputted
+ * @returns {String}
+ */
+jasmine.pp = function(value) {
+  var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
+  stringPrettyPrinter.format(value);
+  return stringPrettyPrinter.string;
+};
+
+/**
+ * Returns true if the object is a DOM Node.
+ *
+ * @param {Object} obj object to check
+ * @returns {Boolean}
+ */
+jasmine.isDomNode = function(obj) {
+  return obj.nodeType > 0;
+};
+
+/**
+ * Returns a matchable 'generic' object of the class type.  For use in expecations of type when values don't matter.
+ *
+ * @example
+ * // don't care about which function is passed in, as long as it's a function
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
+ *
+ * @param {Class} clazz
+ * @returns matchable object of the type clazz
+ */
+jasmine.any = function(clazz) {
+  return new jasmine.Matchers.Any(clazz);
+};
+
+/**
+ * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
+ * attributes on the object.
+ *
+ * @example
+ * // don't care about any other attributes than foo.
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
+ *
+ * @param sample {Object} sample
+ * @returns matchable object for the sample
+ */
+jasmine.objectContaining = function (sample) {
+    return new jasmine.Matchers.ObjectContaining(sample);
+};
+
+/**
+ * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
+ *
+ * Spies should be created in test setup, before expectations.  They can then be checked, using the standard Jasmine
+ * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
+ *
+ * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
+ *
+ * Spies are torn down at the end of every spec.
+ *
+ * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
+ *
+ * @example
+ * // a stub
+ * var myStub = jasmine.createSpy('myStub');  // can be used anywhere
+ *
+ * // spy example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ *
+ * // actual foo.not will not be called, execution stops
+ * spyOn(foo, 'not');
+
+ // foo.not spied upon, execution will continue to implementation
+ * spyOn(foo, 'not').andCallThrough();
+ *
+ * // fake example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ *
+ * // foo.not(val) will return val
+ * spyOn(foo, 'not').andCallFake(function(value) {return value;});
+ *
+ * // mock example
+ * foo.not(7 == 7);
+ * expect(foo.not).toHaveBeenCalled();
+ * expect(foo.not).toHaveBeenCalledWith(true);
+ *
+ * @constructor
+ * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
+ * @param {String} name
+ */
+jasmine.Spy = function(name) {
+  /**
+   * The name of the spy, if provided.
+   */
+  this.identity = name || 'unknown';
+  /**
+   *  Is this Object a spy?
+   */
+  this.isSpy = true;
+  /**
+   * The actual function this spy stubs.
+   */
+  this.plan = function() {
+  };
+  /**
+   * Tracking of the most recent call to the spy.
+   * @example
+   * var mySpy = jasmine.createSpy('foo');
+   * mySpy(1, 2);
+   * mySpy.mostRecentCall.args = [1, 2];
+   */
+  this.mostRecentCall = {};
+
+  /**
+   * Holds arguments for each call to the spy, indexed by call count
+   * @example
+   * var mySpy = jasmine.createSpy('foo');
+   * mySpy(1, 2);
+   * mySpy(7, 8);
+   * mySpy.mostRecentCall.args = [7, 8];
+   * mySpy.argsForCall[0] = [1, 2];
+   * mySpy.argsForCall[1] = [7, 8];
+   */
+  this.argsForCall = [];
+  this.calls = [];
+};
+
+/**
+ * Tells a spy to call through to the actual implemenatation.
+ *
+ * @example
+ * var foo = {
+ *   bar: function() { // do some stuff }
+ * }
+ *
+ * // defining a spy on an existing property: foo.bar
+ * spyOn(foo, 'bar').andCallThrough();
+ */
+jasmine.Spy.prototype.andCallThrough = function() {
+  this.plan = this.originalValue;
+  return this;
+};
+
+/**
+ * For setting the return value of a spy.
+ *
+ * @example
+ * // defining a spy from scratch: foo() returns 'baz'
+ * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() returns 'baz'
+ * spyOn(foo, 'bar').andReturn('baz');
+ *
+ * @param {Object} value
+ */
+jasmine.Spy.prototype.andReturn = function(value) {
+  this.plan = function() {
+    return value;
+  };
+  return this;
+};
+
+/**
+ * For throwing an exception when a spy is called.
+ *
+ * @example
+ * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
+ * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
+ * spyOn(foo, 'bar').andThrow('baz');
+ *
+ * @param {String} exceptionMsg
+ */
+jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
+  this.plan = function() {
+    throw exceptionMsg;
+  };
+  return this;
+};
+
+/**
+ * Calls an alternate implementation when a spy is called.
+ *
+ * @example
+ * var baz = function() {
+ *   // do some stuff, return something
+ * }
+ * // defining a spy from scratch: foo() calls the function baz
+ * var foo = jasmine.createSpy('spy on foo').andCall(baz);
+ *
+ * // defining a spy on an existing property: foo.bar() calls an anonymnous function
+ * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
+ *
+ * @param {Function} fakeFunc
+ */
+jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
+  this.plan = fakeFunc;
+  return this;
+};
+
+/**
+ * Resets all of a spy's the tracking variables so that it can be used again.
+ *
+ * @example
+ * spyOn(foo, 'bar');
+ *
+ * foo.bar();
+ *
+ * expect(foo.bar.callCount).toEqual(1);
+ *
+ * foo.bar.reset();
+ *
+ * expect(foo.bar.callCount).toEqual(0);
+ */
+jasmine.Spy.prototype.reset = function() {
+  this.wasCalled = false;
+  this.callCount = 0;
+  this.argsForCall = [];
+  this.calls = [];
+  this.mostRecentCall = {};
+};
+
+jasmine.createSpy = function(name) {
+
+  var spyObj = function() {
+    spyObj.wasCalled = true;
+    spyObj.callCount++;
+    var args = jasmine.util.argsToArray(arguments);
+    spyObj.mostRecentCall.object = this;
+    spyObj.mostRecentCall.args = args;
+    spyObj.argsForCall.push(args);
+    spyObj.calls.push({object: this, args: args});
+    return spyObj.plan.apply(this, arguments);
+  };
+
+  var spy = new jasmine.Spy(name);
+
+  for (var prop in spy) {
+    spyObj[prop] = spy[prop];
+  }
+
+  spyObj.reset();
+
+  return spyObj;
+};
+
+/**
+ * Determines whether an object is a spy.
+ *
+ * @param {jasmine.Spy|Object} putativeSpy
+ * @returns {Boolean}
+ */
+jasmine.isSpy = function(putativeSpy) {
+  return putativeSpy && putativeSpy.isSpy;
+};
+
+/**
+ * Creates a more complicated spy: an Object that has every property a function that is a spy.  Used for stubbing something
+ * large in one call.
+ *
+ * @param {String} baseName name of spy class
+ * @param {Array} methodNames array of names of methods to make spies
+ */
+jasmine.createSpyObj = function(baseName, methodNames) {
+  if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
+    throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
+  }
+  var obj = {};
+  for (var i = 0; i < methodNames.length; i++) {
+    obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
+  }
+  return obj;
+};
+
+/**
+ * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
+ *
+ * Be careful not to leave calls to <code>jasmine.log</code> in production code.
+ */
+jasmine.log = function() {
+  var spec = jasmine.getEnv().currentSpec;
+  spec.log.apply(spec, arguments);
+};
+
+/**
+ * Function that installs a spy on an existing object's method name.  Used within a Spec to create a spy.
+ *
+ * @example
+ * // spy example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
+ *
+ * @see jasmine.createSpy
+ * @param obj
+ * @param methodName
+ * @returns a Jasmine spy that can be chained with all spy methods
+ */
+var spyOn = function(obj, methodName) {
+  return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
+};
+if (isCommonJS) exports.spyOn = spyOn;
+
+/**
+ * Creates a Jasmine spec that will be added to the current suite.
+ *
+ * // TODO: pending tests
+ *
+ * @example
+ * it('should be true', function() {
+ *   expect(true).toEqual(true);
+ * });
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var it = function(desc, func) {
+  return jasmine.getEnv().it(desc, func);
+};
+if (isCommonJS) exports.it = it;
+
+/**
+ * Creates a <em>disabled</em> Jasmine spec.
+ *
+ * A convenience method that allows existing specs to be disabled temporarily during development.
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var xit = function(desc, func) {
+  return jasmine.getEnv().xit(desc, func);
+};
+if (isCommonJS) exports.xit = xit;
+
+/**
+ * Starts a chain for a Jasmine expectation.
+ *
+ * It is passed an Object that is the actual value and should chain to one of the many
+ * jasmine.Matchers functions.
+ *
+ * @param {Object} actual Actual value to test against and expected value
+ */
+var expect = function(actual) {
+  return jasmine.getEnv().currentSpec.expect(actual);
+};
+if (isCommonJS) exports.expect = expect;
+
+/**
+ * Defines part of a jasmine spec.  Used in cominbination with waits or waitsFor in asynchrnous specs.
+ *
+ * @param {Function} func Function that defines part of a jasmine spec.
+ */
+var runs = function(func) {
+  jasmine.getEnv().currentSpec.runs(func);
+};
+if (isCommonJS) exports.runs = runs;
+
+/**
+ * Waits a fixed time period before moving to the next block.
+ *
+ * @deprecated Use waitsFor() instead
+ * @param {Number} timeout milliseconds to wait
+ */
+var waits = function(timeout) {
+  jasmine.getEnv().currentSpec.waits(timeout);
+};
+if (isCommonJS) exports.waits = waits;
+
+/**
+ * Waits for the latchFunction to return true before proceeding to the next block.
+ *
+ * @param {Function} latchFunction
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
+  jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
+};
+if (isCommonJS) exports.waitsFor = waitsFor;
+
+/**
+ * A function that is called before each spec in a suite.
+ *
+ * Used for spec setup, including validating assumptions.
+ *
+ * @param {Function} beforeEachFunction
+ */
+var beforeEach = function(beforeEachFunction) {
+  jasmine.getEnv().beforeEach(beforeEachFunction);
+};
+if (isCommonJS) exports.beforeEach = beforeEach;
+
+/**
+ * A function that is called after each spec in a suite.
+ *
+ * Used for restoring any state that is hijacked during spec execution.
+ *
+ * @param {Function} afterEachFunction
+ */
+var afterEach = function(afterEachFunction) {
+  jasmine.getEnv().afterEach(afterEachFunction);
+};
+if (isCommonJS) exports.afterEach = afterEach;
+
+/**
+ * Defines a suite of specifications.
+ *
+ * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
+ * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
+ * of setup in some tests.
+ *
+ * @example
+ * // TODO: a simple suite
+ *
+ * // TODO: a simple suite with a nested describe block
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var describe = function(description, specDefinitions) {
+  return jasmine.getEnv().describe(description, specDefinitions);
+};
+if (isCommonJS) exports.describe = describe;
+
+/**
+ * Disables a suite of specifications.  Used to disable some suites in a file, or files, temporarily during development.
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var xdescribe = function(description, specDefinitions) {
+  return jasmine.getEnv().xdescribe(description, specDefinitions);
+};
+if (isCommonJS) exports.xdescribe = xdescribe;
+
+
+// Provide the XMLHttpRequest class for IE 5.x-6.x:
+jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
+  function tryIt(f) {
+    try {
+      return f();
+    } catch(e) {
+    }
+    return null;
+  }
+
+  var xhr = tryIt(function() {
+    return new ActiveXObject("Msxml2.XMLHTTP.6.0");
+  }) ||
+    tryIt(function() {
+      return new ActiveXObject("Msxml2.XMLHTTP.3.0");
+    }) ||
+    tryIt(function() {
+      return new ActiveXObject("Msxml2.XMLHTTP");
+    }) ||
+    tryIt(function() {
+      return new ActiveXObject("Microsoft.XMLHTTP");
+    });
+
+  if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
+
+  return xhr;
+} : XMLHttpRequest;
+/**
+ * @namespace
+ */
+jasmine.util = {};
+
+/**
+ * Declare that a child class inherit it's prototype from the parent class.
+ *
+ * @private
+ * @param {Function} childClass
+ * @param {Function} parentClass
+ */
+jasmine.util.inherit = function(childClass, parentClass) {
+  /**
+   * @private
+   */
+  var subclass = function() {
+  };
+  subclass.prototype = parentClass.prototype;
+  childClass.prototype = new subclass();
+};
+
+jasmine.util.formatException = function(e) {
+  var lineNumber;
+  if (e.line) {
+    lineNumber = e.line;
+  }
+  else if (e.lineNumber) {
+    lineNumber = e.lineNumber;
+  }
+
+  var file;
+
+  if (e.sourceURL) {
+    file = e.sourceURL;
+  }
+  else if (e.fileName) {
+    file = e.fileName;
+  }
+
+  var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
+
+  if (file && lineNumber) {
+    message += ' in ' + file + ' (line ' + lineNumber + ')';
+  }
+
+  return message;
+};
+
+jasmine.util.htmlEscape = function(str) {
+  if (!str) return str;
+  return str.replace(/&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;');
+};
+
+jasmine.util.argsToArray = function(args) {
+  var arrayOfArgs = [];
+  for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
+  return arrayOfArgs;
+};
+
+jasmine.util.extend = function(destination, source) {
+  for (var property in source) destination[property] = source[property];
+  return destination;
+};
+
+/**
+ * Environment for Jasmine
+ *
+ * @constructor
+ */
+jasmine.Env = function() {
+  this.currentSpec = null;
+  this.currentSuite = null;
+  this.currentRunner_ = new jasmine.Runner(this);
+
+  this.reporter = new jasmine.MultiReporter();
+
+  this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
+  this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
+  this.lastUpdate = 0;
+  this.specFilter = function() {
+    return true;
+  };
+
+  this.nextSpecId_ = 0;
+  this.nextSuiteId_ = 0;
+  this.equalityTesters_ = [];
+
+  // wrap matchers
+  this.matchersClass = function() {
+    jasmine.Matchers.apply(this, arguments);
+  };
+  jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
+
+  jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
+};
+
+
+jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
+jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
+jasmine.Env.prototype.setInterval = jasmine.setInterval;
+jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
+
+/**
+ * @returns an object containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.version = function () {
+  if (jasmine.version_) {
+    return jasmine.version_;
+  } else {
+    throw new Error('Version not set');
+  }
+};
+
+/**
+ * @returns string containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.versionString = function() {
+  if (!jasmine.version_) {
+    return "version unknown";
+  }
+
+  var version = this.version();
+  var versionString = version.major + "." + version.minor + "." + version.build;
+  if (version.release_candidate) {
+    versionString += ".rc" + version.release_candidate;
+  }
+  versionString += " revision " + version.revision;
+  return versionString;
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSpecId = function () {
+  return this.nextSpecId_++;
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSuiteId = function () {
+  return this.nextSuiteId_++;
+};
+
+/**
+ * Register a reporter to receive status updates from Jasmine.
+ * @param {jasmine.Reporter} reporter An object which will receive status updates.
+ */
+jasmine.Env.prototype.addReporter = function(reporter) {
+  this.reporter.addReporter(reporter);
+};
+
+jasmine.Env.prototype.execute = function() {
+  this.currentRunner_.execute();
+};
+
+jasmine.Env.prototype.describe = function(description, specDefinitions) {
+  var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
+
+  var parentSuite = this.currentSuite;
+  if (parentSuite) {
+    parentSuite.add(suite);
+  } else {
+    this.currentRunner_.add(suite);
+  }
+
+  this.currentSuite = suite;
+
+  var declarationError = null;
+  try {
+    specDefinitions.call(suite);
+  } catch(e) {
+    declarationError = e;
+  }
+
+  if (declarationError) {
+    this.it("encountered a declaration exception", function() {
+      throw declarationError;
+    });
+  }
+
+  this.currentSuite = parentSuite;
+
+  return suite;
+};
+
+jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
+  if (this.currentSuite) {
+    this.currentSuite.beforeEach(beforeEachFunction);
+  } else {
+    this.currentRunner_.beforeEach(beforeEachFunction);
+  }
+};
+
+jasmine.Env.prototype.currentRunner = function () {
+  return this.currentRunner_;
+};
+
+jasmine.Env.prototype.afterEach = function(afterEachFunction) {
+  if (this.currentSuite) {
+    this.currentSuite.afterEach(afterEachFunction);
+  } else {
+    this.currentRunner_.afterEach(afterEachFunction);
+  }
+
+};
+
+jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
+  return {
+    execute: function() {
+    }
+  };
+};
+
+jasmine.Env.prototype.it = function(description, func) {
+  var spec = new jasmine.Spec(this, this.currentSuite, description);
+  this.currentSuite.add(spec);
+  this.currentSpec = spec;
+
+  if (func) {
+    spec.runs(func);
+  }
+
+  return spec;
+};
+
+jasmine.Env.prototype.xit = function(desc, func) {
+  return {
+    id: this.nextSpecId(),
+    runs: function() {
+    }
+  };
+};
+
+jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
+  if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
+    return true;
+  }
+
+  a.__Jasmine_been_here_before__ = b;
+  b.__Jasmine_been_here_before__ = a;
+
+  var hasKey = function(obj, keyName) {
+    return obj !== null && obj[keyName] !== jasmine.undefined;
+  };
+
+  for (var property in b) {
+    if (!hasKey(a, property) && hasKey(b, property)) {
+      mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+    }
+  }
+  for (property in a) {
+    if (!hasKey(b, property) && hasKey(a, property)) {
+      mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
+    }
+  }
+  for (property in b) {
+    if (property == '__Jasmine_been_here_before__') continue;
+    if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
+      mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
+    }
+  }
+
+  if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
+    mismatchValues.push("arrays were not the same length");
+  }
+
+  delete a.__Jasmine_been_here_before__;
+  delete b.__Jasmine_been_here_before__;
+  return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+};
+
+jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
+  mismatchKeys = mismatchKeys || [];
+  mismatchValues = mismatchValues || [];
+
+  for (var i = 0; i < this.equalityTesters_.length; i++) {
+    var equalityTester = this.equalityTesters_[i];
+    var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
+    if (result !== jasmine.undefined) return result;
+  }
+
+  if (a === b) return true;
+
+  if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
+    return (a == jasmine.undefined && b == jasmine.undefined);
+  }
+
+  if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
+    return a === b;
+  }
+
+  if (a instanceof Date && b instanceof Date) {
+    return a.getTime() == b.getTime();
+  }
+
+  if (a.jasmineMatches) {
+    return a.jasmineMatches(b);
+  }
+
+  if (b.jasmineMatches) {
+    return b.jasmineMatches(a);
+  }
+
+  if (a instanceof jasmine.Matchers.ObjectContaining) {
+    return a.matches(b);
+  }
+
+  if (b instanceof jasmine.Matchers.ObjectContaining) {
+    return b.matches(a);
+  }
+
+  if (jasmine.isString_(a) && jasmine.isString_(b)) {
+    return (a == b);
+  }
+
+  if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
+    return (a == b);
+  }
+
+  if (typeof a === "object" && typeof b === "object") {
+    return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
+  }
+
+  //Straight check
+  return (a === b);
+};
+
+jasmine.Env.prototype.contains_ = function(haystack, needle) {
+  if (jasmine.isArray_(haystack)) {
+    for (var i = 0; i < haystack.length; i++) {
+      if (this.equals_(haystack[i], needle)) return true;
+    }
+    return false;
+  }
+  return haystack.indexOf(needle) >= 0;
+};
+
+jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
+  this.equalityTesters_.push(equalityTester);
+};
+/** No-op base class for Jasmine reporters.
+ *
+ * @constructor
+ */
+jasmine.Reporter = function() {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecResults = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.log = function(str) {
+};
+
+/**
+ * Blocks are functions with executable code that make up a spec.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {Function} func
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Block = function(env, func, spec) {
+  this.env = env;
+  this.func = func;
+  this.spec = spec;
+};
+
+jasmine.Block.prototype.execute = function(onComplete) {  
+  try {
+    this.func.apply(this.spec);
+  } catch (e) {
+    this.spec.fail(e);
+  }
+  onComplete();
+};
+/** JavaScript API reporter.
+ *
+ * @constructor
+ */
+jasmine.JsApiReporter = function() {
+  this.started = false;
+  this.finished = false;
+  this.suites_ = [];
+  this.results_ = {};
+};
+
+jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
+  this.started = true;
+  var suites = runner.topLevelSuites();
+  for (var i = 0; i < suites.length; i++) {
+    var suite = suites[i];
+    this.suites_.push(this.summarize_(suite));
+  }
+};
+
+jasmine.JsApiReporter.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
+  var isSuite = suiteOrSpec instanceof jasmine.Suite;
+  var summary = {
+    id: suiteOrSpec.id,
+    name: suiteOrSpec.description,
+    type: isSuite ? 'suite' : 'spec',
+    children: []
+  };
+  
+  if (isSuite) {
+    var children = suiteOrSpec.children();
+    for (var i = 0; i < children.length; i++) {
+      summary.children.push(this.summarize_(children[i]));
+    }
+  }
+  return summary;
+};
+
+jasmine.JsApiReporter.prototype.results = function() {
+  return this.results_;
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
+  return this.results_[specId];
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
+  this.finished = true;
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
+  this.results_[spec.id] = {
+    messages: spec.results().getItems(),
+    result: spec.results().failedCount > 0 ? "failed" : "passed"
+  };
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.log = function(str) {
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
+  var results = {};
+  for (var i = 0; i < specIds.length; i++) {
+    var specId = specIds[i];
+    results[specId] = this.summarizeResult_(this.results_[specId]);
+  }
+  return results;
+};
+
+jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
+  var summaryMessages = [];
+  var messagesLength = result.messages.length;
+  for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
+    var resultMessage = result.messages[messageIndex];
+    summaryMessages.push({
+      text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
+      passed: resultMessage.passed ? resultMessage.passed() : true,
+      type: resultMessage.type,
+      message: resultMessage.message,
+      trace: {
+        stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
+      }
+    });
+  }
+
+  return {
+    result : result.result,
+    messages : summaryMessages
+  };
+};
+
+/**
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param actual
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Matchers = function(env, actual, spec, opt_isNot) {
+  this.env = env;
+  this.actual = actual;
+  this.spec = spec;
+  this.isNot = opt_isNot || false;
+  this.reportWasCalled_ = false;
+};
+
+// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
+jasmine.Matchers.pp = function(str) {
+  throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
+};
+
+// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
+jasmine.Matchers.prototype.report = function(result, failing_message, details) {
+  throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
+};
+
+jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
+  for (var methodName in prototype) {
+    if (methodName == 'report') continue;
+    var orig = prototype[methodName];
+    matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
+  }
+};
+
+jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
+  return function() {
+    var matcherArgs = jasmine.util.argsToArray(arguments);
+    var result = matcherFunction.apply(this, arguments);
+
+    if (this.isNot) {
+      result = !result;
+    }
+
+    if (this.reportWasCalled_) return result;
+
+    var message;
+    if (!result) {
+      if (this.message) {
+        message = this.message.apply(this, arguments);
+        if (jasmine.isArray_(message)) {
+          message = message[this.isNot ? 1 : 0];
+        }
+      } else {
+        var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
+        message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
+        if (matcherArgs.length > 0) {
+          for (var i = 0; i < matcherArgs.length; i++) {
+            if (i > 0) message += ",";
+            message += " " + jasmine.pp(matcherArgs[i]);
+          }
+        }
+        message += ".";
+      }
+    }
+    var expectationResult = new jasmine.ExpectationResult({
+      matcherName: matcherName,
+      passed: result,
+      expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
+      actual: this.actual,
+      message: message
+    });
+    this.spec.addMatcherResult(expectationResult);
+    return jasmine.undefined;
+  };
+};
+
+
+
+
+/**
+ * toBe: compares the actual to the expected using ===
+ * @param expected
+ */
+jasmine.Matchers.prototype.toBe = function(expected) {
+  return this.actual === expected;
+};
+
+/**
+ * toNotBe: compares the actual to the expected using !==
+ * @param expected
+ * @deprecated as of 1.0. Use not.toBe() instead.
+ */
+jasmine.Matchers.prototype.toNotBe = function(expected) {
+  return this.actual !== expected;
+};
+
+/**
+ * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toEqual = function(expected) {
+  return this.env.equals_(this.actual, expected);
+};
+
+/**
+ * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
+ * @param expected
+ * @deprecated as of 1.0. Use not.toEqual() instead.
+ */
+jasmine.Matchers.prototype.toNotEqual = function(expected) {
+  return !this.env.equals_(this.actual, expected);
+};
+
+/**
+ * Matcher that compares the actual to the expected using a regular expression.  Constructs a RegExp, so takes
+ * a pattern or a String.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toMatch = function(expected) {
+  return new RegExp(expected).test(this.actual);
+};
+
+/**
+ * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
+ * @param expected
+ * @deprecated as of 1.0. Use not.toMatch() instead.
+ */
+jasmine.Matchers.prototype.toNotMatch = function(expected) {
+  return !(new RegExp(expected).test(this.actual));
+};
+
+/**
+ * Matcher that compares the actual to jasmine.undefined.
+ */
+jasmine.Matchers.prototype.toBeDefined = function() {
+  return (this.actual !== jasmine.undefined);
+};
+
+/**
+ * Matcher that compares the actual to jasmine.undefined.
+ */
+jasmine.Matchers.prototype.toBeUndefined = function() {
+  return (this.actual === jasmine.undefined);
+};
+
+/**
+ * Matcher that compares the actual to null.
+ */
+jasmine.Matchers.prototype.toBeNull = function() {
+  return (this.actual === null);
+};
+
+/**
+ * Matcher that boolean not-nots the actual.
+ */
+jasmine.Matchers.prototype.toBeTruthy = function() {
+  return !!this.actual;
+};
+
+
+/**
+ * Matcher that boolean nots the actual.
+ */
+jasmine.Matchers.prototype.toBeFalsy = function() {
+  return !this.actual;
+};
+
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was called.
+ */
+jasmine.Matchers.prototype.toHaveBeenCalled = function() {
+  if (arguments.length > 0) {
+    throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
+  }
+
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy " + this.actual.identity + " to have been called.",
+      "Expected spy " + this.actual.identity + " not to have been called."
+    ];
+  };
+
+  return this.actual.wasCalled;
+};
+
+/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
+jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was not called.
+ *
+ * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
+ */
+jasmine.Matchers.prototype.wasNotCalled = function() {
+  if (arguments.length > 0) {
+    throw new Error('wasNotCalled does not take arguments');
+  }
+
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy " + this.actual.identity + " to not have been called.",
+      "Expected spy " + this.actual.identity + " to have been called."
+    ];
+  };
+
+  return !this.actual.wasCalled;
+};
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
+ *
+ * @example
+ *
+ */
+jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
+  var expectedArgs = jasmine.util.argsToArray(arguments);
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+  this.message = function() {
+    if (this.actual.callCount === 0) {
+      // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
+      return [
+        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
+        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
+      ];
+    } else {
+      return [
+        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
+        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
+      ];
+    }
+  };
+
+  return this.env.contains_(this.actual.argsForCall, expectedArgs);
+};
+
+/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
+jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
+
+/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
+jasmine.Matchers.prototype.wasNotCalledWith = function() {
+  var expectedArgs = jasmine.util.argsToArray(arguments);
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
+      "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
+    ];
+  };
+
+  return !this.env.contains_(this.actual.argsForCall, expectedArgs);
+};
+
+/**
+ * Matcher that checks that the expected item is an element in the actual Array.
+ *
+ * @param {Object} expected
+ */
+jasmine.Matchers.prototype.toContain = function(expected) {
+  return this.env.contains_(this.actual, expected);
+};
+
+/**
+ * Matcher that checks that the expected item is NOT an element in the actual Array.
+ *
+ * @param {Object} expected
+ * @deprecated as of 1.0. Use not.toContain() instead.
+ */
+jasmine.Matchers.prototype.toNotContain = function(expected) {
+  return !this.env.contains_(this.actual, expected);
+};
+
+jasmine.Matchers.prototype.toBeLessThan = function(expected) {
+  return this.actual < expected;
+};
+
+jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
+  return this.actual > expected;
+};
+
+/**
+ * Matcher that checks that the expected item is equal to the actual item
+ * up to a given level of decimal precision (default 2).
+ *
+ * @param {Number} expected
+ * @param {Number} precision
+ */
+jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
+  if (!(precision === 0)) {
+    precision = precision || 2;
+  }
+  var multiplier = Math.pow(10, precision);
+  var actual = Math.round(this.actual * multiplier);
+  expected = Math.round(expected * multiplier);
+  return expected == actual;
+};
+
+/**
+ * Matcher that checks that the expected exception was thrown by the actual.
+ *
+ * @param {String} expected
+ */
+jasmine.Matchers.prototype.toThrow = function(expected) {
+  var result = false;
+  var exception;
+  if (typeof this.actual != 'function') {
+    throw new Error('Actual is not a function');
+  }
+  try {
+    this.actual();
+  } catch (e) {
+    exception = e;
+  }
+  if (exception) {
+    result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
+  }
+
+  var not = this.isNot ? "not " : "";
+
+  this.message = function() {
+    if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
+      return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
+    } else {
+      return "Expected function to throw an exception.";
+    }
+  };
+
+  return result;
+};
+
+jasmine.Matchers.Any = function(expectedClass) {
+  this.expectedClass = expectedClass;
+};
+
+jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {
+  if (this.expectedClass == String) {
+    return typeof other == 'string' || other instanceof String;
+  }
+
+  if (this.expectedClass == Number) {
+    return typeof other == 'number' || other instanceof Number;
+  }
+
+  if (this.expectedClass == Function) {
+    return typeof other == 'function' || other instanceof Function;
+  }
+
+  if (this.expectedClass == Object) {
+    return typeof other == 'object';
+  }
+
+  return other instanceof this.expectedClass;
+};
+
+jasmine.Matchers.Any.prototype.jasmineToString = function() {
+  return '<jasmine.any(' + this.expectedClass + ')>';
+};
+
+jasmine.Matchers.ObjectContaining = function (sample) {
+  this.sample = sample;
+};
+
+jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
+  mismatchKeys = mismatchKeys || [];
+  mismatchValues = mismatchValues || [];
+
+  var env = jasmine.getEnv();
+
+  var hasKey = function(obj, keyName) {
+    return obj != null && obj[keyName] !== jasmine.undefined;
+  };
+
+  for (var property in this.sample) {
+    if (!hasKey(other, property) && hasKey(this.sample, property)) {
+      mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+    }
+    else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
+      mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
+    }
+  }
+
+  return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+};
+
+jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
+  return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>";
+};
+// Mock setTimeout, clearTimeout
+// Contributed by Pivotal Computer Systems, www.pivotalsf.com
+
+jasmine.FakeTimer = function() {
+  this.reset();
+
+  var self = this;
+  self.setTimeout = function(funcToCall, millis) {
+    self.timeoutsMade++;
+    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
+    return self.timeoutsMade;
+  };
+
+  self.setInterval = function(funcToCall, millis) {
+    self.timeoutsMade++;
+    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
+    return self.timeoutsMade;
+  };
+
+  self.clearTimeout = function(timeoutKey) {
+    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
+  };
+
+  self.clearInterval = function(timeoutKey) {
+    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
+  };
+
+};
+
+jasmine.FakeTimer.prototype.reset = function() {
+  this.timeoutsMade = 0;
+  this.scheduledFunctions = {};
+  this.nowMillis = 0;
+};
+
+jasmine.FakeTimer.prototype.tick = function(millis) {
+  var oldMillis = this.nowMillis;
+  var newMillis = oldMillis + millis;
+  this.runFunctionsWithinRange(oldMillis, newMillis);
+  this.nowMillis = newMillis;
+};
+
+jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
+  var scheduledFunc;
+  var funcsToRun = [];
+  for (var timeoutKey in this.scheduledFunctions) {
+    scheduledFunc = this.scheduledFunctions[timeoutKey];
+    if (scheduledFunc != jasmine.undefined &&
+        scheduledFunc.runAtMillis >= oldMillis &&
+        scheduledFunc.runAtMillis <= nowMillis) {
+      funcsToRun.push(scheduledFunc);
+      this.scheduledFunctions[timeoutKey] = jasmine.undefined;
+    }
+  }
+
+  if (funcsToRun.length > 0) {
+    funcsToRun.sort(function(a, b) {
+      return a.runAtMillis - b.runAtMillis;
+    });
+    for (var i = 0; i < funcsToRun.length; ++i) {
+      try {
+        var funcToRun = funcsToRun[i];
+        this.nowMillis = funcToRun.runAtMillis;
+        funcToRun.funcToCall();
+        if (funcToRun.recurring) {
+          this.scheduleFunction(funcToRun.timeoutKey,
+              funcToRun.funcToCall,
+              funcToRun.millis,
+              true);
+        }
+      } catch(e) {
+      }
+    }
+    this.runFunctionsWithinRange(oldMillis, nowMillis);
+  }
+};
+
+jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
+  this.scheduledFunctions[timeoutKey] = {
+    runAtMillis: this.nowMillis + millis,
+    funcToCall: funcToCall,
+    recurring: recurring,
+    timeoutKey: timeoutKey,
+    millis: millis
+  };
+};
+
+/**
+ * @namespace
+ */
+jasmine.Clock = {
+  defaultFakeTimer: new jasmine.FakeTimer(),
+
+  reset: function() {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.defaultFakeTimer.reset();
+  },
+
+  tick: function(millis) {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.defaultFakeTimer.tick(millis);
+  },
+
+  runFunctionsWithinRange: function(oldMillis, nowMillis) {
+    jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
+  },
+
+  scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
+    jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
+  },
+
+  useMock: function() {
+    if (!jasmine.Clock.isInstalled()) {
+      var spec = jasmine.getEnv().currentSpec;
+      spec.after(jasmine.Clock.uninstallMock);
+
+      jasmine.Clock.installMock();
+    }
+  },
+
+  installMock: function() {
+    jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
+  },
+
+  uninstallMock: function() {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.installed = jasmine.Clock.real;
+  },
+
+  real: {
+    setTimeout: jasmine.getGlobal().setTimeout,
+    clearTimeout: jasmine.getGlobal().clearTimeout,
+    setInterval: jasmine.getGlobal().setInterval,
+    clearInterval: jasmine.getGlobal().clearInterval
+  },
+
+  assertInstalled: function() {
+    if (!jasmine.Clock.isInstalled()) {
+      throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
+    }
+  },
+
+  isInstalled: function() {
+    return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
+  },
+
+  installed: null
+};
+jasmine.Clock.installed = jasmine.Clock.real;
+
+//else for IE support
+jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
+  if (jasmine.Clock.installed.setTimeout.apply) {
+    return jasmine.Clock.installed.setTimeout.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.setTimeout(funcToCall, millis);
+  }
+};
+
+jasmine.getGlobal().setInterval = function(funcToCall, millis) {
+  if (jasmine.Clock.installed.setInterval.apply) {
+    return jasmine.Clock.installed.setInterval.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.setInterval(funcToCall, millis);
+  }
+};
+
+jasmine.getGlobal().clearTimeout = function(timeoutKey) {
+  if (jasmine.Clock.installed.clearTimeout.apply) {
+    return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.clearTimeout(timeoutKey);
+  }
+};
+
+jasmine.getGlobal().clearInterval = function(timeoutKey) {
+  if (jasmine.Clock.installed.clearTimeout.apply) {
+    return jasmine.Clock.installed.clearInterval.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.clearInterval(timeoutKey);
+  }
+};
+
+/**
+ * @constructor
+ */
+jasmine.MultiReporter = function() {
+  this.subReporters_ = [];
+};
+jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
+
+jasmine.MultiReporter.prototype.addReporter = function(reporter) {
+  this.subReporters_.push(reporter);
+};
+
+(function() {
+  var functionNames = [
+    "reportRunnerStarting",
+    "reportRunnerResults",
+    "reportSuiteResults",
+    "reportSpecStarting",
+    "reportSpecResults",
+    "log"
+  ];
+  for (var i = 0; i < functionNames.length; i++) {
+    var functionName = functionNames[i];
+    jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
+      return function() {
+        for (var j = 0; j < this.subReporters_.length; j++) {
+          var subReporter = this.subReporters_[j];
+          if (subReporter[functionName]) {
+            subReporter[functionName].apply(subReporter, arguments);
+          }
+        }
+      };
+    })(functionName);
+  }
+})();
+/**
+ * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
+ *
+ * @constructor
+ */
+jasmine.NestedResults = function() {
+  /**
+   * The total count of results
+   */
+  this.totalCount = 0;
+  /**
+   * Number of passed results
+   */
+  this.passedCount = 0;
+  /**
+   * Number of failed results
+   */
+  this.failedCount = 0;
+  /**
+   * Was this suite/spec skipped?
+   */
+  this.skipped = false;
+  /**
+   * @ignore
+   */
+  this.items_ = [];
+};
+
+/**
+ * Roll up the result counts.
+ *
+ * @param result
+ */
+jasmine.NestedResults.prototype.rollupCounts = function(result) {
+  this.totalCount += result.totalCount;
+  this.passedCount += result.passedCount;
+  this.failedCount += result.failedCount;
+};
+
+/**
+ * Adds a log message.
+ * @param values Array of message parts which will be concatenated later.
+ */
+jasmine.NestedResults.prototype.log = function(values) {
+  this.items_.push(new jasmine.MessageResult(values));
+};
+
+/**
+ * Getter for the results: message & results.
+ */
+jasmine.NestedResults.prototype.getItems = function() {
+  return this.items_;
+};
+
+/**
+ * Adds a result, tracking counts (total, passed, & failed)
+ * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
+ */
+jasmine.NestedResults.prototype.addResult = function(result) {
+  if (result.type != 'log') {
+    if (result.items_) {
+      this.rollupCounts(result);
+    } else {
+      this.totalCount++;
+      if (result.passed()) {
+        this.passedCount++;
+      } else {
+        this.failedCount++;
+      }
+    }
+  }
+  this.items_.push(result);
+};
+
+/**
+ * @returns {Boolean} True if <b>everything</b> below passed
+ */
+jasmine.NestedResults.prototype.passed = function() {
+  return this.passedCount === this.totalCount;
+};
+/**
+ * Base class for pretty printing for expectation results.
+ */
+jasmine.PrettyPrinter = function() {
+  this.ppNestLevel_ = 0;
+};
+
+/**
+ * Formats a value in a nice, human-readable string.
+ *
+ * @param value
+ */
+jasmine.PrettyPrinter.prototype.format = function(value) {
+  if (this.ppNestLevel_ > 40) {
+    throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
+  }
+
+  this.ppNestLevel_++;
+  try {
+    if (value === jasmine.undefined) {
+      this.emitScalar('undefined');
+    } else if (value === null) {
+      this.emitScalar('null');
+    } else if (value === jasmine.getGlobal()) {
+      this.emitScalar('<global>');
+    } else if (value.jasmineToString) {
+      this.emitScalar(value.jasmineToString());
+    } else if (typeof value === 'string') {
+      this.emitString(value);
+    } else if (jasmine.isSpy(value)) {
+      this.emitScalar("spy on " + value.identity);
+    } else if (value instanceof RegExp) {
+      this.emitScalar(value.toString());
+    } else if (typeof value === 'function') {
+      this.emitScalar('Function');
+    } else if (typeof value.nodeType === 'number') {
+      this.emitScalar('HTMLNode');
+    } else if (value instanceof Date) {
+      this.emitScalar('Date(' + value + ')');
+    } else if (value.__Jasmine_been_here_before__) {
+      this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
+    } else if (jasmine.isArray_(value) || typeof value == 'object') {
+      value.__Jasmine_been_here_before__ = true;
+      if (jasmine.isArray_(value)) {
+        this.emitArray(value);
+      } else {
+        this.emitObject(value);
+      }
+      delete value.__Jasmine_been_here_before__;
+    } else {
+      this.emitScalar(value.toString());
+    }
+  } finally {
+    this.ppNestLevel_--;
+  }
+};
+
+jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
+  for (var property in obj) {
+    if (property == '__Jasmine_been_here_before__') continue;
+    fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && 
+                                         obj.__lookupGetter__(property) !== null) : false);
+  }
+};
+
+jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
+
+jasmine.StringPrettyPrinter = function() {
+  jasmine.PrettyPrinter.call(this);
+
+  this.string = '';
+};
+jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
+
+jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
+  this.append(value);
+};
+
+jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
+  this.append("'" + value + "'");
+};
+
+jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
+  this.append('[ ');
+  for (var i = 0; i < array.length; i++) {
+    if (i > 0) {
+      this.append(', ');
+    }
+    this.format(array[i]);
+  }
+  this.append(' ]');
+};
+
+jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
+  var self = this;
+  this.append('{ ');
+  var first = true;
+
+  this.iterateObject(obj, function(property, isGetter) {
+    if (first) {
+      first = false;
+    } else {
+      self.append(', ');
+    }
+
+    self.append(property);
+    self.append(' : ');
+    if (isGetter) {
+      self.append('<getter>');
+    } else {
+      self.format(obj[property]);
+    }
+  });
+
+  this.append(' }');
+};
+
+jasmine.StringPrettyPrinter.prototype.append = function(value) {
+  this.string += value;
+};
+jasmine.Queue = function(env) {
+  this.env = env;
+  this.blocks = [];
+  this.running = false;
+  this.index = 0;
+  this.offset = 0;
+  this.abort = false;
+};
+
+jasmine.Queue.prototype.addBefore = function(block) {
+  this.blocks.unshift(block);
+};
+
+jasmine.Queue.prototype.add = function(block) {
+  this.blocks.push(block);
+};
+
+jasmine.Queue.prototype.insertNext = function(block) {
+  this.blocks.splice((this.index + this.offset + 1), 0, block);
+  this.offset++;
+};
+
+jasmine.Queue.prototype.start = function(onComplete) {
+  this.running = true;
+  this.onComplete = onComplete;
+  this.next_();
+};
+
+jasmine.Queue.prototype.isRunning = function() {
+  return this.running;
+};
+
+jasmine.Queue.LOOP_DONT_RECURSE = true;
+
+jasmine.Queue.prototype.next_ = function() {
+  var self = this;
+  var goAgain = true;
+
+  while (goAgain) {
+    goAgain = false;
+    
+    if (self.index < self.blocks.length && !this.abort) {
+      var calledSynchronously = true;
+      var completedSynchronously = false;
+
+      var onComplete = function () {
+        if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
+          completedSynchronously = true;
+          return;
+        }
+
+        if (self.blocks[self.index].abort) {
+          self.abort = true;
+        }
+
+        self.offset = 0;
+        self.index++;
+
+        var now = new Date().getTime();
+        if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
+          self.env.lastUpdate = now;
+          self.env.setTimeout(function() {
+            self.next_();
+          }, 0);
+        } else {
+          if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
+            goAgain = true;
+          } else {
+            self.next_();
+          }
+        }
+      };
+      self.blocks[self.index].execute(onComplete);
+
+      calledSynchronously = false;
+      if (completedSynchronously) {
+        onComplete();
+      }
+      
+    } else {
+      self.running = false;
+      if (self.onComplete) {
+        self.onComplete();
+      }
+    }
+  }
+};
+
+jasmine.Queue.prototype.results = function() {
+  var results = new jasmine.NestedResults();
+  for (var i = 0; i < this.blocks.length; i++) {
+    if (this.blocks[i].results) {
+      results.addResult(this.blocks[i].results());
+    }
+  }
+  return results;
+};
+
+
+/**
+ * Runner
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ */
+jasmine.Runner = function(env) {
+  var self = this;
+  self.env = env;
+  self.queue = new jasmine.Queue(env);
+  self.before_ = [];
+  self.after_ = [];
+  self.suites_ = [];
+};
+
+jasmine.Runner.prototype.execute = function() {
+  var self = this;
+  if (self.env.reporter.reportRunnerStarting) {
+    self.env.reporter.reportRunnerStarting(this);
+  }
+  self.queue.start(function () {
+    self.finishCallback();
+  });
+};
+
+jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
+  beforeEachFunction.typeName = 'beforeEach';
+  this.before_.splice(0,0,beforeEachFunction);
+};
+
+jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
+  afterEachFunction.typeName = 'afterEach';
+  this.after_.splice(0,0,afterEachFunction);
+};
+
+
+jasmine.Runner.prototype.finishCallback = function() {
+  this.env.reporter.reportRunnerResults(this);
+};
+
+jasmine.Runner.prototype.addSuite = function(suite) {
+  this.suites_.push(suite);
+};
+
+jasmine.Runner.prototype.add = function(block) {
+  if (block instanceof jasmine.Suite) {
+    this.addSuite(block);
+  }
+  this.queue.add(block);
+};
+
+jasmine.Runner.prototype.specs = function () {
+  var suites = this.suites();
+  var specs = [];
+  for (var i = 0; i < suites.length; i++) {
+    specs = specs.concat(suites[i].specs());
+  }
+  return specs;
+};
+
+jasmine.Runner.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.Runner.prototype.topLevelSuites = function() {
+  var topLevelSuites = [];
+  for (var i = 0; i < this.suites_.length; i++) {
+    if (!this.suites_[i].parentSuite) {
+      topLevelSuites.push(this.suites_[i]);
+    }
+  }
+  return topLevelSuites;
+};
+
+jasmine.Runner.prototype.results = function() {
+  return this.queue.results();
+};
+/**
+ * Internal representation of a Jasmine specification, or test.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {jasmine.Suite} suite
+ * @param {String} description
+ */
+jasmine.Spec = function(env, suite, description) {
+  if (!env) {
+    throw new Error('jasmine.Env() required');
+  }
+  if (!suite) {
+    throw new Error('jasmine.Suite() required');
+  }
+  var spec = this;
+  spec.id = env.nextSpecId ? env.nextSpecId() : null;
+  spec.env = env;
+  spec.suite = suite;
+  spec.description = description;
+  spec.queue = new jasmine.Queue(env);
+
+  spec.afterCallbacks = [];
+  spec.spies_ = [];
+
+  spec.results_ = new jasmine.NestedResults();
+  spec.results_.description = description;
+  spec.matchersClass = null;
+};
+
+jasmine.Spec.prototype.getFullName = function() {
+  return this.suite.getFullName() + ' ' + this.description + '.';
+};
+
+
+jasmine.Spec.prototype.results = function() {
+  return this.results_;
+};
+
+/**
+ * All parameters are pretty-printed and concatenated together, then written to the spec's output.
+ *
+ * Be careful not to leave calls to <code>jasmine.log</code> in production code.
+ */
+jasmine.Spec.prototype.log = function() {
+  return this.results_.log(arguments);
+};
+
+jasmine.Spec.prototype.runs = function (func) {
+  var block = new jasmine.Block(this.env, func, this);
+  this.addToQueue(block);
+  return this;
+};
+
+jasmine.Spec.prototype.addToQueue = function (block) {
+  if (this.queue.isRunning()) {
+    this.queue.insertNext(block);
+  } else {
+    this.queue.add(block);
+  }
+};
+
+/**
+ * @param {jasmine.ExpectationResult} result
+ */
+jasmine.Spec.prototype.addMatcherResult = function(result) {
+  this.results_.addResult(result);
+};
+
+jasmine.Spec.prototype.expect = function(actual) {
+  var positive = new (this.getMatchersClass_())(this.env, actual, this);
+  positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
+  return positive;
+};
+
+/**
+ * Waits a fixed time period before moving to the next block.
+ *
+ * @deprecated Use waitsFor() instead
+ * @param {Number} timeout milliseconds to wait
+ */
+jasmine.Spec.prototype.waits = function(timeout) {
+  var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
+  this.addToQueue(waitsFunc);
+  return this;
+};
+
+/**
+ * Waits for the latchFunction to return true before proceeding to the next block.
+ *
+ * @param {Function} latchFunction
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
+  var latchFunction_ = null;
+  var optional_timeoutMessage_ = null;
+  var optional_timeout_ = null;
+
+  for (var i = 0; i < arguments.length; i++) {
+    var arg = arguments[i];
+    switch (typeof arg) {
+      case 'function':
+        latchFunction_ = arg;
+        break;
+      case 'string':
+        optional_timeoutMessage_ = arg;
+        break;
+      case 'number':
+        optional_timeout_ = arg;
+        break;
+    }
+  }
+
+  var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
+  this.addToQueue(waitsForFunc);
+  return this;
+};
+
+jasmine.Spec.prototype.fail = function (e) {
+  var expectationResult = new jasmine.ExpectationResult({
+    passed: false,
+    message: e ? jasmine.util.formatException(e) : 'Exception',
+    trace: { stack: e.stack }
+  });
+  this.results_.addResult(expectationResult);
+};
+
+jasmine.Spec.prototype.getMatchersClass_ = function() {
+  return this.matchersClass || this.env.matchersClass;
+};
+
+jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
+  var parent = this.getMatchersClass_();
+  var newMatchersClass = function() {
+    parent.apply(this, arguments);
+  };
+  jasmine.util.inherit(newMatchersClass, parent);
+  jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
+  this.matchersClass = newMatchersClass;
+};
+
+jasmine.Spec.prototype.finishCallback = function() {
+  this.env.reporter.reportSpecResults(this);
+};
+
+jasmine.Spec.prototype.finish = function(onComplete) {
+  this.removeAllSpies();
+  this.finishCallback();
+  if (onComplete) {
+    onComplete();
+  }
+};
+
+jasmine.Spec.prototype.after = function(doAfter) {
+  if (this.queue.isRunning()) {
+    this.queue.add(new jasmine.Block(this.env, doAfter, this));
+  } else {
+    this.afterCallbacks.unshift(doAfter);
+  }
+};
+
+jasmine.Spec.prototype.execute = function(onComplete) {
+  var spec = this;
+  if (!spec.env.specFilter(spec)) {
+    spec.results_.skipped = true;
+    spec.finish(onComplete);
+    return;
+  }
+
+  this.env.reporter.reportSpecStarting(this);
+
+  spec.env.currentSpec = spec;
+
+  spec.addBeforesAndAftersToQueue();
+
+  spec.queue.start(function () {
+    spec.finish(onComplete);
+  });
+};
+
+jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
+  var runner = this.env.currentRunner();
+  var i;
+
+  for (var suite = this.suite; suite; suite = suite.parentSuite) {
+    for (i = 0; i < suite.before_.length; i++) {
+      this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
+    }
+  }
+  for (i = 0; i < runner.before_.length; i++) {
+    this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
+  }
+  for (i = 0; i < this.afterCallbacks.length; i++) {
+    this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
+  }
+  for (suite = this.suite; suite; suite = suite.parentSuite) {
+    for (i = 0; i < suite.after_.length; i++) {
+      this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
+    }
+  }
+  for (i = 0; i < runner.after_.length; i++) {
+    this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
+  }
+};
+
+jasmine.Spec.prototype.explodes = function() {
+  throw 'explodes function should not have been called';
+};
+
+jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
+  if (obj == jasmine.undefined) {
+    throw "spyOn could not find an object to spy upon for " + methodName + "()";
+  }
+
+  if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
+    throw methodName + '() method does not exist';
+  }
+
+  if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
+    throw new Error(methodName + ' has already been spied upon');
+  }
+
+  var spyObj = jasmine.createSpy(methodName);
+
+  this.spies_.push(spyObj);
+  spyObj.baseObj = obj;
+  spyObj.methodName = methodName;
+  spyObj.originalValue = obj[methodName];
+
+  obj[methodName] = spyObj;
+
+  return spyObj;
+};
+
+jasmine.Spec.prototype.removeAllSpies = function() {
+  for (var i = 0; i < this.spies_.length; i++) {
+    var spy = this.spies_[i];
+    spy.baseObj[spy.methodName] = spy.originalValue;
+  }
+  this.spies_ = [];
+};
+
+/**
+ * Internal representation of a Jasmine suite.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {String} description
+ * @param {Function} specDefinitions
+ * @param {jasmine.Suite} parentSuite
+ */
+jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
+  var self = this;
+  self.id = env.nextSuiteId ? env.nextSuiteId() : null;
+  self.description = description;
+  self.queue = new jasmine.Queue(env);
+  self.parentSuite = parentSuite;
+  self.env = env;
+  self.before_ = [];
+  self.after_ = [];
+  self.children_ = [];
+  self.suites_ = [];
+  self.specs_ = [];
+};
+
+jasmine.Suite.prototype.getFullName = function() {
+  var fullName = this.description;
+  for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
+    fullName = parentSuite.description + ' ' + fullName;
+  }
+  return fullName;
+};
+
+jasmine.Suite.prototype.finish = function(onComplete) {
+  this.env.reporter.reportSuiteResults(this);
+  this.finished = true;
+  if (typeof(onComplete) == 'function') {
+    onComplete();
+  }
+};
+
+jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
+  beforeEachFunction.typeName = 'beforeEach';
+  this.before_.unshift(beforeEachFunction);
+};
+
+jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
+  afterEachFunction.typeName = 'afterEach';
+  this.after_.unshift(afterEachFunction);
+};
+
+jasmine.Suite.prototype.results = function() {
+  return this.queue.results();
+};
+
+jasmine.Suite.prototype.add = function(suiteOrSpec) {
+  this.children_.push(suiteOrSpec);
+  if (suiteOrSpec instanceof jasmine.Suite) {
+    this.suites_.push(suiteOrSpec);
+    this.env.currentRunner().addSuite(suiteOrSpec);
+  } else {
+    this.specs_.push(suiteOrSpec);
+  }
+  this.queue.add(suiteOrSpec);
+};
+
+jasmine.Suite.prototype.specs = function() {
+  return this.specs_;
+};
+
+jasmine.Suite.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.Suite.prototype.children = function() {
+  return this.children_;
+};
+
+jasmine.Suite.prototype.execute = function(onComplete) {
+  var self = this;
+  this.queue.start(function () {
+    self.finish(onComplete);
+  });
+};
+jasmine.WaitsBlock = function(env, timeout, spec) {
+  this.timeout = timeout;
+  jasmine.Block.call(this, env, null, spec);
+};
+
+jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
+
+jasmine.WaitsBlock.prototype.execute = function (onComplete) {
+  if (jasmine.VERBOSE) {
+    this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
+  }
+  this.env.setTimeout(function () {
+    onComplete();
+  }, this.timeout);
+};
+/**
+ * A block which waits for some condition to become true, with timeout.
+ *
+ * @constructor
+ * @extends jasmine.Block
+ * @param {jasmine.Env} env The Jasmine environment.
+ * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
+ * @param {Function} latchFunction A function which returns true when the desired condition has been met.
+ * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
+ * @param {jasmine.Spec} spec The Jasmine spec.
+ */
+jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
+  this.timeout = timeout || env.defaultTimeoutInterval;
+  this.latchFunction = latchFunction;
+  this.message = message;
+  this.totalTimeSpentWaitingForLatch = 0;
+  jasmine.Block.call(this, env, null, spec);
+};
+jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
+
+jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
+
+jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
+  if (jasmine.VERBOSE) {
+    this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
+  }
+  var latchFunctionResult;
+  try {
+    latchFunctionResult = this.latchFunction.apply(this.spec);
+  } catch (e) {
+    this.spec.fail(e);
+    onComplete();
+    return;
+  }
+
+  if (latchFunctionResult) {
+    onComplete();
+  } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
+    var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
+    this.spec.fail({
+      name: 'timeout',
+      message: message
+    });
+
+    this.abort = true;
+    onComplete();
+  } else {
+    this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
+    var self = this;
+    this.env.setTimeout(function() {
+      self.execute(onComplete);
+    }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
+  }
+};
+
+jasmine.version_= {
+  "major": 1,
+  "minor": 2,
+  "build": 0,
+  "revision": 1333310630,
+  "release_candidate": 1
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/accelerometer.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/accelerometer.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/accelerometer.html
new file mode 100644
index 0000000..bac1836
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/accelerometer.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Cordova: Accelerometer API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/accelerometer.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/all.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/all.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/all.html
new file mode 100644
index 0000000..c6cc910
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/all.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Cordova: API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/accelerometer.tests.js"></script>
+  <script type="text/javascript" src="../tests/battery.tests.js"></script>
+  <script type="text/javascript" src="../tests/capture.tests.js"></script>
+  <script type="text/javascript" src="../tests/compass.tests.js"></script>
+  <script type="text/javascript" src="../tests/contacts.tests.js"></script>
+  <script type="text/javascript" src="../tests/camera.tests.js"></script>
+  <script type="text/javascript" src="../tests/datauri.tests.js"></script>
+  <script type="text/javascript" src="../tests/device.tests.js"></script>
+  <script type="text/javascript" src="../tests/file.tests.js"></script>
+  <script type="text/javascript" src="../tests/filetransfer.tests.js"></script>
+  <script type="text/javascript" src="../tests/geolocation.tests.js"></script>
+  <!-- script type="text/javascript" src="../tests/globalization.tests.js"></script -->
+  <script type="text/javascript" src="../tests/media.tests.js"></script>
+  <script type="text/javascript" src="../tests/network.tests.js"></script>
+  <script type="text/javascript" src="../tests/notification.tests.js"></script>
+  <script type="text/javascript" src="../tests/platform.tests.js"></script>
+  <script type="text/javascript" src="../tests/storage.tests.js"></script>
+
+  <script type="text/javascript">
+      var root, temp_root, persistent_root;
+
+      document.addEventListener('deviceready', function () {
+          // one-time retrieval of the root file system entry
+          var onError = function(e) {
+              console.log('[ERROR] Problem setting up root filesystem for test running! Error to follow.');
+              console.log(JSON.stringify(e));
+          };
+
+          window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting PERSISTENT FS.');
+                  root = fileSystem.root; // set in file.tests.js
+                  persistent_root = root;
+
+                  // Once root is set up, fire off tests
+                  var jasmineEnv = jasmine.getEnv();
+                  jasmineEnv.updateInterval = 1000;
+
+                  var htmlReporter = new jasmine.HtmlReporter();
+
+                  jasmineEnv.addReporter(htmlReporter);
+
+                  jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                  };
+
+                  jasmineEnv.execute();
+              }, onError);
+          window.requestFileSystem(LocalFileSystem.TEMPORARY, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting TEMPORARY FS.');
+                  temp_root = fileSystem.root; // set in file.tests.js
+              }, onError);
+      }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/battery.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/battery.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/battery.html
new file mode 100644
index 0000000..8441950
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/battery.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Cordova: Battery API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/battery.tests.js"></script>
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/bridge.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/bridge.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/bridge.html
new file mode 100644
index 0000000..a1d0a90
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/bridge.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<!--
+
+ 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.
+
+-->
+
+
+<html>
+
+<head>
+  <title>Cordova: Device API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/bridge.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/camera.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/camera.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/camera.html
new file mode 100644
index 0000000..e136f27
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/camera.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Camera API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/camera.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/capture.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/capture.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/capture.html
new file mode 100644
index 0000000..0cb2baf
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/capture.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Capture API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/capture.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/compass.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/compass.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/compass.html
new file mode 100644
index 0000000..dfd9975
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/autotest/pages/compass.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Compass API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/compass.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+


[06/50] Add WP7 and WP8 support to cordova-cli.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/platform-script/wp8/wp8.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/wp8/wp8.spec.js b/spec/platform-script/wp8/wp8.spec.js
new file mode 100644
index 0000000..998094c
--- /dev/null
+++ b/spec/platform-script/wp8/wp8.spec.js
@@ -0,0 +1,99 @@
+var cordova = require('../../../cordova'),
+    shell = require('shelljs'),
+    path = require('path'),
+    fs = require('fs'),
+    wp8_parser = require('../../../src/metadata/wp8_parser'),
+    tempDir = path.join(__dirname, '..', '..', '..', 'temp'),
+    fixtures = path.join(__dirname, '..', '..', 'fixtures'),
+    cordova_project = path.join(fixtures, 'projects', 'cordova');
+
+var cwd = process.cwd();
+
+describe('Test:', function() {
+    afterEach(function() {
+        process.chdir(cwd);
+    });
+
+    describe('\'platform add wp8\'', function() {
+        var sh, cr;
+        var fake_reqs_check = function() {
+            expect(cr.mostRecentCall.args).toBeDefined();
+            cr.mostRecentCall.args[0](false);
+        };
+        var fake_create = function(a_path) {
+            shell.mkdir('-p', a_path);
+            fs.writeFileSync(path.join(a_path, 'wp7Project.csproj'), 'hi', 'utf-8');
+            fs.writeFileSync(path.join(a_path, 'wp7Project.sln'), 'hi', 'utf-8');
+            sh.mostRecentCall.args[2](0, '');
+        };
+        beforeEach(function() {
+            sh = spyOn(shell, 'exec');
+            cr = spyOn(wp8_parser, 'check_requirements');
+            shell.rm('-rf', tempDir);
+            cordova.create(tempDir);
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        it('should shell out to wp8 /bin/create', function() {
+            cordova.platform('add', 'wp8');
+            fake_reqs_check();
+            var shell_cmd = sh.mostRecentCall.args[0];
+            var create_cmd = path.join('wp8', 'bin', 'create');
+            expect(shell_cmd).toContain(create_cmd);
+        });
+        it('should call wp8_parser\'s update_project', function() {
+            spyOn(wp8_parser.prototype, 'update_project');
+            cordova.platform('add', 'wp8');
+            fake_reqs_check();
+            fake_create(path.join(tempDir, 'platforms', 'wp8'));
+            expect(wp8_parser.prototype.update_project).toHaveBeenCalled();
+        });
+    });
+
+    describe('\'emulate wp8\'', function() {
+        beforeEach(function() {
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        shell.rm('-rf', tempDir);
+        cordova.create(tempDir);
+        shell.cp('-rf', path.join(cordova_project, 'platforms', 'wp8'), path.join(tempDir, 'platforms'));
+        it('should shell out to run command on wp8', function() {
+            var proj_spy = spyOn(wp8_parser.prototype, 'update_project');
+            var s = spyOn(require('shelljs'), 'exec');
+            cordova.emulate('wp8');
+            proj_spy.mostRecentCall.args[1](); // update_project fake
+            expect(s).toHaveBeenCalled();
+            var emulate_cmd = path.join('wp8', 'cordova', 'run');
+            expect(s.mostRecentCall.args[0]).toContain(emulate_cmd);
+        });
+        it('should call wp8_parser\'s update_project', function() {
+            spyOn(require('shelljs'), 'exec');
+            spyOn(wp8_parser.prototype, 'update_project');
+            cordova.emulate('wp8');
+            expect(wp8_parser.prototype.update_project).toHaveBeenCalled();
+        });
+    });
+
+    describe('\'compile wp8\'', function() {
+        beforeEach(function() {
+            process.chdir(tempDir);
+        });
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+        shell.rm('-rf', tempDir);
+        cordova.create(tempDir);
+        shell.cp('-rf', path.join(cordova_project, 'platforms', 'wp8'), path.join(tempDir, 'platforms'));
+        it('should shell out to build command', function() {
+            var build_cmd = path.join('wp8', 'cordova', 'build');
+            var s = spyOn(require('shelljs'), 'exec').andReturn({code:0});
+            cordova.compile('wp8');
+            expect(s.mostRecentCall.args[0]).toContain(build_cmd);
+        });
+    });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/spec/platform-script/wp8/wp8_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform-script/wp8/wp8_parser.spec.js b/spec/platform-script/wp8/wp8_parser.spec.js
new file mode 100644
index 0000000..5669e02
--- /dev/null
+++ b/spec/platform-script/wp8/wp8_parser.spec.js
@@ -0,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 wp8_parser = require('../../../src/metadata/wp8_parser'),
+    config_parser = require('../../../src/config_parser'),
+    util = require('../../../src/util'),
+    path = require('path'),
+    shell = require('shelljs'),
+    fs = require('fs'),
+    os = require('os'),
+    et = require('elementtree'),
+    cordova = require('../../../cordova'),
+    projects_path = path.join(__dirname, '..', '..', 'fixtures', 'projects'),
+    wp8_path = path.join(projects_path, 'native', 'wp8_fixture'),
+    project_path = path.join(projects_path, 'cordova'),
+    wp8_project_path = path.join(project_path, 'platforms', 'wp8');
+
+var www_config = util.projectConfig(project_path);
+var original_www_config = fs.readFileSync(www_config, 'utf-8');
+
+describe('wp8 project parser', function() {
+    it('should throw an exception with a path that is not a native wp8 project', function() {
+        expect(function() {
+            var project = new wp8_parser(process.cwd());
+        }).toThrow();
+    });
+    it('should accept a proper native wp8 project path as construction parameter', function() {
+        expect(function() {
+            var project = new wp8_parser(wp8_path);
+            expect(project).toBeDefined();
+        }).not.toThrow();
+    });
+
+    describe('update_from_config method', function() {
+        var config;
+        var project = new wp8_parser(wp8_path);
+
+        var manifest_path  = path.join(wp8_path, 'Properties', 'WMAppManifest.xml');
+        var csproj_path    = project.csproj_path;
+        var sln_path       = project.sln_path;
+        var app_xaml_path  = path.join(wp8_path, 'App.xaml');
+        var app_cs_path    = path.join(wp8_path, 'App.xaml.cs');
+        var main_xaml_path = path.join(wp8_path, 'MainPage.xaml');
+        var main_cs_path   = path.join(wp8_path, 'MainPage.xaml.cs');
+
+
+        var original_manifest  = fs.readFileSync(manifest_path, 'utf-8');
+        var original_csproj    = fs.readFileSync(csproj_path, 'utf-8');
+        var original_sln       = fs.readFileSync(sln_path, 'utf-8');
+        var original_app_xaml  = fs.readFileSync(app_xaml_path, 'utf-8');
+        var original_app_cs    = fs.readFileSync(app_cs_path, 'utf-8');
+        var original_main_xaml = fs.readFileSync(main_xaml_path, 'utf-8');
+        var original_main_cs   = fs.readFileSync(main_cs_path, 'utf-8');
+
+        beforeEach(function() {
+            project = new wp8_parser(wp8_path);
+            config = new config_parser(www_config);
+        });
+        afterEach(function() {
+            fs.writeFileSync(manifest_path, original_manifest, 'utf-8');
+            // csproj file changes name if app changes name
+            fs.unlinkSync(project.csproj_path);
+            fs.unlinkSync(project.sln_path);
+            fs.writeFileSync(csproj_path, original_csproj, 'utf-8');
+            fs.writeFileSync(sln_path, original_sln, 'utf-8');
+            fs.writeFileSync(app_xaml_path, original_app_xaml, 'utf-8');
+            fs.writeFileSync(app_cs_path, original_app_cs, 'utf-8');
+            fs.writeFileSync(main_xaml_path, original_main_xaml, 'utf-8');
+            fs.writeFileSync(main_cs_path, original_main_cs, '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() {
+            var test_name = 'bond. james bond.';
+            config.name(test_name);
+            project.update_from_config(config);
+            var raw_manifest = fs.readFileSync(manifest_path, 'utf-8');
+            //Strip three bytes that windows adds (http://www.multiasking.com/2012/11/851)
+            var cleaned_manifest = raw_manifest.replace('\ufeff', '');
+            var manifest = new et.ElementTree(et.XML(cleaned_manifest));
+            var app_name = manifest.find('.//App[@Title]')['attrib']['Title'];
+            expect(app_name).toBe(test_name);
+
+            //check for the proper name of csproj and solution files
+            test_name = test_name.replace(/(\.\s|\s\.|\s+|\.+)/g, '_'); //make it a ligitamate name
+            expect(project.csproj_path).toContain(test_name);
+            expect(project.sln_path).toContain(test_name);
+        });
+        it('should update the application package name properly', function() {
+            var test_package = 'ca.filmaj.dewd'
+            config.packageName(test_package);
+            project.update_from_config(config);
+
+            // check csproj file (use regex instead of elementtree?)
+            var raw_csproj = fs.readFileSync(project.csproj_path, 'utf-8');
+            var cleaned_csproj = raw_csproj.replace(/^\uFEFF/i, '');
+            var csproj = new et.ElementTree(et.XML(cleaned_csproj));
+            expect(csproj.find('.//RootNamespace').text).toEqual(test_package);
+            expect(csproj.find('.//AssemblyName').text).toEqual(test_package);
+            expect(csproj.find('.//XapFilename').text).toEqual(test_package + '.xap');
+            expect(csproj.find('.//SilverlightAppEntry').text).toEqual(test_package + '.App');
+
+            // check app.xaml (use regex instead of elementtree?)
+            var new_app_xaml = fs.readFileSync(app_xaml_path, 'utf-8');
+            var cleaned_app_xaml = new_app_xaml.replace(/^\uFEFF/i, '');
+            var app_xaml = new et.ElementTree(et.XML(cleaned_app_xaml));
+            expect(app_xaml._root.attrib['x:Class']).toEqual(test_package + '.App');
+
+            // check app.xaml.cs
+            var new_app_cs = fs.readFileSync(app_cs_path, 'utf-8');
+            expect(new_app_cs).toContain('namespace ' + test_package);
+
+            // check MainPage.xaml (use regex instead of elementtree?)
+            var new_main_xaml = fs.readFileSync(main_xaml_path, 'utf-8');
+            var cleaned_main_xaml = new_main_xaml.replace(/^\uFEFF/i, '');
+            var main_xaml = new et.ElementTree(et.XML(cleaned_main_xaml));
+            expect(main_xaml._root.attrib['x:Class']).toEqual(test_package + '.MainPage');
+
+            //check MainPage.xaml.cs
+            var new_main_cs = fs.readFileSync(main_cs_path, 'utf-8');
+            expect(new_main_cs).toContain('namespace ' + test_package);
+        });
+        xdescribe('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');*/
+
+                // TODO : figure out if this is supported
+                //expect(true).toBe(false);
+            });
+            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');*/
+
+                // TODO : figure out if this is supported
+                //expect(true).toBe(false);
+            });
+        });
+    });
+
+    describe('cross-platform project level methods', function() {
+        var parser, config;
+
+        beforeEach(function() {
+            parser = new wp8_parser(wp8_project_path);
+            config = new config_parser(www_config);
+        });
+        afterEach(function() {
+        });
+        describe('update_www method', function() {
+            it('should update all www assets', function() {
+                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(wp8_project_path, 'www', 'somescript.js'))).toBe(true);
+            });
+            it('should write out windows-phone js to cordova.js', function() {
+                parser.update_www();
+                var raw_version = fs.readFileSync(path.join(util.libDirectory, 'cordova-wp8', 'VERSION'), 'utf-8');
+                var wp8_version = raw_version.replace(/\r\n/,'').replace(/\n/,'');
+                expect(fs.readFileSync(path.join(wp8_project_path, 'www', 'cordova.js'),'utf-8')).toEqual(fs.readFileSync(path.join(util.libDirectory, 'cordova-wp8', 'templates', 'standalone', 'www', 'cordova-' + wp8_version + '.js'), 'utf-8'));
+            });
+        });
+
+        xdescribe('update_overrides method',function() {
+            /*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(wp8_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(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(wp8_project_path, 'assets', 'www', 'merge.js'))).toBe(true);
+                expect(fs.readFileSync(path.join(wp8_project_path, 'assets', 'www', 'merge.js'),'utf-8')).toEqual('alert("sup");');
+            });*/
+
+            // TODO : figure out if this is supported
+            //expect(true).toBe(false);
+        });
+
+        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();
+            });
+        });
+    });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/src/compile.js
----------------------------------------------------------------------
diff --git a/src/compile.js b/src/compile.js
index 4efbd6b..c88c36e 100644
--- a/src/compile.js
+++ b/src/compile.js
@@ -19,14 +19,11 @@
 var cordova_util      = require('./util'),
     path              = require('path'),
     config_parser     = require('./config_parser'),
-    platform          = require('./platform'),
     fs                = require('fs'),
     shell             = require('shelljs'),
     et                = require('elementtree'),
     hooker            = require('./hooker'),
-    n                 = require('ncallbacks'),
-    prompt            = require('prompt'),
-    util              = require('util');
+    n                 = require('ncallbacks');
 
 
 function shell_out_to_debug(projectRoot, platform, callback) {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/src/create.js
----------------------------------------------------------------------
diff --git a/src/create.js b/src/create.js
index 0f7c8a6..f1d1792 100644
--- a/src/create.js
+++ b/src/create.js
@@ -45,7 +45,7 @@ module.exports = function create (dir, id, name) {
     id = id || DEFAULT_ID;
     name = name || DEFAULT_NAME;
 
-    if (!(dir && (dir[0] == '~' || dir[0] == '/'))) {
+    if (!(dir && (dir[0] == '~' || dir[0] == '/' || dir[0] + dir[1] == 'C:'))) {
         dir = dir ? path.join(process.cwd(), dir) : process.cwd();
     }
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/src/emulate.js
----------------------------------------------------------------------
diff --git a/src/emulate.js b/src/emulate.js
index 7acb441..b396eeb 100644
--- a/src/emulate.js
+++ b/src/emulate.js
@@ -23,6 +23,8 @@ var cordova_util      = require('./util'),
     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'),
     platform          = require('./platform'),
     fs                = require('fs'),
     ls                = fs.readdirSync,
@@ -33,16 +35,16 @@ var cordova_util      = require('./util'),
 var parsers = {
     "android":android_parser,
     "ios":ios_parser,
-    "blackberry":blackberry_parser
+    "blackberry":blackberry_parser,
+    "wp7":wp7_parser,
+    "wp8":wp8_parser
 };
 
 function shell_out_to_emulate(root, platform, callback) {
-    var cmd = '"' + path.join(root, 'platforms', platform, 'cordova', 'emulate') + '"';
+    var cmd = '"' + path.join(root, 'platforms', platform, 'cordova', 'run') + '"';
     // TODO: PLATFORM LIBRARY INCONSISTENCY 
     if (platform == 'blackberry') {
         cmd = 'ant -f "' + path.join(root, 'platforms', platform, 'build.xml') + '" qnx load-simulator';
-    } else if (platform.indexOf('android') > -1) {
-        cmd = '"' + path.join(root, 'platforms', platform, 'cordova', 'run') + '"';
     }
     shell.exec(cmd, {silent:true, async:true}, function(code, output) {
         if (code > 0) {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/src/metadata/wp7_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/wp7_parser.js b/src/metadata/wp7_parser.js
new file mode 100644
index 0000000..289eca0
--- /dev/null
+++ b/src/metadata/wp7_parser.js
@@ -0,0 +1,160 @@
+
+/**
+    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'),
+    shell         = require('shelljs'),
+    config_parser = require('../config_parser');
+
+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 Error('The provided path "' + project + '" is not a Windows Phone 8 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 Error('The provided path "' + project + '" is not a Windows Phone 8 project.' + e);
+    }
+    this.manifest_path  = path.join(this.wp7_proj_dir, 'Properties', 'WMAppManifest.xml');
+};
+
+module.exports.check_requirements = function(callback) {
+    shell.exec(path.join(util.libDirectory, 'cordova-wp7', 'bin', 'check_reqs'), {silent:true, async:true}, function(code, output) {
+        if (code != 0) {
+            callback(output);
+        } else {
+                callback(false);
+        }
+    });
+};
+
+module.exports.prototype = {
+    update_from_config:function(config) {
+        //check config parser
+        if (config instanceof config_parser) {
+        } else throw new Error('update_from_config requires a config_parser object');
+
+        // Update app name by editing app title in Properties\WMAppManifest.xml
+        var name = config.name();
+        var man = fs.readFileSync(this.manifest_path, 'utf-8');
+        //Strip three bytes that windows adds (http://www.multiasking.com/2012/11/851/)
+        var cleanedMan = man.replace('\ufeff', '');
+        var manifest = new et.ElementTree(et.XML(cleanedMan));
+        var prev_name = manifest.find('.//App[@Title]')['attrib']['Title'];
+        if(prev_name != name)
+        {
+            //console.log("Updating app name from " + prev_name + " to " + name);
+            manifest.find('.//App').attrib.Title = name;
+            manifest.find('.//Title').text = name;
+            fs.writeFileSync(this.manifest_path, manifest.write({indent: 4}), 'utf-8');
+
+            //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, '_'); 
+            // TODO: might return .sln.user? (generated file)
+            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 raw = fs.readFileSync(this.csproj_path, 'utf-8');
+         var cleanedPage = raw.replace(/^\uFEFF/i, '');
+         var csproj = new et.ElementTree(et.XML(cleanedPage));
+         prev_name = csproj.find('.//RootNamespace').text;
+         if(prev_name != pkg)
+         {
+            //console.log("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
+            raw = fs.readFileSync(path.join(this.wp7_proj_dir, 'MainPage.xaml'), 'utf-8');
+            // Remove potential UTF Byte Order Mark
+            cleanedPage = raw.replace(/^\uFEFF/i, '');
+            var mainPageXAML = new et.ElementTree(et.XML(cleanedPage));
+            mainPageXAML._root.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
+            raw = fs.readFileSync(path.join(this.wp7_proj_dir, 'App.xaml'), 'utf-8');
+            cleanedPage = raw.replace(/^\uFEFF/i, '');
+            var appXAML = new et.ElementTree(et.XML(cleanedPage));
+            appXAML._root.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');
+         }
+    },
+    // Returns the platform-specific www directory.
+    www_dir:function() {
+        return path.join(this.wp7_proj_dir, 'www');
+    },
+    // copyies the app www folder into the wp7 project's www folder
+    update_www:function() {
+        var project_www = path.join(this.wp7_proj_dir, '..', '..', util.projectWww());
+        // remove stock platform assets
+        shell.rm('-rf', this.www_dir());
+        // copy over all app www assets
+        shell.cp('-rf', project_www, this.wp7_proj_dir);
+
+        // copy over wp7 lib's cordova.js
+        var raw_version = fs.readFileSync(path.join(util.libDirectory, 'cordova-wp7', 'VERSION'), 'utf-8')
+        var VERSION = raw_version.replace(/\r\n/,'').replace(/\n/,'');
+        var cordovajs_path = path.join(util.libDirectory, 'cordova-wp7', 'templates', 'standalone', 'www', 'cordova-' + VERSION + '.js');
+        fs.writeFileSync(path.join(this.www_dir(), 'cordova.js'), fs.readFileSync(cordovajs_path, 'utf-8'), 'utf-8');
+    },
+    // calls the nessesary functions to update the wp7 project 
+    update_project:function(cfg, callback) {
+        //console.log("Updating wp7 project...");
+
+        this.update_from_config(cfg);
+        this.update_www();
+        util.deleteSvnFolders(this.www_dir());
+
+        //console.log("Done updating.");
+
+        if (callback) callback();
+    }
+};
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/src/metadata/wp8_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/wp8_parser.js b/src/metadata/wp8_parser.js
new file mode 100644
index 0000000..5ecb6dc
--- /dev/null
+++ b/src/metadata/wp8_parser.js
@@ -0,0 +1,158 @@
+
+/**
+    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'),
+    shell         = require('shelljs'),
+    config_parser = require('../config_parser');
+
+module.exports = function wp8_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 Error('The provided path "' + project + '" is not a Windows Phone 8 project.');
+        this.wp8_proj_dir = project;
+        this.csproj_path  = path.join(this.wp8_proj_dir, csproj_file);
+        this.sln_path     = path.join(this.wp8_proj_dir, csproj_file.replace(/\.csproj/, '.sln'));
+    } catch(e) {
+        throw new Error('The provided path "' + project + '" is not a Windows Phone 8 project.' + e);
+    }
+    this.manifest_path  = path.join(this.wp8_proj_dir, 'Properties', 'WMAppManifest.xml');
+};
+
+module.exports.check_requirements = function(callback) {
+    shell.exec(path.join(util.libDirectory, 'cordova-wp8', 'bin', 'check_reqs'), {silent:true, async:true}, function(code, output) {
+        if (code != 0) {
+            callback(output);
+        } else {
+                callback(false);
+        }
+    });
+};
+
+module.exports.prototype = {
+    update_from_config:function(config) {
+        //check config parser
+        if (config instanceof config_parser) {
+        } else throw new Error('update_from_config requires a config_parser object');
+
+        // Update app name by editing app title in Properties\WMAppManifest.xml
+        var name = config.name();
+        var man = fs.readFileSync(this.manifest_path, 'utf-8');
+        //Strip three bytes that windows adds (http://www.multiasking.com/2012/11/851/)
+        var cleanedMan = man.replace('\ufeff', '');
+        var manifest = new et.ElementTree(et.XML(cleanedMan));
+        var prev_name = manifest.find('.//App[@Title]')['attrib']['Title'];
+        if(prev_name != name)
+        {
+            //console.log("Updating app name from " + prev_name + " to " + name);
+            manifest.find('.//App').attrib.Title = name;
+            manifest.find('.//Title').text = name;
+            fs.writeFileSync(this.manifest_path, manifest.write({indent: 4}), 'utf-8');
+
+            //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_path = path.join(this.wp8_proj_dir, prev_name + '.sln');
+            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.wp8_proj_dir, name + '.csproj'));
+            this.csproj_path = path.join(this.wp8_proj_dir, name + '.csproj');
+            shell.mv('-f', sln_path, path.join(this.wp8_proj_dir, name + '.sln'));
+            this.sln_path    = path.join(this.wp8_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 raw = fs.readFileSync(this.csproj_path, 'utf-8');
+         var cleanedPage = raw.replace(/^\uFEFF/i, '');
+         var csproj = new et.ElementTree(et.XML(cleanedPage));
+         prev_name = csproj.find('.//RootNamespace').text;
+         if(prev_name != pkg)
+         {
+            //console.log("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
+            raw = fs.readFileSync(path.join(this.wp8_proj_dir, 'MainPage.xaml'), 'utf-8');
+            // Remove potential UTF Byte Order Mark
+            cleanedPage = raw.replace(/^\uFEFF/i, '');
+            var mainPageXAML = new et.ElementTree(et.XML(cleanedPage));
+            mainPageXAML._root.attrib['x:Class'] = pkg + '.MainPage';
+            fs.writeFileSync(path.join(this.wp8_proj_dir, 'MainPage.xaml'), mainPageXAML.write({indent: 4}), 'utf-8');
+            //MainPage.xaml.cs
+            var mainPageCS = fs.readFileSync(path.join(this.wp8_proj_dir, 'MainPage.xaml.cs'), 'utf-8');
+            var namespaceRegEx = new RegExp('namespace ' + prev_name);
+            fs.writeFileSync(path.join(this.wp8_proj_dir, 'MainPage.xaml.cs'), mainPageCS.replace(namespaceRegEx, 'namespace ' + pkg), 'utf-8');
+            //App.xaml
+            raw = fs.readFileSync(path.join(this.wp8_proj_dir, 'App.xaml'), 'utf-8');
+            cleanedPage = raw.replace(/^\uFEFF/i, '');
+            var appXAML = new et.ElementTree(et.XML(cleanedPage));
+            appXAML._root.attrib['x:Class'] = pkg + '.App';
+            fs.writeFileSync(path.join(this.wp8_proj_dir, 'App.xaml'), appXAML.write({indent: 4}), 'utf-8');
+            //App.xaml.cs
+            var appCS = fs.readFileSync(path.join(this.wp8_proj_dir, 'App.xaml.cs'), 'utf-8');
+            fs.writeFileSync(path.join(this.wp8_proj_dir, 'App.xaml.cs'), appCS.replace(namespaceRegEx, 'namespace ' + pkg), 'utf-8');
+         }
+    },
+    // Returns the platform-specific www directory.
+    www_dir:function() {
+        return path.join(this.wp8_proj_dir, 'www');
+    },
+    // copyies the app www folder into the wp8 project's www folder
+    update_www:function() {
+        var project_www = path.join(this.wp8_proj_dir, '..', '..', util.projectWww());
+        // remove stock platform assets
+        shell.rm('-rf', this.www_dir());
+        // copy over all app www assets
+        shell.cp('-rf', project_www, this.wp8_proj_dir);
+
+        // copy over wp8 lib's cordova.js
+        var raw_version = fs.readFileSync(path.join(util.libDirectory, 'cordova-wp8', 'VERSION'), 'utf-8')
+        var VERSION = raw_version.replace(/\r\n/,'').replace(/\n/,'');
+        var cordovajs_path = path.join(util.libDirectory, 'cordova-wp8', 'templates', 'standalone', 'www', 'cordova-' + VERSION + '.js');
+        fs.writeFileSync(path.join(this.www_dir(), 'cordova.js'), fs.readFileSync(cordovajs_path, 'utf-8'), 'utf-8');
+    },
+    // calls the nessesary functions to update the wp8 project 
+    update_project:function(cfg, callback) {
+        //console.log("Updating wp8 project...");
+
+        this.update_from_config(cfg);
+        this.update_www();
+        util.deleteSvnFolders(this.www_dir());
+
+        //console.log("Done updating.");
+
+        if (callback) callback();
+    }
+};
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/src/platform.js
----------------------------------------------------------------------
diff --git a/src/platform.js b/src/platform.js
index 00f2157..15285fb 100644
--- a/src/platform.js
+++ b/src/platform.js
@@ -26,12 +26,16 @@ var config_parser     = require('./config_parser'),
     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'),
     shell             = require('shelljs');
 
 var parsers = {
     "android":android_parser,
     "ios":ios_parser,
-    "blackberry":blackberry_parser
+    "blackberry":blackberry_parser,
+    "wp7":wp7_parser,
+    "wp8":wp8_parser
 };
 
 module.exports = function platform(command, targets, callback) {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/src/plugin.js
----------------------------------------------------------------------
diff --git a/src/plugin.js b/src/plugin.js
index 8997f97..b6d9911 100644
--- a/src/plugin.js
+++ b/src/plugin.js
@@ -108,6 +108,7 @@ module.exports = function plugin(command, targets, callback) {
                 intersection.forEach(function(platform) {
                     var cmd = util.format('%s --platform %s --project "%s" --plugin "%s"', cli, platform, path.join(projectRoot, 'platforms', platform), target);
                     var plugin_cli = shell.exec(cmd, {silent:true});
+                    if(!plugin_cli) throw new Error('Plugman command failed to execute for ' + platform + '.');
                     if (plugin_cli.code > 0) throw new Error('An error occured during plugin installation for ' + platform + '. ' + plugin_cli.output);
                 });
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/src/plugin_loader.js
----------------------------------------------------------------------
diff --git a/src/plugin_loader.js b/src/plugin_loader.js
index 3798e2f..b4730b4 100644
--- a/src/plugin_loader.js
+++ b/src/plugin_loader.js
@@ -25,6 +25,8 @@ var path            = require('path'),
     android_parser  = require('./metadata/android_parser'),
     blackberry_parser= require('./metadata/blackberry_parser'),
     ios_parser      = require('./metadata/ios_parser'),
+    wp7_parser      = require('./metadata/wp7_parser'),
+    wp8_parser      = require('./metadata/wp8_parser'),
     exec            = require('child_process').exec,
     et              = require('elementtree');
 
@@ -88,6 +90,13 @@ module.exports = function plugin_loader(platform) {
         case 'blackberry':
             parser = new blackberry_parser(path.join(projectRoot, 'platforms', 'blackberry'));
             break;
+        case 'wp7':
+            parser = new wp7_parser(path.join(projectRoot, 'platforms', 'wp7'));
+            break;
+        case 'wp8':
+            parser = new wp8_parser(path.join(projectRoot, 'platforms', 'wp8'));
+            break;
+
     }
 
     plugins && plugins.forEach(function(plugin) {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fd19030/src/util.js
----------------------------------------------------------------------
diff --git a/src/util.js b/src/util.js
index 7ebbb17..cd34533 100644
--- a/src/util.js
+++ b/src/util.js
@@ -76,5 +76,11 @@ module.exports = {
         }
 
         return plugins;
+    },
+    projectWww: function(projectDir) {
+        return path.join(projectDir, 'www');
+    },
+    projectConfig: function(projectDir) {
+        return path.join(projectDir, 'www', 'config.xml');
     }
 };


[10/50] Add WP7 and WP8 platform files.

Posted by br...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/camera/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/camera/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/camera/index.html
new file mode 100644
index 0000000..bc3b554
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/camera/index.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    //-------------------------------------------------------------------------
+    // Camera 
+    //-------------------------------------------------------------------------
+
+    /**
+     * Capture picture
+     */
+    function getPicture() {
+        
+        //navigator.camera.getPicture(onPhotoDataSuccess, onFail, { quality: 50, 
+        //    destinationType: Camera.DestinationType.FILE_URI, sourceType : Camera.PictureSourceType.CAMERA });
+        
+        navigator.camera.getPicture(
+            function(data) {
+                var img = document.getElementById('camera_image');
+                img.style.visibility = "visible";
+                img.style.display = "block";
+                //img.src = "data:image/jpeg;base64," + data;
+                img.src = data;
+                document.getElementById('camera_status').innerHTML = "Success";
+            },
+            function(e) {
+                console.log("Error getting picture: " + e);
+                document.getElementById('camera_status').innerHTML = "Error getting picture.";
+            },
+            { quality: 50, destinationType:
+            Camera.DestinationType.FILE_URI, sourceType : Camera.PictureSourceType.CAMERA});
+    };
+
+    /**
+     * Select image from library
+     */
+    function getImage() {
+        navigator.camera.getPicture(
+            function(data) {
+                var img = document.getElementById('camera_image');
+                img.style.visibility = "visible";
+                img.style.display = "block";
+                //img.src = "data:image/jpeg;base64," + data;
+                img.src = data;
+                document.getElementById('camera_status').innerHTML = "Success";
+            },
+            function(e) {
+                console.log("Error getting picture: " + e);
+                document.getElementById('camera_status').innerHTML = "Error getting picture.";
+            },
+            { quality: 50, destinationType:
+            Camera.DestinationType.FILE_URI, sourceType: Camera.PictureSourceType.PHOTOLIBRARY});
+    };
+
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Camera</h1>
+    <div id="info">
+        <b>Status:</b> <span id="camera_status"></span><br>
+        <img style="width:120px;height:120px;visibility:hidden;display:none;" id="camera_image" src="" />
+    </div>
+    <h2>Action</h2>
+    <div class="btn large" onclick="getPicture();">Take Picture</div>
+    <div class="btn large" onclick="getImage();">Select Image from Library</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/compass/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/compass/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/compass/index.html
new file mode 100644
index 0000000..8dbf99a
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/compass/index.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    function roundNumber(num) {
+        var dec = 3;
+        var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
+        return result;
+    }
+
+    //-------------------------------------------------------------------------
+    // Compass
+    //-------------------------------------------------------------------------
+    var watchCompassId = null;
+
+    /**
+     * Start watching compass
+     */
+    var watchCompass = function() {
+        console.log("watchCompass()");
+
+        // Success callback
+        var success = function(a){
+            document.getElementById('compassHeading').innerHTML = roundNumber(a.magneticHeading);
+        };
+
+        // Fail callback
+        var fail = function(e){
+            console.log("watchCompass fail callback with error code "+e);
+            stopCompass();
+            setCompassStatus(e);
+        };
+
+        // Update heading every 1 sec
+        var opt = {};
+        opt.frequency = 1000;
+        watchCompassId = navigator.compass.watchHeading(success, fail, opt);
+
+        setCompassStatus("Running");
+    };
+
+    /**
+     * Stop watching the acceleration
+     */
+    var stopCompass = function() {
+        setCompassStatus("Stopped");
+        if (watchCompassId) {
+            navigator.compass.clearWatch(watchCompassId);
+            watchCompassId = null;
+        }
+    };
+
+    /**
+     * Get current compass
+     */
+    var getCompass = function() {
+        console.log("getCompass()");
+
+        // Stop compass if running
+        stopCompass();
+
+        // Success callback
+        var success = function(a){
+            document.getElementById('compassHeading').innerHTML = roundNumber(a.magneticHeading);
+        };
+
+        // Fail callback
+        var fail = function(e){
+            console.log("getCompass fail callback with error code "+e);
+            setCompassStatus(e);
+        };
+
+        // Make call
+        var opt = {};
+        navigator.compass.getCurrentHeading(success, fail, opt);
+    };
+
+    /**
+     * Set compass status
+     */
+    var setCompassStatus = function(status) {
+        document.getElementById('compass_status').innerHTML = status;
+    };
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Compass</h1>
+    <div id="info">
+        <b>Status:</b> <span id="compass_status">Stopped</span>
+        <table width="100%"><tr>
+            <td width="33%">Heading: <span id="compassHeading"> </span></td>
+        </tr></table>
+    </div>
+    <h2>Action</h2>
+    <div class="btn large" onclick="getCompass();">Get Compass</div>
+    <div class="btn large" onclick="watchCompass();">Start Watching Compass</div>
+    <div class="btn large" onclick="stopCompass();">Stop Watching Compass</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/0fea2dd2/lib/cordova-wp8/tests/MobileSpecUnitTests/www/contacts/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/tests/MobileSpecUnitTests/www/contacts/index.html b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/contacts/index.html
new file mode 100644
index 0000000..950e1cc
--- /dev/null
+++ b/lib/cordova-wp8/tests/MobileSpecUnitTests/www/contacts/index.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    //-------------------------------------------------------------------------
+    // Contacts
+    //-------------------------------------------------------------------------
+    function getContacts() {
+        obj = new ContactFindOptions();
+        obj.filter = "D"; //Brooks";
+        obj.multiple = true;
+        navigator.contacts.find(
+            ["displayName", "name", "phoneNumbers", "emails", "urls", "note"],
+            function(contacts) {
+                var s = "";
+                if (contacts.length == 0) {
+                    s = "No contacts found";
+                }
+                else {
+                    s = "Number of contacts: "+contacts.length+"<br><table width='100%'><tr><th>Name</th><td>Phone</td><td>Email</td></tr>";
+                    for (var i=0; i<contacts.length; i++) {
+                        var contact = contacts[i];
+                        s = s + "<tr><td>" + contact.name.formatted + "</td><td>";
+                        if (contact.phoneNumbers && contact.phoneNumbers.length > 0) {
+                            s = s + contact.phoneNumbers[0].value;
+                        }
+                        s = s + "</td><td>"
+                        if (contact.emails && contact.emails.length > 0) {
+                            s = s + contact.emails[0].value;
+                        }
+                        s = s + "</td></tr>";
+                    }
+                    s = s + "</table>";
+                }
+                document.getElementById('contacts_results').innerHTML = s;
+            },
+            function(e) {
+                document.getElementById('contacts_results').innerHTML = "Error: "+e.code;
+            },
+            obj);
+    };
+
+    function addContact(){
+        console.log("addContact()");
+        try{
+            var contact = navigator.contacts.create({"displayName": "Dooney Evans"});
+            var contactName = {
+                formatted: "Dooney Evans",
+                familyName: "Evans",
+                givenName: "Dooney",
+                middleName: ""
+            };
+
+            contact.name = contactName;
+
+            var phoneNumbers = [1];
+            phoneNumbers[0] = new ContactField('work', '512-555-1234', true);
+            contact.phoneNumbers = phoneNumbers;
+
+            contact.save(
+                function() { alert("Contact saved.");},
+                function(e) { alert("Contact save failed: " + e.code); }
+            );
+            console.log("you have saved the contact");
+        }
+        catch (e){
+            alert(e);
+        }
+
+    };
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Contacts</h1>    
+    <div id="info">
+        <b>Results:</b><br>
+        <span id="contacts_results"> </span>
+    </div>
+    <h2>Action</h2>
+    <div class="btn large" onclick="getContacts();">Get phone's contacts</div>
+    <div class="btn large" onclick="addContact();">Add a new contact 'Dooney Evans'</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>