Browse Source

cordova start

Johan LE BAUT 5 years ago
parent
commit
232f7365ee
89 changed files with 8566 additions and 118 deletions
  1. 26 0
      config.xml
  2. 399 115
      package-lock.json
  3. 16 0
      package.json
  4. 23 0
      platforms/browser/browser.json
  5. 18 0
      platforms/browser/config.xml
  6. 531 0
      platforms/browser/cordova/Api.js
  7. 135 0
      platforms/browser/cordova/browser_handler.js
  8. 120 0
      platforms/browser/cordova/browser_parser.js
  9. 34 0
      platforms/browser/cordova/build
  10. 26 0
      platforms/browser/cordova/build.bat
  11. 36 0
      platforms/browser/cordova/clean
  12. 26 0
      platforms/browser/cordova/clean.bat
  13. 22 0
      platforms/browser/cordova/defaults.xml
  14. 37 0
      platforms/browser/cordova/lib/build.js
  15. 27 0
      platforms/browser/cordova/lib/check_reqs.js
  16. 51 0
      platforms/browser/cordova/lib/clean.js
  17. 68 0
      platforms/browser/cordova/lib/run.js
  18. 20 0
      platforms/browser/cordova/log
  19. 53 0
      platforms/browser/cordova/run
  20. 26 0
      platforms/browser/cordova/run.bat
  21. 25 0
      platforms/browser/cordova/version
  22. 26 0
      platforms/browser/cordova/version.bat
  23. 90 0
      platforms/browser/platform_www/cordova-js-src/confighelper.js
  24. 114 0
      platforms/browser/platform_www/cordova-js-src/exec.js
  25. 46 0
      platforms/browser/platform_www/cordova-js-src/platform.js
  26. 1594 0
      platforms/browser/platform_www/cordova.js
  27. 10 0
      platforms/browser/platform_www/cordova_plugins.js
  28. BIN
      platforms/browser/platform_www/favicon.ico
  29. 24 0
      platforms/browser/platform_www/manifest.json
  30. 18 0
      platforms/browser/www/config.xml
  31. 90 0
      platforms/browser/www/cordova-js-src/confighelper.js
  32. 114 0
      platforms/browser/www/cordova-js-src/exec.js
  33. 46 0
      platforms/browser/www/cordova-js-src/platform.js
  34. 1594 0
      platforms/browser/www/cordova.js
  35. 10 0
      platforms/browser/www/cordova_plugins.js
  36. 107 0
      platforms/browser/www/css/index.css
  37. BIN
      platforms/browser/www/favicon.ico
  38. BIN
      platforms/browser/www/img/logo.png
  39. 49 0
      platforms/browser/www/index.html
  40. 46 0
      platforms/browser/www/js/index.js
  41. 11 0
      platforms/browser/www/manifest.json
  42. 18 0
      plugins/browser.json
  43. 3 0
      plugins/cordova-plugin-webpack/.eslintignore
  44. 35 0
      plugins/cordova-plugin-webpack/.eslintrc.js
  45. 8 0
      plugins/cordova-plugin-webpack/.vscode/settings.json
  46. 76 0
      plugins/cordova-plugin-webpack/CODE_OF_CONDUCT.md
  47. 7 0
      plugins/cordova-plugin-webpack/CONTRIBUTING.md
  48. 201 0
      plugins/cordova-plugin-webpack/LICENSE
  49. 241 0
      plugins/cordova-plugin-webpack/README.md
  50. 1 0
      plugins/cordova-plugin-webpack/commitlint.config.js
  51. 4 0
      plugins/cordova-plugin-webpack/jest.config.js
  52. BIN
      plugins/cordova-plugin-webpack/media/cordova-plugin-webpack-demo.gif
  53. 146 0
      plugins/cordova-plugin-webpack/package.json
  54. 10 0
      plugins/cordova-plugin-webpack/plugin.xml
  55. 5 0
      plugins/cordova-plugin-webpack/renovate.json
  56. 52 0
      plugins/cordova-plugin-webpack/scripts/utils/CordovaConfigParser.js
  57. 1 0
      plugins/cordova-plugin-webpack/scripts/utils/CordovaConfigParser.js.map
  58. 33 0
      plugins/cordova-plugin-webpack/scripts/utils/webpackHelpers.js
  59. 1 0
      plugins/cordova-plugin-webpack/scripts/utils/webpackHelpers.js.map
  60. 89 0
      plugins/cordova-plugin-webpack/scripts/webpackCompile.js
  61. 1 0
      plugins/cordova-plugin-webpack/scripts/webpackCompile.js.map
  62. 171 0
      plugins/cordova-plugin-webpack/scripts/webpackServe.js
  63. 0 0
      plugins/cordova-plugin-webpack/scripts/webpackServe.js.map
  64. 96 0
      plugins/cordova-plugin-webpack/scripts/www/injectCSP.js
  65. 96 0
      plugins/cordova-plugin-webpack/scripts/www/injectCordovaScript.js
  66. 30 0
      plugins/cordova-plugin-webpack/tsconfig.json
  67. 31 0
      plugins/cordova-plugin-webpack/webpack.config.ts
  68. 37 0
      plugins/cordova-plugin-whitelist/CONTRIBUTING.md
  69. 202 0
      plugins/cordova-plugin-whitelist/LICENSE
  70. 5 0
      plugins/cordova-plugin-whitelist/NOTICE
  71. 167 0
      plugins/cordova-plugin-whitelist/README.md
  72. 87 0
      plugins/cordova-plugin-whitelist/RELEASENOTES.md
  73. 67 0
      plugins/cordova-plugin-whitelist/package.json
  74. 44 0
      plugins/cordova-plugin-whitelist/plugin.xml
  75. 161 0
      plugins/cordova-plugin-whitelist/src/android/WhitelistPlugin.java
  76. 53 0
      plugins/cordova-plugin-whitelist/tests/README.md
  77. 15 0
      plugins/cordova-plugin-whitelist/tests/package.json
  78. 56 0
      plugins/cordova-plugin-whitelist/tests/plugin.xml
  79. 19 0
      plugins/cordova-plugin-whitelist/tests/scripts/remove-access.js
  80. 90 0
      plugins/cordova-plugin-whitelist/tests/src/android/WhitelistAPI.java
  81. 214 0
      plugins/cordova-plugin-whitelist/tests/tests.js
  82. 34 0
      plugins/cordova-plugin-whitelist/tests/www/whitelist.js
  83. 18 0
      plugins/fetch.json
  84. 29 1
      src/main.js
  85. 2 2
      webpack.config.dev.js
  86. 107 0
      www/css/index.css
  87. BIN
      www/img/logo.png
  88. 49 0
      www/index.html
  89. 46 0
      www/js/index.js

+ 26 - 0
config.xml

@@ -0,0 +1,26 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="com.jojo.twelve-heroes" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>twelve-heroes</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <plugin name="cordova-plugin-whitelist" spec="1" />
+    <access origin="*" />
+    <allow-intent href="http://*/*" />
+    <allow-intent href="https://*/*" />
+    <allow-intent href="tel:*" />
+    <allow-intent href="sms:*" />
+    <allow-intent href="mailto:*" />
+    <allow-intent href="geo:*" />
+    <platform name="android">
+        <allow-intent href="market:*" />
+    </platform>
+    <platform name="ios">
+        <allow-intent href="itms:*" />
+        <allow-intent href="itms-apps:*" />
+    </platform>
+</widget>

File diff suppressed because it is too large
+ 399 - 115
package-lock.json


+ 16 - 0
package.json

@@ -2,6 +2,9 @@
   "name": "javascript-development-environment",
   "version": "1.0.0",
   "description": "JavaScript development environment Pluralsight course by Cory House",
+  "keywords": [
+    "ecosystem:cordova"
+  ],
   "scripts": {
     "prestart": "babel-node buildScripts/startMessage.js",
     "start": "npm-run-all --parallel open:src lint:watch test:watch",
@@ -26,17 +29,21 @@
   "dependencies": {
     "@babel/node": "7.6.2",
     "@babel/polyfill": "7.6.0",
+    "cordova-browser": "^6.0.0",
+    "cordova-plugin-webpack": "^0.4.7",
     "phaser": "3.19.0"
   },
   "devDependencies": {
     "@babel/cli": "7.6.2",
     "@babel/core": "7.6.2",
+    "@babel/plugin-proposal-object-rest-spread": "^7.6.2",
     "@babel/preset-env": "7.6.2",
     "@babel/register": "7.6.2",
     "babel-loader": "8.0.6",
     "chai": "4.2.0",
     "chalk": "2.4.2",
     "clean-webpack-plugin": "3.0.0",
+    "cordova-plugin-whitelist": "^1.3.4",
     "eslint": "6.4.0",
     "eslint-plugin-import": "2.18.2",
     "eslint-watch": "6.0.1",
@@ -52,5 +59,14 @@
     "webpack-cli": "3.3.9",
     "webpack-dev-middleware": "3.7.2",
     "webpack-md5-hash": "0.0.6"
+  },
+  "cordova": {
+    "plugins": {
+      "cordova-plugin-whitelist": {},
+      "cordova-plugin-webpack": {}
+    },
+    "platforms": [
+      "browser"
+    ]
   }
 }

+ 23 - 0
platforms/browser/browser.json

@@ -0,0 +1,23 @@
+{
+  "prepare_queue": {
+    "installed": [],
+    "uninstalled": []
+  },
+  "config_munge": {
+    "files": {}
+  },
+  "installed_plugins": {
+    "cordova-plugin-whitelist": {
+      "PACKAGE_NAME": "com.jojo.twelve-heroes"
+    },
+    "cordova-plugin-webpack": {
+      "PACKAGE_NAME": "com.jojo.twelve-heroes"
+    }
+  },
+  "dependent_plugins": {},
+  "modules": [],
+  "plugin_metadata": {
+    "cordova-plugin-whitelist": "1.3.4",
+    "cordova-plugin-webpack": "0.4.7"
+  }
+}

+ 18 - 0
platforms/browser/config.xml

@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="com.jojo.twelve-heroes" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>twelve-heroes</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <allow-intent href="http://*/*" />
+    <allow-intent href="https://*/*" />
+    <allow-intent href="tel:*" />
+    <allow-intent href="sms:*" />
+    <allow-intent href="mailto:*" />
+    <allow-intent href="geo:*" />
+</widget>

+ 531 - 0
platforms/browser/cordova/Api.js

@@ -0,0 +1,531 @@
+/**
+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 file is found by cordova-lib when you attempt to
+    'cordova platform add PATH' where path is this repo.
+*/
+
+var shell = require('shelljs');
+var path = require('path');
+var fs = require('fs');
+
+var cdvcmn = require('cordova-common');
+var CordovaLogger = cdvcmn.CordovaLogger;
+var ConfigParser = cdvcmn.ConfigParser;
+var ActionStack = cdvcmn.ActionStack;
+var selfEvents = cdvcmn.events;
+var xmlHelpers = cdvcmn.xmlHelpers;
+var PlatformJson = cdvcmn.PlatformJson;
+var PlatformMunger = cdvcmn.ConfigChanges.PlatformMunger;
+var PluginInfoProvider = cdvcmn.PluginInfoProvider;
+
+var BrowserParser = require('./browser_parser');
+var PLATFORM_NAME = 'browser';
+
+function setupEvents (externalEventEmitter) {
+    if (externalEventEmitter) {
+        // This will make the platform internal events visible outside
+        selfEvents.forwardEventsTo(externalEventEmitter);
+        return externalEventEmitter;
+    }
+
+    // There is no logger if external emitter is not present,
+    // so attach a console logger
+    CordovaLogger.get().subscribe(selfEvents);
+    return selfEvents;
+}
+
+function Api (platform, platformRootDir, events) {
+
+    this.platform = platform || PLATFORM_NAME;
+
+    // MyApp/platforms/browser
+    this.root = path.resolve(__dirname, '..');
+    this.events = setupEvents(events);
+    this.parser = new BrowserParser(this.root);
+    this._handler = require('./browser_handler');
+
+    this.locations = {
+        platformRootDir: platformRootDir,
+        root: this.root,
+        www: path.join(this.root, 'www'),
+        res: path.join(this.root, 'res'),
+        platformWww: path.join(this.root, 'platform_www'),
+        configXml: path.join(this.root, 'config.xml'),
+        defaultConfigXml: path.join(this.root, 'cordova/defaults.xml'),
+        build: path.join(this.root, 'build'),
+        // NOTE: Due to platformApi spec we need to return relative paths here
+        cordovaJs: 'bin/templates/project/assets/www/cordova.js',
+        cordovaJsSrc: 'cordova-js-src'
+    };
+
+    this._platformJson = PlatformJson.load(this.root, platform);
+    this._pluginInfoProvider = new PluginInfoProvider();
+    this._munger = new PlatformMunger(platform, this.root, this._platformJson, this._pluginInfoProvider);
+}
+
+Api.createPlatform = function (dest, config, options, events) {
+
+    var creator = require('../../lib/create');
+    events = setupEvents(events);
+
+    var name = 'HelloCordova';
+    var id = 'io.cordova.hellocordova';
+    if (config) {
+        name = config.name();
+        id = config.packageName();
+    }
+
+    var result;
+    try {
+        // we create the project using our scripts in this platform
+        result = creator.createProject(dest, id, name, options)
+            .then(function () {
+                // after platform is created we return Api instance based on new Api.js location
+                // Api.js has been copied to the new project
+                // This is required to correctly resolve paths in the future api calls
+                var PlatformApi = require(path.resolve(dest, 'cordova/Api'));
+                return new PlatformApi('browser', dest, events);
+            });
+    } catch (e) {
+        events.emit('error', 'createPlatform is not callable from the browser project API.');
+        throw (e);
+    }
+    return result;
+};
+
+Api.updatePlatform = function (dest, options, events) {
+    // console.log("test-platform:Api:updatePlatform");
+    // todo?: create projectInstance and fulfill promise with it.
+    return Promise.resolve();
+};
+
+Api.prototype.getPlatformInfo = function () {
+    // console.log("browser-platform:Api:getPlatformInfo");
+    // return PlatformInfo object
+    return {
+        'locations': this.locations,
+        'root': this.root,
+        'name': this.platform,
+        'version': { 'version': '1.0.0' }, // um, todo!
+        'projectConfig': this.config
+    };
+};
+
+Api.prototype.prepare = function (cordovaProject, options) {
+
+    // First cleanup current config and merge project's one into own
+    var defaultConfigPath = path.join(this.locations.platformRootDir, 'cordova',
+        'defaults.xml');
+    var ownConfigPath = this.locations.configXml;
+    var sourceCfg = cordovaProject.projectConfig;
+
+    // If defaults.xml is present, overwrite platform config.xml with it.
+    // Otherwise save whatever is there as defaults so it can be
+    // restored or copy project config into platform if none exists.
+    if (fs.existsSync(defaultConfigPath)) {
+        this.events.emit('verbose', 'Generating config.xml from defaults for platform "' + this.platform + '"');
+        shell.cp('-f', defaultConfigPath, ownConfigPath);
+    } else if (fs.existsSync(ownConfigPath)) {
+        this.events.emit('verbose', 'Generating defaults.xml from own config.xml for platform "' + this.platform + '"');
+        shell.cp('-f', ownConfigPath, defaultConfigPath);
+    } else {
+        this.events.emit('verbose', 'case 3"' + this.platform + '"');
+        shell.cp('-f', sourceCfg.path, ownConfigPath);
+    }
+
+    // merge our configs
+    this.config = new ConfigParser(ownConfigPath);
+    xmlHelpers.mergeXml(cordovaProject.projectConfig.doc.getroot(),
+        this.config.doc.getroot(),
+        this.platform, true);
+    this.config.write();
+
+    // Update own www dir with project's www assets and plugins' assets and js-files
+    this.parser.update_www(cordovaProject, options);
+
+    // Copy or Create manifest.json
+    // todo: move this to a manifest helper module
+    // output path
+    var manifestPath = path.join(this.locations.www, 'manifest.json');
+    var srcManifestPath = path.join(cordovaProject.locations.www, 'manifest.json');
+    if (fs.existsSync(srcManifestPath)) {
+        // just blindly copy it to our output/www
+        // todo: validate it? ensure all properties we expect exist?
+        this.events.emit('verbose', 'copying ' + srcManifestPath + ' => ' + manifestPath);
+        shell.cp('-f', srcManifestPath, manifestPath);
+    } else {
+        var manifestJson = {
+            'background_color': '#FFF',
+            'display': 'standalone'
+        };
+        if (this.config) {
+            if (this.config.name()) {
+                manifestJson.name = this.config.name();
+            }
+            if (this.config.shortName()) {
+                manifestJson.short_name = this.config.shortName();
+            }
+            if (this.config.packageName()) {
+                manifestJson.version = this.config.packageName();
+            }
+            if (this.config.description()) {
+                manifestJson.description = this.config.description();
+            }
+            if (this.config.author()) {
+                manifestJson.author = this.config.author();
+            }
+            // icons
+            var icons = this.config.getStaticResources('browser', 'icon');
+            var manifestIcons = icons.map(function (icon) {
+                // given a tag like this :
+                // <icon src="res/ios/icon.png" width="57" height="57" density="mdpi" />
+                /* configParser returns icons that look like this :
+                {   src: 'res/ios/icon.png',
+                    target: undefined,
+                    density: 'mdpi',
+                    platform: null,
+                    width: 57,
+                    height: 57
+                } ******/
+                /* manifest expects them to be like this :
+                {   "src": "images/touch/icon-128x128.png",
+                    "type": "image/png",
+                    "sizes": "128x128"
+                } ******/
+                // ?Is it worth looking at file extentions?
+                return { 'src': icon.src,
+                    'type': 'image/png',
+                    'sizes': (icon.width + 'x' + icon.height) };
+            });
+            manifestJson.icons = manifestIcons;
+
+            // orientation
+            // <preference name="Orientation" value="landscape" />
+            var oriPref = this.config.getGlobalPreference('Orientation');
+            if (oriPref) {
+                // if it's a supported value, use it
+                if (['landscape', 'portrait'].indexOf(oriPref) > -1) {
+                    manifestJson.orientation = oriPref;
+                } else { // anything else maps to 'any'
+                    manifestJson.orientation = 'any';
+                }
+            }
+
+            // get start_url
+            var contentNode = this.config.doc.find('content') || { 'attrib': { 'src': 'index.html' } }; // sensible default
+            manifestJson.start_url = contentNode.attrib.src;
+
+            // now we get some values from start_url page ...
+            var startUrlPath = path.join(cordovaProject.locations.www, manifestJson.start_url);
+            if (fs.existsSync(startUrlPath)) {
+                var contents = fs.readFileSync(startUrlPath, 'utf-8');
+                // matches <meta name="theme-color" content="#FF0044">
+                var themeColorRegex = /<meta(?=[^>]*name="theme-color")\s[^>]*content="([^>]*)"/i;
+                var result = themeColorRegex.exec(contents);
+                var themeColor;
+                if (result && result.length >= 2) {
+                    themeColor = result[1];
+                } else { // see if there is a preference in config.xml
+                    // <preference name="StatusBarBackgroundColor" value="#000000" />
+                    themeColor = this.config.getGlobalPreference('StatusBarBackgroundColor');
+                }
+                if (themeColor) {
+                    manifestJson.theme_color = themeColor;
+                }
+            }
+        }
+        fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 2), 'utf8');
+    }
+
+    // update project according to config.xml changes.
+    return this.parser.update_project(this.config, options);
+};
+
+Api.prototype.addPlugin = function (pluginInfo, installOptions) {
+
+    // console.log(new Error().stack);
+    if (!pluginInfo) {
+        return Promise.reject(new Error('The parameter is incorrect. The first parameter ' +
+            'should be valid PluginInfo instance'));
+    }
+
+    installOptions = installOptions || {};
+    installOptions.variables = installOptions.variables || {};
+    // CB-10108 platformVersion option is required for proper plugin installation
+    installOptions.platformVersion = installOptions.platformVersion ||
+        this.getPlatformInfo().version;
+
+    var self = this;
+    var actions = new ActionStack();
+    var projectFile = this._handler.parseProjectFile && this._handler.parseProjectFile(this.root);
+
+    // gather all files needs to be handled during install
+    pluginInfo.getFilesAndFrameworks(this.platform)
+        .concat(pluginInfo.getAssets(this.platform))
+        .concat(pluginInfo.getJsModules(this.platform))
+        .forEach(function (item) {
+            actions.push(actions.createAction(
+                self._getInstaller(item.itemType),
+                [item, pluginInfo.dir, pluginInfo.id, installOptions, projectFile],
+                self._getUninstaller(item.itemType),
+                [item, pluginInfo.dir, pluginInfo.id, installOptions, projectFile]));
+        });
+
+    // run through the action stack
+    return actions.process(this.platform, this.root)
+        .then(function () {
+            if (projectFile) {
+                projectFile.write();
+            }
+
+            // Add PACKAGE_NAME variable into vars
+            if (!installOptions.variables.PACKAGE_NAME) {
+                installOptions.variables.PACKAGE_NAME = self._handler.package_name(self.root);
+            }
+
+            self._munger
+                // Ignore passed `is_top_level` option since platform itself doesn't know
+                // anything about managing dependencies - it's responsibility of caller.
+                .add_plugin_changes(pluginInfo, installOptions.variables, /* is_top_level= */true, /* should_increment= */true)
+                .save_all();
+
+            var targetDir = installOptions.usePlatformWww ?
+                self.getPlatformInfo().locations.platformWww :
+                self.getPlatformInfo().locations.www;
+
+            self._addModulesInfo(pluginInfo, targetDir);
+        });
+};
+
+Api.prototype.removePlugin = function (plugin, uninstallOptions) {
+    // console.log("NotImplemented :: browser-platform:Api:removePlugin ",plugin, uninstallOptions);
+
+    uninstallOptions = uninstallOptions || {};
+    // CB-10108 platformVersion option is required for proper plugin installation
+    uninstallOptions.platformVersion = uninstallOptions.platformVersion ||
+        this.getPlatformInfo().version;
+
+    var self = this;
+    var actions = new ActionStack();
+    var projectFile = this._handler.parseProjectFile && this._handler.parseProjectFile(this.root);
+
+    // queue up plugin files
+    plugin.getFilesAndFrameworks(this.platform)
+        .concat(plugin.getAssets(this.platform))
+        .concat(plugin.getJsModules(this.platform))
+        .forEach(function (item) {
+            actions.push(actions.createAction(
+                self._getUninstaller(item.itemType), [item, plugin.dir, plugin.id, uninstallOptions, projectFile],
+                self._getInstaller(item.itemType), [item, plugin.dir, plugin.id, uninstallOptions, projectFile]));
+        });
+
+    // run through the action stack
+    return actions.process(this.platform, this.root)
+        .then(function () {
+            if (projectFile) {
+                projectFile.write();
+            }
+
+            self._munger
+                // Ignore passed `is_top_level` option since platform itself doesn't know
+                // anything about managing dependencies - it's responsibility of caller.
+                .remove_plugin_changes(plugin, /* is_top_level= */true)
+                .save_all();
+
+            var targetDir = uninstallOptions.usePlatformWww ?
+                self.getPlatformInfo().locations.platformWww :
+                self.getPlatformInfo().locations.www;
+
+            self._removeModulesInfo(plugin, targetDir);
+            // Remove stale plugin directory
+            // TODO: this should be done by plugin files uninstaller
+            shell.rm('-rf', path.resolve(self.root, 'Plugins', plugin.id));
+        });
+};
+
+Api.prototype._getInstaller = function (type) {
+    var self = this;
+    return function (item, plugin_dir, plugin_id, options, project) {
+        var installer = self._handler[type];
+
+        if (!installer) {
+            console.log('unrecognized type ' + type);
+
+        } else {
+            var wwwDest = options.usePlatformWww ?
+                self.getPlatformInfo().locations.platformWww :
+                self._handler.www_dir(self.root);
+
+            if (type === 'asset') {
+                installer.install(item, plugin_dir, wwwDest);
+            } else if (type === 'js-module') {
+                installer.install(item, plugin_dir, plugin_id, wwwDest);
+            } else {
+                installer.install(item, plugin_dir, self.root, plugin_id, options, project);
+            }
+        }
+    };
+};
+
+Api.prototype._getUninstaller = function (type) {
+    var self = this;
+    return function (item, plugin_dir, plugin_id, options, project) {
+        var installer = self._handler[type];
+
+        if (!installer) {
+            console.log('browser plugin uninstall: unrecognized type, skipping : ' + type);
+
+        } else {
+            var wwwDest = options.usePlatformWww ?
+                self.getPlatformInfo().locations.platformWww :
+                self._handler.www_dir(self.root);
+
+            if (['asset', 'js-module'].indexOf(type) > -1) {
+                return installer.uninstall(item, wwwDest, plugin_id);
+            } else {
+                return installer.uninstall(item, self.root, plugin_id, options, project);
+            }
+
+        }
+    };
+};
+
+/**
+ * Removes the specified modules from list of installed modules and updates
+ *   platform_json and cordova_plugins.js on disk.
+ *
+ * @param   {PluginInfo}  plugin  PluginInfo instance for plugin, which modules
+ *   needs to be added.
+ * @param   {String}  targetDir  The directory, where updated cordova_plugins.js
+ *   should be written to.
+ */
+Api.prototype._addModulesInfo = function (plugin, targetDir) {
+    var installedModules = this._platformJson.root.modules || [];
+
+    var installedPaths = installedModules.map(function (installedModule) {
+        return installedModule.file;
+    });
+
+    var modulesToInstall = plugin.getJsModules(this.platform)
+        .filter(function (moduleToInstall) {
+            return installedPaths.indexOf(moduleToInstall.file) === -1;
+        }).map(function (moduleToInstall) {
+            var moduleName = plugin.id + '.' + (moduleToInstall.name || moduleToInstall.src.match(/([^\/]+)\.js/)[1]);
+            var obj = {
+                file: ['plugins', plugin.id, moduleToInstall.src].join('/'), /* eslint no-useless-escape : 0 */
+                id: moduleName,
+                pluginId: plugin.id
+            };
+            if (moduleToInstall.clobbers.length > 0) {
+                obj.clobbers = moduleToInstall.clobbers.map(function (o) { return o.target; });
+            }
+            if (moduleToInstall.merges.length > 0) {
+                obj.merges = moduleToInstall.merges.map(function (o) { return o.target; });
+            }
+            if (moduleToInstall.runs) {
+                obj.runs = true;
+            }
+
+            return obj;
+        });
+
+    this._platformJson.root.modules = installedModules.concat(modulesToInstall);
+    if (!this._platformJson.root.plugin_metadata) {
+        this._platformJson.root.plugin_metadata = {};
+    }
+    this._platformJson.root.plugin_metadata[plugin.id] = plugin.version;
+
+    this._writePluginModules(targetDir);
+    this._platformJson.save();
+};
+/**
+ * Fetches all installed modules, generates cordova_plugins contents and writes
+ *   it to file.
+ *
+ * @param   {String}  targetDir  Directory, where write cordova_plugins.js to.
+ *   Ususally it is either <platform>/www or <platform>/platform_www
+ *   directories.
+ */
+Api.prototype._writePluginModules = function (targetDir) {
+    // Write out moduleObjects as JSON wrapped in a cordova module to cordova_plugins.js
+    var final_contents = 'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {\n';
+    final_contents += 'module.exports = ' + JSON.stringify(this._platformJson.root.modules, null, '    ') + ';\n';
+    final_contents += 'module.exports.metadata = \n';
+    final_contents += '// TOP OF METADATA\n';
+    final_contents += JSON.stringify(this._platformJson.root.plugin_metadata || {}, null, '    ') + '\n';
+    final_contents += '// BOTTOM OF METADATA\n';
+    final_contents += '});'; // Close cordova.define.
+
+    shell.mkdir('-p', targetDir);
+    fs.writeFileSync(path.join(targetDir, 'cordova_plugins.js'), final_contents, 'utf-8');
+};
+
+/**
+ * Removes the specified modules from list of installed modules and updates
+ *   platform_json and cordova_plugins.js on disk.
+ *
+ * @param   {PluginInfo}  plugin  PluginInfo instance for plugin, which modules
+ *   needs to be removed.
+ * @param   {String}  targetDir  The directory, where updated cordova_plugins.js
+ *   should be written to.
+ */
+Api.prototype._removeModulesInfo = function (plugin, targetDir) {
+    var installedModules = this._platformJson.root.modules || [];
+    var modulesToRemove = plugin.getJsModules(this.platform)
+        .map(function (jsModule) {
+            return ['plugins', plugin.id, jsModule.src].join('/');
+        });
+
+    var updatedModules = installedModules
+        .filter(function (installedModule) {
+            return (modulesToRemove.indexOf(installedModule.file) === -1);
+        });
+
+    this._platformJson.root.modules = updatedModules;
+    if (this._platformJson.root.plugin_metadata) {
+        delete this._platformJson.root.plugin_metadata[plugin.id];
+    }
+
+    this._writePluginModules(targetDir);
+    this._platformJson.save();
+};
+
+Api.prototype.build = function (buildOptions) {
+    var self = this;
+    return require('./lib/check_reqs').run()
+        .then(function () {
+            return require('./lib/build').run.call(self, buildOptions);
+        });
+};
+
+Api.prototype.run = function (runOptions) {
+    return require('./lib/run').run(runOptions);
+};
+
+Api.prototype.clean = function (cleanOptions) {
+    return require('./lib/clean').run(cleanOptions);
+};
+
+Api.prototype.requirements = function () {
+    return require('./lib/check_reqs').run();
+};
+
+module.exports = Api;

+ 135 - 0
platforms/browser/cordova/browser_handler.js

@@ -0,0 +1,135 @@
+/**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+
+var path = require('path');
+var fs = require('fs');
+var shell = require('shelljs');
+var events = require('cordova-common').events;
+
+module.exports = {
+    www_dir: function (project_dir) {
+        return path.join(project_dir, 'www');
+    },
+    package_name: function (project_dir) {
+        // this method should the id from root config.xml => <widget id=xxx
+        // return common.package_name(project_dir, this.www_dir(project_dir));
+        // console.log('package_name called with ' + project_dir);
+        var pkgName = 'io.cordova.hellocordova';
+        var widget_id_regex = /(?:<widget\s+id=['"])(\S+)(?:['"])/;
+
+        var configPath = path.join(project_dir, 'config.xml');
+        if (fs.existsSync(configPath)) {
+            var configStr = fs.readFileSync(configPath, 'utf8');
+            var res = configStr.match(widget_id_regex);
+            if (res && res.length > 1) {
+                pkgName = res[1];
+            }
+        }
+        return pkgName;
+    },
+    'js-module': {
+        install: function (jsModule, plugin_dir, plugin_id, www_dir) {
+            // Copy the plugin's files into the www directory.
+            var moduleSource = path.resolve(plugin_dir, jsModule.src);
+            // Get module name based on existing 'name' attribute or filename
+            // Must use path.extname/path.basename instead of path.parse due to CB-9981
+            var moduleName = plugin_id + '.' + (jsModule.name || path.basename(jsModule.src, path.extname(jsModule.src)));
+
+            // Read in the file, prepend the cordova.define, and write it back out.
+            var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
+            if (moduleSource.match(/.*\.json$/)) {
+                scriptContent = 'module.exports = ' + scriptContent;
+            }
+            scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) { ' + scriptContent + '\n});\n';
+
+            var moduleDestination = path.resolve(www_dir, 'plugins', plugin_id, jsModule.src);
+            shell.mkdir('-p', path.dirname(moduleDestination));
+            fs.writeFileSync(moduleDestination, scriptContent, 'utf-8');
+        },
+        uninstall: function (jsModule, www_dir, plugin_id) {
+            var pluginRelativePath = path.join('plugins', plugin_id, jsModule.src);
+            // common.removeFileAndParents(www_dir, pluginRelativePath);
+            console.log('js-module uninstall called : ' + pluginRelativePath);
+        }
+    },
+    'source-file': {
+        install: function (obj, plugin_dir, project_dir, plugin_id, options) {
+            // var dest = path.join(obj.targetDir, path.basename(obj.src));
+            // common.copyFile(plugin_dir, obj.src, project_dir, dest);
+            console.log('install called');
+        },
+        uninstall: function (obj, project_dir, plugin_id, options) {
+            // var dest = path.join(obj.targetDir, path.basename(obj.src));
+            // common.removeFile(project_dir, dest);
+            console.log('uninstall called');
+        }
+    },
+    'header-file': {
+        install: function (obj, plugin_dir, project_dir, plugin_id, options) {
+            events.emit('verbose', 'header-fileinstall is not supported for browser');
+        },
+        uninstall: function (obj, project_dir, plugin_id, options) {
+            events.emit('verbose', 'header-file.uninstall is not supported for browser');
+        }
+    },
+    'resource-file': {
+        install: function (obj, plugin_dir, project_dir, plugin_id, options) {
+            events.emit('verbose', 'resource-file.install is not supported for browser');
+        },
+        uninstall: function (obj, project_dir, plugin_id, options) {
+            events.emit('verbose', 'resource-file.uninstall is not supported for browser');
+        }
+    },
+    'framework': {
+        install: function (obj, plugin_dir, project_dir, plugin_id, options) {
+            events.emit('verbose', 'framework.install is not supported for browser');
+        },
+        uninstall: function (obj, project_dir, plugin_id, options) {
+            events.emit('verbose', 'framework.uninstall is not supported for browser');
+        }
+    },
+    'lib-file': {
+        install: function (obj, plugin_dir, project_dir, plugin_id, options) {
+            events.emit('verbose', 'lib-file.install is not supported for browser');
+        },
+        uninstall: function (obj, project_dir, plugin_id, options) {
+            events.emit('verbose', 'lib-file.uninstall is not supported for browser');
+        }
+    },
+    asset: {
+        install: function (asset, plugin_dir, wwwDest) {
+            var src = path.join(plugin_dir, asset.src);
+            var dest = path.join(wwwDest, asset.target);
+            var destDir = path.parse(dest).dir;
+            if (destDir !== '' && !fs.existsSync(destDir)) {
+                shell.mkdir('-p', destDir);
+            }
+
+            if (fs.statSync(src).isDirectory()) {
+                shell.cp('-Rf', src + '/*', dest);
+            } else {
+                shell.cp('-f', src, dest);
+            }
+        },
+        uninstall: function (asset, wwwDest, plugin_id) {
+            shell.rm('-rf', path.join(wwwDest, asset.target));
+            shell.rm('-rf', path.join(wwwDest, 'plugins', plugin_id));
+        }
+    }
+};

+ 120 - 0
platforms/browser/cordova/browser_parser.js

@@ -0,0 +1,120 @@
+/**
+    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');
+var path = require('path');
+var shell = require('shelljs');
+var CordovaError = require('cordova-common').CordovaError;
+var events = require('cordova-common').events;
+var FileUpdater = require('cordova-common').FileUpdater;
+
+function dirExists (dir) {
+    return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
+}
+
+function browser_parser (project) {
+    if (!dirExists(project) || !dirExists(path.join(project, 'cordova'))) {
+        throw new CordovaError('The provided path "' + project + '" is not a valid browser project.');
+    }
+    this.path = project;
+}
+
+module.exports = browser_parser;
+
+// Returns a promise.
+browser_parser.prototype.update_from_config = function () {
+    return Promise.resolve();
+};
+
+browser_parser.prototype.www_dir = function () {
+    return path.join(this.path, 'www');
+};
+
+// Used for creating platform_www in projects created by older versions.
+browser_parser.prototype.cordovajs_path = function (libDir) {
+    var jsPath = path.join(libDir, 'cordova-lib', 'cordova.js');
+    return path.resolve(jsPath);
+};
+
+browser_parser.prototype.cordovajs_src_path = function (libDir) {
+    // console.log("cordovajs_src_path");
+    var jsPath = path.join(libDir, 'cordova-js-src');
+    return path.resolve(jsPath);
+};
+
+/**
+ * Logs all file operations via the verbose event stream, indented.
+ */
+function logFileOp (message) {
+    events.emit('verbose', '  ' + message);
+}
+
+// Replace the www dir with contents of platform_www and app www.
+browser_parser.prototype.update_www = function (cordovaProject, opts) {
+    var platform_www = path.join(this.path, 'platform_www');
+    var my_www = this.www_dir();
+    // add cordova www and platform_www to sourceDirs
+    var sourceDirs = [
+        path.relative(cordovaProject.root, cordovaProject.locations.www),
+        path.relative(cordovaProject.root, platform_www)
+    ];
+
+    // If project contains 'merges' for our platform, use them as another overrides
+    var merges_path = path.join(cordovaProject.root, 'merges', 'browser');
+    if (fs.existsSync(merges_path)) {
+        events.emit('verbose', 'Found "merges/browser" folder. Copying its contents into the browser project.');
+        // add merges/browser to sourceDirs
+        sourceDirs.push(path.join('merges', 'browser'));
+    }
+
+    // targetDir points to browser/www
+    var targetDir = path.relative(cordovaProject.root, my_www);
+    events.emit('verbose', 'Merging and updating files from [' + sourceDirs.join(', ') + '] to ' + targetDir);
+    FileUpdater.mergeAndUpdateDir(sourceDirs, targetDir, { rootDir: cordovaProject.root }, logFileOp);
+};
+
+browser_parser.prototype.update_overrides = function () {
+    // console.log("update_overrides");
+
+    // TODO: ?
+    // var projectRoot = util.isCordova(this.path);
+    // var mergesPath = path.join(util.appDir(projectRoot), 'merges', 'browser');
+    // if(fs.existsSync(mergesPath)) {
+    //     var overrides = path.join(mergesPath, '*');
+    //     shell.cp('-rf', overrides, this.www_dir());
+    // }
+};
+
+browser_parser.prototype.config_xml = function () {
+    return path.join(this.path, 'config.xml');
+};
+
+// Returns a promise.
+browser_parser.prototype.update_project = function (cfg) {
+    // console.log("update_project ",cfg);
+    var defer = this.update_from_config();
+    var self = this;
+    var www_dir = self.www_dir();
+    defer.then(function () {
+        self.update_overrides();
+        // Copy munged config.xml to platform www dir
+        shell.cp('-rf', path.join(www_dir, '..', 'config.xml'), www_dir);
+    });
+    return defer;
+};

+ 34 - 0
platforms/browser/cordova/build

@@ -0,0 +1,34 @@
+#!/usr/bin/env node
+
+/*
+       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 build = require('./lib/build'),
+    args  = process.argv;
+
+// provide help
+if ( args[2] == '--help' || args[2] == '/?' || args[2] == '-h' || args[2] == '/h' ||
+                    args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
+    build.help();
+    process.exit(0);
+} else {
+
+    build.run();
+}

+ 26 - 0
platforms/browser/cordova/build.bat

@@ -0,0 +1,26 @@
+:: 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 OFF
+SET script_path="%~dp0build"
+IF EXIST %script_path% (
+        node %script_path% %*
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'build' script in 'cordova' folder, aborting...>&2
+    EXIT /B 1
+)

+ 36 - 0
platforms/browser/cordova/clean

@@ -0,0 +1,36 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+
+var path = require('path'),
+    clean = require('./lib/clean'),
+    args  = process.argv;
+
+// Support basic help commands
+if ( args.length > 2
+   || args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
+                    args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
+    console.log('Usage: ' + path.relative(process.cwd(), path.join(__dirname, 'clean')) );
+    process.exit(0);
+} else {
+    clean.run();
+}
+

+ 26 - 0
platforms/browser/cordova/clean.bat

@@ -0,0 +1,26 @@
+:: 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 OFF
+SET script_path="%~dp0clean"
+IF EXIST %script_path% (
+        node %script_path% %*
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'clean' script in 'cordova' folder, aborting...>&2
+    EXIT /B 1
+)

+ 22 - 0
platforms/browser/cordova/defaults.xml

@@ -0,0 +1,22 @@
+<?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 id="io.cordova.hellocordova" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+
+</widget>

+ 37 - 0
platforms/browser/cordova/lib/build.js

@@ -0,0 +1,37 @@
+#!/usr/bin/env node
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var path = require('path');
+var check_reqs = require('./check_reqs');
+
+/**
+ * run
+ *   Creates a zip file int platform/build folder
+ */
+module.exports.run = function () {
+    return check_reqs.run();
+};
+
+module.exports.help = function () {
+    console.log('Usage: cordova build browser');
+    var wwwPath = path.resolve(path.join(__dirname, '../../www'));
+    console.log("Build will create the packaged app in '" + wwwPath + "'.");
+};

+ 27 - 0
platforms/browser/cordova/lib/check_reqs.js

@@ -0,0 +1,27 @@
+#!/usr/bin/env node
+
+/*
+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.
+*/
+
+// add methods as we determine what are the requirements
+
+module.exports.run = function () {
+    // caller expects a promise resolved with an array of conditions
+    return Promise.resolve([]);
+};

+ 51 - 0
platforms/browser/cordova/lib/clean.js

@@ -0,0 +1,51 @@
+#!/usr/bin/env node
+
+/*
+ * 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');
+var shell = require('shelljs');
+var path = require('path');
+var check_reqs = require('./check_reqs');
+var platformBuildDir = path.join('platforms', 'browser', 'www');
+
+var run = function () {
+
+    // TODO: everything calls check_reqs ... why?
+    // Check that requirements are (still) met
+    if (!check_reqs.run()) {
+        console.error('Please make sure you meet the software requirements in order to clean an browser cordova project');
+        process.exit(2);
+    }
+
+    try {
+        if (fs.existsSync(platformBuildDir)) {
+            shell.rm('-r', platformBuildDir);
+        }
+    } catch (err) {
+        console.log('could not remove ' + platformBuildDir + ' : ' + err.message);
+    }
+};
+
+module.exports.run = run;
+// just on the off chance something is still calling cleanProject, we will leave this here for a while
+module.exports.cleanProject = function () {
+    console.log('lib/clean will soon only export a `run` command, please update to not call `cleanProject`.');
+    return run();
+};

+ 68 - 0
platforms/browser/cordova/lib/run.js

@@ -0,0 +1,68 @@
+#!/usr/bin/env node
+
+/*
+ * 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');
+var path = require('path');
+var url = require('url');
+var cordovaServe = require('cordova-serve');
+
+module.exports.run = function (args) {
+    // defaults
+    args.port = args.port || 8000;
+    args.target = args.target || 'default'; // make default the system browser
+    args.noLogOutput = args.silent || false;
+
+    var wwwPath = path.join(__dirname, '../../www');
+    var manifestFilePath = path.resolve(path.join(wwwPath, 'manifest.json'));
+
+    var startPage;
+
+    // get start page from manifest
+    if (fs.existsSync(manifestFilePath)) {
+        try {
+            var manifest = require(manifestFilePath);
+            startPage = manifest.start_url;
+        } catch (err) {
+            console.log('failed to require manifest ... ' + err);
+        }
+    }
+
+    var server = cordovaServe();
+    server.servePlatform('browser', { port: args.port, noServerInfo: true, noLogOutput: args.noLogOutput })
+        .then(function () {
+            if (!startPage) {
+                // failing all else, set the default
+                startPage = 'index.html';
+            }
+
+            var projectUrl = (new url.URL(`http://localhost:${server.port}/${startPage}`)).href;
+
+            console.log('startPage = ' + startPage);
+            console.log('Static file server running @ ' + projectUrl + '\nCTRL + C to shut down');
+            return server.launchBrowser({ 'target': args.target, 'url': projectUrl });
+        })
+        .catch(function (error) {
+            console.log(error.message || error.toString());
+            if (server.server) {
+                server.server.close();
+            }
+        });
+};

+ 20 - 0
platforms/browser/cordova/log

@@ -0,0 +1,20 @@
+#!/usr/bin/env node
+
+/*
+       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.
+*/
+
+console.log("cordova/log");

+ 53 - 0
platforms/browser/cordova/run

@@ -0,0 +1,53 @@
+#!/usr/bin/env node
+
+/*
+       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'),
+    nopt  = require('nopt'),
+    url = require('url'),
+    runForrest = require('./lib/run'),
+    cordovaServe = require('cordova-serve');
+
+var args = process.argv;
+
+start(args);
+
+function start(argv) {
+    var args  = nopt({'help': Boolean, 'target': String, 'port': Number}, {'help': ['/?', '-h', 'help', '-help', '/help']}, argv);
+    if(args.help) {
+        help();
+    }
+    else {
+        return runForrest.run(args);
+    }
+}
+
+function help() {
+    console.log("\nUsage: run [ --target=<browser> ] [ --port=<number> ]");
+    console.log("    --target=<browser> : Launches the specified browser. Chrome is default.");
+    console.log("    --port=<number>    : Http server uses specified port number.");
+    console.log("Examples:");
+    console.log("    run");
+    console.log("    run -- --target=ie");
+    console.log("    run -- --target=chrome --port=8000");
+    console.log("");
+    process.exit(0);
+}

+ 26 - 0
platforms/browser/cordova/run.bat

@@ -0,0 +1,26 @@
+:: 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 OFF
+SET script_path="%~dp0run"
+IF EXIST %script_path% (
+        node %script_path% %*
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'run' script in 'cordova' folder, aborting...>&2
+    EXIT /B 1
+)

+ 25 - 0
platforms/browser/cordova/version

@@ -0,0 +1,25 @@
+#!/usr/bin/env node
+
+/*
+       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.
+*/
+
+// Coho updates this line:
+var VERSION = "6.0.0";
+
+console.log(VERSION);

+ 26 - 0
platforms/browser/cordova/version.bat

@@ -0,0 +1,26 @@
+:: 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 OFF
+SET script_path="%~dp0version"
+IF EXIST %script_path% (
+        node %script_path% %*
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'version' script in 'cordova' folder, aborting...>&2
+    EXIT /B 1
+)

+ 90 - 0
platforms/browser/platform_www/cordova-js-src/confighelper.js

@@ -0,0 +1,90 @@
+/*
+ *
+ * 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 config;
+
+function Config(xhr) {
+    function loadPreferences(xhr) {
+       var parser = new DOMParser();
+       var doc = parser.parseFromString(xhr.responseText, "application/xml");
+
+       var preferences = doc.getElementsByTagName("preference");
+       return Array.prototype.slice.call(preferences);
+    }
+
+    this.xhr = xhr;
+    this.preferences = loadPreferences(this.xhr);
+}
+
+function readConfig(success, error) {
+    var xhr;
+
+    if(typeof config != 'undefined') {
+        success(config);
+    }
+
+    function fail(msg) {
+        console.error(msg);
+
+        if(error) {
+            error(msg);
+        }
+    }
+
+    var xhrStatusChangeHandler = function() {
+        if (xhr.readyState == 4) {
+            if (xhr.status == 200 || xhr.status == 304 || xhr.status === 0 /* file:// */) {
+                config = new Config(xhr);
+                success(config);
+            }
+            else {
+                fail('[Browser][cordova.js][xhrStatusChangeHandler] Could not XHR config.xml: ' + xhr.statusText);
+            }
+        }
+    };
+
+    xhr = new XMLHttpRequest();
+    xhr.addEventListener("load", xhrStatusChangeHandler);
+
+
+    try {
+        xhr.open("get", "config.xml", true);
+        xhr.send();
+    } catch(e) {
+        fail('[Browser][cordova.js][readConfig] Could not XHR config.xml: ' + JSON.stringify(e));
+    }
+}
+
+/**
+ * Reads a preference value from config.xml.
+ * Returns preference value or undefined if it does not exist.
+ * @param {String} preferenceName Preference name to read */
+Config.prototype.getPreferenceValue = function getPreferenceValue(preferenceName) {
+    var preferenceItem = this.preferences && this.preferences.filter(function(item) {
+        return item.attributes.name && item.attributes.name.value === preferenceName;
+    });
+
+    if(preferenceItem && preferenceItem[0] && preferenceItem[0].attributes && preferenceItem[0].attributes.value) {
+        return preferenceItem[0].attributes.value.value;
+    }
+};
+
+exports.readConfig = readConfig;

+ 114 - 0
platforms/browser/platform_www/cordova-js-src/exec.js

@@ -0,0 +1,114 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/*jslint sloppy:true, plusplus:true*/
+/*global require, module, console */
+
+var cordova = require('cordova');
+var execProxy = require('cordova/exec/proxy');
+
+/**
+ * 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 proxy = execProxy.get(service, action);
+
+    args = args || [];
+
+    if (proxy) {
+        
+        var callbackId = service + cordova.callbackId++;
+        
+        if (typeof success === "function" || typeof fail === "function") {
+            cordova.callbacks[callbackId] = {success: success, fail: fail};
+        }
+        try {
+
+            
+
+            // callbackOptions param represents additional optional parameters command could pass back, like keepCallback or
+            // custom callbackId, for example {callbackId: id, keepCallback: true, status: cordova.callbackStatus.JSON_EXCEPTION }
+            var onSuccess = function (result, callbackOptions) {
+                callbackOptions = callbackOptions || {};
+                var callbackStatus;
+                // covering both undefined and null.
+                // strict null comparison was causing callbackStatus to be undefined
+                // and then no callback was called because of the check in cordova.callbackFromNative
+                // see CB-8996 Mobilespec app hang on windows
+                if (callbackOptions.status !== undefined && callbackOptions.status !== null) {
+                    callbackStatus = callbackOptions.status;
+                }
+                else {
+                    callbackStatus = cordova.callbackStatus.OK;
+                }
+                cordova.callbackSuccess(callbackOptions.callbackId || callbackId,
+                    {
+                        status: callbackStatus,
+                        message: result,
+                        keepCallback: callbackOptions.keepCallback || false
+                    });
+            };
+            var onError = function (err, callbackOptions) {
+                callbackOptions = callbackOptions || {};
+                var callbackStatus;
+                // covering both undefined and null.
+                // strict null comparison was causing callbackStatus to be undefined
+                // and then no callback was called because of the check in cordova.callbackFromNative
+                // note: status can be 0
+                if (callbackOptions.status !== undefined && callbackOptions.status !== null) {
+                    callbackStatus = callbackOptions.status;
+                }
+                else {
+                    callbackStatus = cordova.callbackStatus.OK;
+                }
+                cordova.callbackError(callbackOptions.callbackId || callbackId,
+                {
+                    status: callbackStatus,
+                    message: err,
+                    keepCallback: callbackOptions.keepCallback || false
+                });
+            };
+            proxy(onSuccess, onError, args);
+
+        } catch (e) {
+            console.log("Exception calling native with command :: " + service + " :: " + action  + " ::exception=" + e);
+        }
+    } else {
+
+        console.log("Error: exec proxy not found for :: " + service + " :: " + action);
+        
+        if(typeof fail === "function" ) {
+            fail("Missing Command Error");
+        }
+    }
+};

+ 46 - 0
platforms/browser/platform_www/cordova-js-src/platform.js

@@ -0,0 +1,46 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+module.exports = {
+    id: 'browser',
+    cordovaVersion: '4.2.0', // cordova-js
+
+    bootstrap: function() {
+
+        var modulemapper = require('cordova/modulemapper');
+        var channel = require('cordova/channel');
+
+        modulemapper.clobbers('cordova/exec/proxy', 'cordova.commandProxy');
+
+        channel.onNativeReady.fire();
+
+        document.addEventListener("visibilitychange", function(){
+            if(document.hidden) {
+                channel.onPause.fire();
+            }
+            else {
+                channel.onResume.fire();
+            }
+        });
+
+    // End of bootstrap
+    }
+};

+ 1594 - 0
platforms/browser/platform_www/cordova.js

@@ -0,0 +1,1594 @@
+// Platform: browser
+// d07d9d0989196f1b90fe962ca68f5ceb355c69ec
+/*
+ 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() {
+var PLATFORM_VERSION_BUILD_LABEL = '6.0.0';
+// file: src/scripts/require.js
+
+var require;
+var 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 = {};
+    var SEPARATOR = '.';
+
+    function build (module) {
+        var factory = module.factory;
+        var localRequire = function (id) {
+            var resultantId = id;
+            // Its a relative path, so lop off the last portion and add the id (minus "./")
+            if (id.charAt(0) === '.') {
+                resultantId = module.id.slice(0, module.id.lastIndexOf(SEPARATOR)) + SEPARATOR + id.slice(2);
+            }
+            return require(resultantId);
+        };
+        module.exports = {};
+        delete module.factory;
+        factory(localRequire, 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: src/cordova.js
+define("cordova", function(require, exports, module) {
+
+// Workaround for Windows 10 in hosted environment case
+// http://www.w3.org/html/wg/drafts/html/master/browsers.html#named-access-on-the-window-object
+if (window.cordova && !(window.cordova instanceof HTMLElement)) { // eslint-disable-line no-undef
+    throw new Error('cordova already defined');
+}
+
+var channel = require('cordova/channel');
+var platform = require('cordova/platform');
+
+/**
+ * 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 = {};
+var 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;
+}
+
+/* eslint-disable no-undef */
+var cordova = {
+    define: define,
+    require: require,
+    version: PLATFORM_VERSION_BUILD_LABEL,
+    platformVersion: PLATFORM_VERSION_BUILD_LABEL,
+    platformId: platform.id,
+
+    /* eslint-enable no-undef */
+
+    /**
+     * 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) {
+        cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
+    },
+
+    /**
+     * 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.
+        cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
+    },
+
+    /**
+     * Called by native code when returning the result from an action.
+     */
+    callbackFromNative: function (callbackId, isSuccess, status, args, keepCallback) {
+        try {
+            var callback = cordova.callbacks[callbackId];
+            if (callback) {
+                if (isSuccess && status === cordova.callbackStatus.OK) {
+                    callback.success && callback.success.apply(null, args);
+                } else if (!isSuccess) {
+                    callback.fail && callback.fail.apply(null, args);
+                }
+                /*
+                else
+                    Note, this case is intentionally not caught.
+                    this can happen if isSuccess is true, but callbackStatus is NO_RESULT
+                    which is used to remove a callback from the list without calling the callbacks
+                    typically keepCallback is false in this case
+                */
+                // Clear callback if not expecting any more results
+                if (!keepCallback) {
+                    delete cordova.callbacks[callbackId];
+                }
+            }
+        } catch (err) {
+            var msg = 'Error in ' + (isSuccess ? 'Success' : 'Error') + ' callbackId: ' + callbackId + ' : ' + err;
+            console && console.log && console.log(msg);
+            console && console.log && err.stack && console.log(err.stack);
+            cordova.fireWindowEvent('cordovacallbackerror', { 'message': msg });
+            throw err;
+        }
+    },
+    addConstructor: function (func) {
+        channel.onCordovaReady.subscribe(function () {
+            try {
+                func();
+            } catch (e) {
+                console.log('Failed to run constructor: ' + e);
+            }
+        });
+    }
+};
+
+module.exports = cordova;
+
+});
+
+// file: src/common/argscheck.js
+define("cordova/argscheck", function(require, exports, module) {
+
+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);
+        var cUpper = c.toUpperCase();
+        var 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 unit tests.
+        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: src/common/base64.js
+define("cordova/base64", function(require, exports, module) {
+
+var base64 = exports;
+
+base64.fromArrayBuffer = function (arrayBuffer) {
+    var array = new Uint8Array(arrayBuffer);
+    return uint8ToBase64(array);
+};
+
+base64.toArrayBuffer = function (str) {
+    var decodedStr = typeof atob !== 'undefined' ? atob(str) : Buffer.from(str, 'base64').toString('binary'); // eslint-disable-line no-undef
+    var arrayBuffer = new ArrayBuffer(decodedStr.length);
+    var array = new Uint8Array(arrayBuffer);
+    for (var i = 0, len = decodedStr.length; i < len; i++) {
+        array[i] = decodedStr.charCodeAt(i);
+    }
+    return arrayBuffer;
+};
+
+// ------------------------------------------------------------------------------
+
+/* This code is based on the performance tests at http://jsperf.com/b64tests
+ * This 12-bit-at-a-time algorithm was the best performing version on all
+ * platforms tested.
+ */
+
+var b64_6bit = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+var b64_12bit;
+
+var b64_12bitTable = function () {
+    b64_12bit = [];
+    for (var i = 0; i < 64; i++) {
+        for (var j = 0; j < 64; j++) {
+            b64_12bit[i * 64 + j] = b64_6bit[i] + b64_6bit[j];
+        }
+    }
+    b64_12bitTable = function () { return b64_12bit; };
+    return b64_12bit;
+};
+
+function uint8ToBase64 (rawData) {
+    var numBytes = rawData.byteLength;
+    var output = '';
+    var segment;
+    var table = b64_12bitTable();
+    for (var i = 0; i < numBytes - 2; i += 3) {
+        segment = (rawData[i] << 16) + (rawData[i + 1] << 8) + rawData[i + 2];
+        output += table[segment >> 12];
+        output += table[segment & 0xfff];
+    }
+    if (numBytes - i === 2) {
+        segment = (rawData[i] << 16) + (rawData[i + 1] << 8);
+        output += table[segment >> 12];
+        output += b64_6bit[(segment & 0xfff) >> 6];
+        output += '=';
+    } else if (numBytes - i === 1) {
+        segment = (rawData[i] << 16);
+        output += table[segment >> 12];
+        output += '==';
+    }
+    return output;
+}
+
+});
+
+// file: src/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);
+    var needsProperty = false;
+    try {
+        obj[key] = value;
+    } catch (e) {
+        needsProperty = true;
+    }
+    // Getters can only be overridden by getters.
+    if (needsProperty || 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: src/common/channel.js
+define("cordova/channel", function(require, exports, module) {
+
+var utils = require('cordova/utils');
+var 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.
+ * 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
+ *
+ * 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;
+};
+var 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;
+        var i = len;
+        var 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();
+    },
+    /* eslint-disable no-return-assign */
+    create: function (type) {
+        return channel[type] = new Channel(type, false);
+    },
+    createSticky: function (type) {
+        return channel[type] = new Channel(type, true);
+    },
+    /* eslint-enable no-return-assign */
+    /**
+     * 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 checkSubscriptionArgument (argument) {
+    if (typeof argument !== 'function' && typeof argument.handleEvent !== 'function') {
+        throw new Error(
+            'Must provide a function or an EventListener object ' +
+                'implementing the handleEvent interface.'
+        );
+    }
+}
+
+/**
+ * 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 (eventListenerOrFunction, eventListener) {
+    checkSubscriptionArgument(eventListenerOrFunction);
+    var handleEvent, guid;
+
+    if (eventListenerOrFunction && typeof eventListenerOrFunction === 'object') {
+        // Received an EventListener object implementing the handleEvent interface
+        handleEvent = eventListenerOrFunction.handleEvent;
+        eventListener = eventListenerOrFunction;
+    } else {
+        // Received a function to handle event
+        handleEvent = eventListenerOrFunction;
+    }
+
+    if (this.state === 2) {
+        handleEvent.apply(eventListener || this, this.fireArgs);
+        return;
+    }
+
+    guid = eventListenerOrFunction.observer_guid;
+    if (typeof eventListener === 'object') {
+        handleEvent = utils.close(eventListener, handleEvent);
+    }
+
+    if (!guid) {
+        // First time any channel has seen this subscriber
+        guid = '' + nextGuid++;
+    }
+    handleEvent.observer_guid = guid;
+    eventListenerOrFunction.observer_guid = guid;
+
+    // Don't add the same handler more than once.
+    if (!this.handlers[guid]) {
+        this.handlers[guid] = handleEvent;
+        this.numHandlers++;
+        if (this.numHandlers === 1) {
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+/**
+ * Unsubscribes the function with the given guid from the channel.
+ */
+Channel.prototype.unsubscribe = function (eventListenerOrFunction) {
+    checkSubscriptionArgument(eventListenerOrFunction);
+    var handleEvent, guid, handler;
+
+    if (eventListenerOrFunction && typeof eventListenerOrFunction === 'object') {
+        // Received an EventListener object implementing the handleEvent interface
+        handleEvent = eventListenerOrFunction.handleEvent;
+    } else {
+        // Received a function to handle event
+        handleEvent = eventListenerOrFunction;
+    }
+
+    guid = handleEvent.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; // eslint-disable-line no-unused-vars
+    var 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 all automatically loaded JS plugins are loaded and ready.
+// FIXME remove this
+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');
+
+// Channels that must fire before "deviceready" is fired.
+channel.waitForInitialization('onCordovaReady');
+channel.waitForInitialization('onDOMContentLoaded');
+
+module.exports = channel;
+
+});
+
+// file: /Users/erisu/git/apache/cordova/cordova-browser/cordova-js-src/confighelper.js
+define("cordova/confighelper", function(require, exports, module) {
+
+var config;
+
+function Config(xhr) {
+    function loadPreferences(xhr) {
+       var parser = new DOMParser();
+       var doc = parser.parseFromString(xhr.responseText, "application/xml");
+
+       var preferences = doc.getElementsByTagName("preference");
+       return Array.prototype.slice.call(preferences);
+    }
+
+    this.xhr = xhr;
+    this.preferences = loadPreferences(this.xhr);
+}
+
+function readConfig(success, error) {
+    var xhr;
+
+    if(typeof config != 'undefined') {
+        success(config);
+    }
+
+    function fail(msg) {
+        console.error(msg);
+
+        if(error) {
+            error(msg);
+        }
+    }
+
+    var xhrStatusChangeHandler = function() {
+        if (xhr.readyState == 4) {
+            if (xhr.status == 200 || xhr.status == 304 || xhr.status === 0 /* file:// */) {
+                config = new Config(xhr);
+                success(config);
+            }
+            else {
+                fail('[Browser][cordova.js][xhrStatusChangeHandler] Could not XHR config.xml: ' + xhr.statusText);
+            }
+        }
+    };
+
+    xhr = new XMLHttpRequest();
+    xhr.addEventListener("load", xhrStatusChangeHandler);
+
+
+    try {
+        xhr.open("get", "config.xml", true);
+        xhr.send();
+    } catch(e) {
+        fail('[Browser][cordova.js][readConfig] Could not XHR config.xml: ' + JSON.stringify(e));
+    }
+}
+
+/**
+ * Reads a preference value from config.xml.
+ * Returns preference value or undefined if it does not exist.
+ * @param {String} preferenceName Preference name to read */
+Config.prototype.getPreferenceValue = function getPreferenceValue(preferenceName) {
+    var preferenceItem = this.preferences && this.preferences.filter(function(item) {
+        return item.attributes.name && item.attributes.name.value === preferenceName;
+    });
+
+    if(preferenceItem && preferenceItem[0] && preferenceItem[0].attributes && preferenceItem[0].attributes.value) {
+        return preferenceItem[0].attributes.value.value;
+    }
+};
+
+exports.readConfig = readConfig;
+
+});
+
+// file: /Users/erisu/git/apache/cordova/cordova-browser/cordova-js-src/exec.js
+define("cordova/exec", function(require, exports, module) {
+
+/*jslint sloppy:true, plusplus:true*/
+/*global require, module, console */
+
+var cordova = require('cordova');
+var execProxy = require('cordova/exec/proxy');
+
+/**
+ * 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 proxy = execProxy.get(service, action);
+
+    args = args || [];
+
+    if (proxy) {
+        
+        var callbackId = service + cordova.callbackId++;
+        
+        if (typeof success === "function" || typeof fail === "function") {
+            cordova.callbacks[callbackId] = {success: success, fail: fail};
+        }
+        try {
+
+            
+
+            // callbackOptions param represents additional optional parameters command could pass back, like keepCallback or
+            // custom callbackId, for example {callbackId: id, keepCallback: true, status: cordova.callbackStatus.JSON_EXCEPTION }
+            var onSuccess = function (result, callbackOptions) {
+                callbackOptions = callbackOptions || {};
+                var callbackStatus;
+                // covering both undefined and null.
+                // strict null comparison was causing callbackStatus to be undefined
+                // and then no callback was called because of the check in cordova.callbackFromNative
+                // see CB-8996 Mobilespec app hang on windows
+                if (callbackOptions.status !== undefined && callbackOptions.status !== null) {
+                    callbackStatus = callbackOptions.status;
+                }
+                else {
+                    callbackStatus = cordova.callbackStatus.OK;
+                }
+                cordova.callbackSuccess(callbackOptions.callbackId || callbackId,
+                    {
+                        status: callbackStatus,
+                        message: result,
+                        keepCallback: callbackOptions.keepCallback || false
+                    });
+            };
+            var onError = function (err, callbackOptions) {
+                callbackOptions = callbackOptions || {};
+                var callbackStatus;
+                // covering both undefined and null.
+                // strict null comparison was causing callbackStatus to be undefined
+                // and then no callback was called because of the check in cordova.callbackFromNative
+                // note: status can be 0
+                if (callbackOptions.status !== undefined && callbackOptions.status !== null) {
+                    callbackStatus = callbackOptions.status;
+                }
+                else {
+                    callbackStatus = cordova.callbackStatus.OK;
+                }
+                cordova.callbackError(callbackOptions.callbackId || callbackId,
+                {
+                    status: callbackStatus,
+                    message: err,
+                    keepCallback: callbackOptions.keepCallback || false
+                });
+            };
+            proxy(onSuccess, onError, args);
+
+        } catch (e) {
+            console.log("Exception calling native with command :: " + service + " :: " + action  + " ::exception=" + e);
+        }
+    } else {
+
+        console.log("Error: exec proxy not found for :: " + service + " :: " + action);
+        
+        if(typeof fail === "function" ) {
+            fail("Missing Command Error");
+        }
+    }
+};
+
+});
+
+// file: src/common/exec/proxy.js
+define("cordova/exec/proxy", 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: src/common/init.js
+define("cordova/init", function(require, exports, module) {
+
+var channel = require('cordova/channel');
+var cordova = require('cordova');
+var modulemapper = require('cordova/modulemapper');
+var platform = require('cordova/platform');
+var pluginloader = require('cordova/pluginloader');
+var utils = require('cordova/utils');
+
+var platformInitChannelsArray = [channel.onNativeReady, channel.onPluginsReady];
+
+function logUnfiredChannels (arr) {
+    for (var i = 0; i < arr.length; ++i) {
+        if (arr[i].state !== 2) {
+            console.log('Channel not fired: ' + arr[i].type);
+        }
+    }
+}
+
+window.setTimeout(function () {
+    if (channel.onDeviceReady.state !== 2) {
+        console.log('deviceready has not fired after 5 seconds.');
+        logUnfiredChannels(platformInitChannelsArray);
+        logUnfiredChannels(channel.deviceReadyChannelsArray);
+    }
+}, 5000);
+
+// Replace navigator before any modules are required(), to ensure it happens as soon as possible.
+// We replace it so that properties that can't be clobbered can instead be overridden.
+function replaceNavigator (origNavigator) {
+    var CordovaNavigator = function () {};
+    CordovaNavigator.prototype = origNavigator;
+    var newNavigator = new CordovaNavigator();
+    // This work-around really only applies to new APIs that are newer than Function.bind.
+    // Without it, APIs such as getGamepads() break.
+    if (CordovaNavigator.bind) {
+        for (var key in origNavigator) {
+            if (typeof origNavigator[key] === 'function') {
+                newNavigator[key] = origNavigator[key].bind(origNavigator);
+            } else {
+                (function (k) {
+                    utils.defineGetterSetter(newNavigator, key, function () {
+                        return origNavigator[k];
+                    });
+                })(key);
+            }
+        }
+    }
+    return newNavigator;
+}
+
+if (window.navigator) {
+    window.navigator = replaceNavigator(window.navigator);
+}
+
+if (!window.console) {
+    window.console = {
+        log: function () {}
+    };
+}
+if (!window.console.warn) {
+    window.console.warn = function (msg) {
+        this.log('warn: ' + msg);
+    };
+}
+
+// Register pause, resume and deviceready channels as events on document.
+channel.onPause = cordova.addDocumentEventHandler('pause');
+channel.onResume = cordova.addDocumentEventHandler('resume');
+channel.onActivated = cordova.addDocumentEventHandler('activated');
+channel.onDeviceReady = cordova.addStickyDocumentEventHandler('deviceready');
+
+// Listen for DOMContentLoaded and notify our channel subscribers.
+if (document.readyState === 'complete' || document.readyState === 'interactive') {
+    channel.onDOMContentLoaded.fire();
+} else {
+    document.addEventListener('DOMContentLoaded', function () {
+        channel.onDOMContentLoaded.fire();
+    }, false);
+}
+
+// _nativeReady is global variable that the native side can set
+// to signify that the native code is ready. It is a global since
+// it may be called before any cordova JS is ready.
+if (window._nativeReady) {
+    channel.onNativeReady.fire();
+}
+
+modulemapper.clobbers('cordova', 'cordova');
+modulemapper.clobbers('cordova/exec', 'cordova.exec');
+modulemapper.clobbers('cordova/exec', 'Cordova.exec');
+
+// Call the platform-specific initialization.
+platform.bootstrap && platform.bootstrap();
+
+// Wrap in a setTimeout to support the use-case of having plugin JS appended to cordova.js.
+// The delay allows the attached modules to be defined before the plugin loader looks for them.
+setTimeout(function () {
+    pluginloader.load(function () {
+        channel.onPluginsReady.fire();
+    });
+}, 0);
+
+/**
+ * Create all cordova objects once native side is ready.
+ */
+channel.join(function () {
+    modulemapper.mapModules(window);
+
+    platform.initialize && platform.initialize();
+
+    // Fire event to notify that all objects are created
+    channel.onCordovaReady.fire();
+
+    // Fire onDeviceReady event once page has fully loaded, all
+    // constructors have run and cordova info has been received from native
+    // side.
+    channel.join(function () {
+        require('cordova').fireDocumentEvent('deviceready');
+    }, channel.deviceReadyChannelsArray);
+
+}, platformInitChannelsArray);
+
+});
+
+// file: src/common/modulemapper.js
+define("cordova/modulemapper", function(require, exports, module) {
+
+var builder = require('cordova/builder');
+var moduleMap = define.moduleMap; // eslint-disable-line no-undef
+var symbolList;
+var 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);
+};
+
+exports.runs = function (moduleName) {
+    addEntry('r', moduleName, null);
+};
+
+function prepareNamespace (symbolPath, context) {
+    if (!symbolPath) {
+        return context;
+    }
+    var parts = symbolPath.split('.');
+    var cur = context;
+    for (var i = 0, part; part = parts[i]; ++i) { // eslint-disable-line no-cond-assign
+        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 module = require(moduleName);
+        // <runs/>
+        if (strategy === 'r') {
+            continue;
+        }
+        var symbolPath = symbolList[i + 2];
+        var lastDot = symbolPath.lastIndexOf('.');
+        var namespace = symbolPath.substr(0, lastDot);
+        var lastName = symbolPath.substr(lastDot + 1);
+
+        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.reset();
+
+});
+
+// file: /Users/erisu/git/apache/cordova/cordova-browser/cordova-js-src/platform.js
+define("cordova/platform", function(require, exports, module) {
+
+module.exports = {
+    id: 'browser',
+    cordovaVersion: '4.2.0', // cordova-js
+
+    bootstrap: function() {
+
+        var modulemapper = require('cordova/modulemapper');
+        var channel = require('cordova/channel');
+
+        modulemapper.clobbers('cordova/exec/proxy', 'cordova.commandProxy');
+
+        channel.onNativeReady.fire();
+
+        document.addEventListener("visibilitychange", function(){
+            if(document.hidden) {
+                channel.onPause.fire();
+            }
+            else {
+                channel.onResume.fire();
+            }
+        });
+
+    // End of bootstrap
+    }
+};
+
+});
+
+// file: src/common/pluginloader.js
+define("cordova/pluginloader", function(require, exports, module) {
+
+var modulemapper = require('cordova/modulemapper');
+
+// Helper function to inject a <script> tag.
+// Exported for testing.
+exports.injectScript = function (url, onload, onerror) {
+    var script = document.createElement('script');
+    // onload fires even when script fails loads with an error.
+    script.onload = onload;
+    // onerror fires for malformed URLs.
+    script.onerror = onerror;
+    script.src = url;
+    document.head.appendChild(script);
+};
+
+function injectIfNecessary (id, url, onload, onerror) {
+    onerror = onerror || onload;
+    if (id in define.moduleMap) { // eslint-disable-line no-undef
+        onload();
+    } else {
+        exports.injectScript(url, function () {
+            if (id in define.moduleMap) { // eslint-disable-line no-undef
+                onload();
+            } else {
+                onerror();
+            }
+        }, onerror);
+    }
+}
+
+function onScriptLoadingComplete (moduleList, finishPluginLoading) {
+    // Loop through all the plugins and then through their clobbers and merges.
+    for (var i = 0, module; module = moduleList[i]; i++) { // eslint-disable-line no-cond-assign
+        if (module.clobbers && module.clobbers.length) {
+            for (var j = 0; j < module.clobbers.length; j++) {
+                modulemapper.clobbers(module.id, module.clobbers[j]);
+            }
+        }
+
+        if (module.merges && module.merges.length) {
+            for (var k = 0; k < module.merges.length; k++) {
+                modulemapper.merges(module.id, module.merges[k]);
+            }
+        }
+
+        // Finally, if runs is truthy we want to simply require() the module.
+        if (module.runs) {
+            modulemapper.runs(module.id);
+        }
+    }
+
+    finishPluginLoading();
+}
+
+// Handler for the cordova_plugins.js content.
+// See plugman's plugin_loader.js for the details of this object.
+// This function is only called if the really is a plugins array that isn't empty.
+// Otherwise the onerror response handler will just call finishPluginLoading().
+function handlePluginsObject (path, moduleList, finishPluginLoading) {
+    // Now inject the scripts.
+    var scriptCounter = moduleList.length;
+
+    if (!scriptCounter) {
+        finishPluginLoading();
+        return;
+    }
+    function scriptLoadedCallback () {
+        if (!--scriptCounter) {
+            onScriptLoadingComplete(moduleList, finishPluginLoading);
+        }
+    }
+
+    for (var i = 0; i < moduleList.length; i++) {
+        injectIfNecessary(moduleList[i].id, path + moduleList[i].file, scriptLoadedCallback);
+    }
+}
+
+function findCordovaPath () {
+    var path = null;
+    var scripts = document.getElementsByTagName('script');
+    var term = '/cordova.js';
+    for (var n = scripts.length - 1; n > -1; n--) {
+        var src = scripts[n].src.replace(/\?.*$/, ''); // Strip any query param (CB-6007).
+        if (src.indexOf(term) === (src.length - term.length)) {
+            path = src.substring(0, src.length - term.length) + '/';
+            break;
+        }
+    }
+    return path;
+}
+
+// Tries to load all plugins' js-modules.
+// This is an async process, but onDeviceReady is blocked on onPluginsReady.
+// onPluginsReady is fired when there are no plugins to load, or they are all done.
+exports.load = function (callback) {
+    var pathPrefix = findCordovaPath();
+    if (pathPrefix === null) {
+        console.log('Could not find cordova.js script tag. Plugin loading may fail.');
+        pathPrefix = '';
+    }
+    injectIfNecessary('cordova/plugin_list', pathPrefix + 'cordova_plugins.js', function () {
+        var moduleList = require('cordova/plugin_list');
+        handlePluginsObject(pathPrefix, moduleList, callback);
+    }, callback);
+};
+
+});
+
+// file: src/common/urlutil.js
+define("cordova/urlutil", function(require, exports, module) {
+
+/**
+ * For already absolute URLs, returns what is passed in.
+ * For relative URLs, converts them to absolute ones.
+ */
+exports.makeAbsolute = function makeAbsolute (url) {
+    var anchorEl = document.createElement('a');
+    anchorEl.href = url;
+    return anchorEl.href;
+};
+
+});
+
+// file: src/common/utils.js
+define("cordova/utils", function(require, exports, module) {
+
+var utils = exports;
+
+/**
+ * Defines a property getter / setter for obj[key].
+ */
+utils.defineGetterSetter = function (obj, key, getFunc, opt_setFunc) {
+    if (Object.defineProperty) {
+        var desc = {
+            get: getFunc,
+            configurable: true
+        };
+        if (opt_setFunc) {
+            desc.set = opt_setFunc;
+        }
+        Object.defineProperty(obj, key, desc);
+    } else {
+        obj.__defineGetter__(key, getFunc);
+        if (opt_setFunc) {
+            obj.__defineSetter__(key, opt_setFunc);
+        }
+    }
+};
+
+/**
+ * Defines a property getter for obj[key].
+ */
+utils.defineGetter = utils.defineGetterSetter;
+
+utils.arrayIndexOf = function (a, item) {
+    if (a.indexOf) {
+        return a.indexOf(item);
+    }
+    var len = a.length;
+    for (var i = 0; i < len; ++i) {
+        if (a[i] === item) {
+            return i;
+        }
+    }
+    return -1;
+};
+
+/**
+ * Returns whether the item was found in the array.
+ */
+utils.arrayRemove = function (a, item) {
+    var index = utils.arrayIndexOf(a, item);
+    if (index !== -1) {
+        a.splice(index, 1);
+    }
+    return index !== -1;
+};
+
+utils.typeName = function (val) {
+    return Object.prototype.toString.call(val).slice(8, -1);
+};
+
+/**
+ * Returns an indication of whether the argument is an array or not
+ */
+utils.isArray = Array.isArray ||
+                function (a) { return utils.typeName(a) === 'Array'; };
+
+/**
+ * Returns an indication of whether the argument is a Date or not
+ */
+utils.isDate = function (d) {
+    return (d instanceof Date);
+};
+
+/**
+ * Does a deep clone of the object.
+ */
+utils.clone = function (obj) {
+    if (!obj || typeof obj === 'function' || utils.isDate(obj) || typeof obj !== 'object') {
+        return obj;
+    }
+
+    var retVal, i;
+
+    if (utils.isArray(obj)) {
+        retVal = [];
+        for (i = 0; i < obj.length; ++i) {
+            retVal.push(utils.clone(obj[i]));
+        }
+        return retVal;
+    }
+
+    retVal = {};
+    for (i in obj) {
+        // https://issues.apache.org/jira/browse/CB-11522 'unknown' type may be returned in
+        // custom protocol activation case on Windows Phone 8.1 causing "No such interface supported" exception
+        // on cloning.
+        if ((!(i in retVal) || retVal[i] !== obj[i]) && typeof obj[i] !== 'undefined' && typeof obj[i] !== 'unknown') { // eslint-disable-line valid-typeof
+            retVal[i] = utils.clone(obj[i]);
+        }
+    }
+    return retVal;
+};
+
+/**
+ * Returns a wrapped version of the function
+ */
+utils.close = function (context, func, params) {
+    return function () {
+        var args = params || arguments;
+        return func.apply(context, args);
+    };
+};
+
+// ------------------------------------------------------------------------------
+function UUIDcreatePart (length) {
+    var uuidpart = '';
+    for (var i = 0; i < length; i++) {
+        var uuidchar = parseInt((Math.random() * 256), 10).toString(16);
+        if (uuidchar.length === 1) {
+            uuidchar = '0' + uuidchar;
+        }
+        uuidpart += uuidchar;
+    }
+    return uuidpart;
+}
+
+/**
+ * Create a UUID
+ */
+utils.createUUID = function () {
+    return UUIDcreatePart(4) + '-' +
+        UUIDcreatePart(2) + '-' +
+        UUIDcreatePart(2) + '-' +
+        UUIDcreatePart(2) + '-' +
+        UUIDcreatePart(6);
+};
+
+/**
+ * Extends a child object from a parent object using classical inheritance
+ * pattern.
+ */
+utils.extend = (function () {
+    // proxy used to establish prototype chain
+    var F = function () {};
+    // extend Child from Parent
+    return function (Child, Parent) {
+
+        F.prototype = Parent.prototype;
+        Child.prototype = new F();
+        Child.__super__ = Parent.prototype;
+        Child.prototype.constructor = Child;
+    };
+}());
+
+/**
+ * Alerts a message in any available way: alert or console.log.
+ */
+utils.alert = function (msg) {
+    if (window.alert) {
+        window.alert(msg);
+    } else if (console && console.log) {
+        console.log(msg);
+    }
+};
+
+});
+
+window.cordova = require('cordova');
+// file: src/scripts/bootstrap.js
+
+require('cordova/init');
+
+})();

+ 10 - 0
platforms/browser/platform_www/cordova_plugins.js

@@ -0,0 +1,10 @@
+cordova.define('cordova/plugin_list', function(require, exports, module) {
+module.exports = [];
+module.exports.metadata = 
+// TOP OF METADATA
+{
+    "cordova-plugin-whitelist": "1.3.4",
+    "cordova-plugin-webpack": "0.4.7"
+}
+// BOTTOM OF METADATA
+});

BIN
platforms/browser/platform_www/favicon.ico


+ 24 - 0
platforms/browser/platform_www/manifest.json

@@ -0,0 +1,24 @@
+{
+  "name": "twelve-heroes",
+  "short_name": "twelve-heroes",
+  "description": "Description of your app from template",
+  "start_url": "index.html",
+  "scope": "index.html",
+  "icons": [
+    {
+      "src": "img/logo.png",
+      "sizes": "192x192",
+      "type": "image/png"
+    },
+    {
+      "src": "img/splash.png",
+      "sizes": "512x512",
+      "type": "image/png"
+    }
+  ],
+  "default_locale": "en",
+  "display": "standalone",
+  "background_color": "#FFF",
+  "theme_color": "#000",
+  "orientation": "landscape"
+}

+ 18 - 0
platforms/browser/www/config.xml

@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="com.jojo.twelve-heroes" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>twelve-heroes</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <allow-intent href="http://*/*" />
+    <allow-intent href="https://*/*" />
+    <allow-intent href="tel:*" />
+    <allow-intent href="sms:*" />
+    <allow-intent href="mailto:*" />
+    <allow-intent href="geo:*" />
+</widget>

+ 90 - 0
platforms/browser/www/cordova-js-src/confighelper.js

@@ -0,0 +1,90 @@
+/*
+ *
+ * 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 config;
+
+function Config(xhr) {
+    function loadPreferences(xhr) {
+       var parser = new DOMParser();
+       var doc = parser.parseFromString(xhr.responseText, "application/xml");
+
+       var preferences = doc.getElementsByTagName("preference");
+       return Array.prototype.slice.call(preferences);
+    }
+
+    this.xhr = xhr;
+    this.preferences = loadPreferences(this.xhr);
+}
+
+function readConfig(success, error) {
+    var xhr;
+
+    if(typeof config != 'undefined') {
+        success(config);
+    }
+
+    function fail(msg) {
+        console.error(msg);
+
+        if(error) {
+            error(msg);
+        }
+    }
+
+    var xhrStatusChangeHandler = function() {
+        if (xhr.readyState == 4) {
+            if (xhr.status == 200 || xhr.status == 304 || xhr.status === 0 /* file:// */) {
+                config = new Config(xhr);
+                success(config);
+            }
+            else {
+                fail('[Browser][cordova.js][xhrStatusChangeHandler] Could not XHR config.xml: ' + xhr.statusText);
+            }
+        }
+    };
+
+    xhr = new XMLHttpRequest();
+    xhr.addEventListener("load", xhrStatusChangeHandler);
+
+
+    try {
+        xhr.open("get", "config.xml", true);
+        xhr.send();
+    } catch(e) {
+        fail('[Browser][cordova.js][readConfig] Could not XHR config.xml: ' + JSON.stringify(e));
+    }
+}
+
+/**
+ * Reads a preference value from config.xml.
+ * Returns preference value or undefined if it does not exist.
+ * @param {String} preferenceName Preference name to read */
+Config.prototype.getPreferenceValue = function getPreferenceValue(preferenceName) {
+    var preferenceItem = this.preferences && this.preferences.filter(function(item) {
+        return item.attributes.name && item.attributes.name.value === preferenceName;
+    });
+
+    if(preferenceItem && preferenceItem[0] && preferenceItem[0].attributes && preferenceItem[0].attributes.value) {
+        return preferenceItem[0].attributes.value.value;
+    }
+};
+
+exports.readConfig = readConfig;

+ 114 - 0
platforms/browser/www/cordova-js-src/exec.js

@@ -0,0 +1,114 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/*jslint sloppy:true, plusplus:true*/
+/*global require, module, console */
+
+var cordova = require('cordova');
+var execProxy = require('cordova/exec/proxy');
+
+/**
+ * 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 proxy = execProxy.get(service, action);
+
+    args = args || [];
+
+    if (proxy) {
+        
+        var callbackId = service + cordova.callbackId++;
+        
+        if (typeof success === "function" || typeof fail === "function") {
+            cordova.callbacks[callbackId] = {success: success, fail: fail};
+        }
+        try {
+
+            
+
+            // callbackOptions param represents additional optional parameters command could pass back, like keepCallback or
+            // custom callbackId, for example {callbackId: id, keepCallback: true, status: cordova.callbackStatus.JSON_EXCEPTION }
+            var onSuccess = function (result, callbackOptions) {
+                callbackOptions = callbackOptions || {};
+                var callbackStatus;
+                // covering both undefined and null.
+                // strict null comparison was causing callbackStatus to be undefined
+                // and then no callback was called because of the check in cordova.callbackFromNative
+                // see CB-8996 Mobilespec app hang on windows
+                if (callbackOptions.status !== undefined && callbackOptions.status !== null) {
+                    callbackStatus = callbackOptions.status;
+                }
+                else {
+                    callbackStatus = cordova.callbackStatus.OK;
+                }
+                cordova.callbackSuccess(callbackOptions.callbackId || callbackId,
+                    {
+                        status: callbackStatus,
+                        message: result,
+                        keepCallback: callbackOptions.keepCallback || false
+                    });
+            };
+            var onError = function (err, callbackOptions) {
+                callbackOptions = callbackOptions || {};
+                var callbackStatus;
+                // covering both undefined and null.
+                // strict null comparison was causing callbackStatus to be undefined
+                // and then no callback was called because of the check in cordova.callbackFromNative
+                // note: status can be 0
+                if (callbackOptions.status !== undefined && callbackOptions.status !== null) {
+                    callbackStatus = callbackOptions.status;
+                }
+                else {
+                    callbackStatus = cordova.callbackStatus.OK;
+                }
+                cordova.callbackError(callbackOptions.callbackId || callbackId,
+                {
+                    status: callbackStatus,
+                    message: err,
+                    keepCallback: callbackOptions.keepCallback || false
+                });
+            };
+            proxy(onSuccess, onError, args);
+
+        } catch (e) {
+            console.log("Exception calling native with command :: " + service + " :: " + action  + " ::exception=" + e);
+        }
+    } else {
+
+        console.log("Error: exec proxy not found for :: " + service + " :: " + action);
+        
+        if(typeof fail === "function" ) {
+            fail("Missing Command Error");
+        }
+    }
+};

+ 46 - 0
platforms/browser/www/cordova-js-src/platform.js

@@ -0,0 +1,46 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+module.exports = {
+    id: 'browser',
+    cordovaVersion: '4.2.0', // cordova-js
+
+    bootstrap: function() {
+
+        var modulemapper = require('cordova/modulemapper');
+        var channel = require('cordova/channel');
+
+        modulemapper.clobbers('cordova/exec/proxy', 'cordova.commandProxy');
+
+        channel.onNativeReady.fire();
+
+        document.addEventListener("visibilitychange", function(){
+            if(document.hidden) {
+                channel.onPause.fire();
+            }
+            else {
+                channel.onResume.fire();
+            }
+        });
+
+    // End of bootstrap
+    }
+};

+ 1594 - 0
platforms/browser/www/cordova.js

@@ -0,0 +1,1594 @@
+// Platform: browser
+// d07d9d0989196f1b90fe962ca68f5ceb355c69ec
+/*
+ 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() {
+var PLATFORM_VERSION_BUILD_LABEL = '6.0.0';
+// file: src/scripts/require.js
+
+var require;
+var 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 = {};
+    var SEPARATOR = '.';
+
+    function build (module) {
+        var factory = module.factory;
+        var localRequire = function (id) {
+            var resultantId = id;
+            // Its a relative path, so lop off the last portion and add the id (minus "./")
+            if (id.charAt(0) === '.') {
+                resultantId = module.id.slice(0, module.id.lastIndexOf(SEPARATOR)) + SEPARATOR + id.slice(2);
+            }
+            return require(resultantId);
+        };
+        module.exports = {};
+        delete module.factory;
+        factory(localRequire, 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: src/cordova.js
+define("cordova", function(require, exports, module) {
+
+// Workaround for Windows 10 in hosted environment case
+// http://www.w3.org/html/wg/drafts/html/master/browsers.html#named-access-on-the-window-object
+if (window.cordova && !(window.cordova instanceof HTMLElement)) { // eslint-disable-line no-undef
+    throw new Error('cordova already defined');
+}
+
+var channel = require('cordova/channel');
+var platform = require('cordova/platform');
+
+/**
+ * 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 = {};
+var 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;
+}
+
+/* eslint-disable no-undef */
+var cordova = {
+    define: define,
+    require: require,
+    version: PLATFORM_VERSION_BUILD_LABEL,
+    platformVersion: PLATFORM_VERSION_BUILD_LABEL,
+    platformId: platform.id,
+
+    /* eslint-enable no-undef */
+
+    /**
+     * 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) {
+        cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
+    },
+
+    /**
+     * 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.
+        cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
+    },
+
+    /**
+     * Called by native code when returning the result from an action.
+     */
+    callbackFromNative: function (callbackId, isSuccess, status, args, keepCallback) {
+        try {
+            var callback = cordova.callbacks[callbackId];
+            if (callback) {
+                if (isSuccess && status === cordova.callbackStatus.OK) {
+                    callback.success && callback.success.apply(null, args);
+                } else if (!isSuccess) {
+                    callback.fail && callback.fail.apply(null, args);
+                }
+                /*
+                else
+                    Note, this case is intentionally not caught.
+                    this can happen if isSuccess is true, but callbackStatus is NO_RESULT
+                    which is used to remove a callback from the list without calling the callbacks
+                    typically keepCallback is false in this case
+                */
+                // Clear callback if not expecting any more results
+                if (!keepCallback) {
+                    delete cordova.callbacks[callbackId];
+                }
+            }
+        } catch (err) {
+            var msg = 'Error in ' + (isSuccess ? 'Success' : 'Error') + ' callbackId: ' + callbackId + ' : ' + err;
+            console && console.log && console.log(msg);
+            console && console.log && err.stack && console.log(err.stack);
+            cordova.fireWindowEvent('cordovacallbackerror', { 'message': msg });
+            throw err;
+        }
+    },
+    addConstructor: function (func) {
+        channel.onCordovaReady.subscribe(function () {
+            try {
+                func();
+            } catch (e) {
+                console.log('Failed to run constructor: ' + e);
+            }
+        });
+    }
+};
+
+module.exports = cordova;
+
+});
+
+// file: src/common/argscheck.js
+define("cordova/argscheck", function(require, exports, module) {
+
+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);
+        var cUpper = c.toUpperCase();
+        var 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 unit tests.
+        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: src/common/base64.js
+define("cordova/base64", function(require, exports, module) {
+
+var base64 = exports;
+
+base64.fromArrayBuffer = function (arrayBuffer) {
+    var array = new Uint8Array(arrayBuffer);
+    return uint8ToBase64(array);
+};
+
+base64.toArrayBuffer = function (str) {
+    var decodedStr = typeof atob !== 'undefined' ? atob(str) : Buffer.from(str, 'base64').toString('binary'); // eslint-disable-line no-undef
+    var arrayBuffer = new ArrayBuffer(decodedStr.length);
+    var array = new Uint8Array(arrayBuffer);
+    for (var i = 0, len = decodedStr.length; i < len; i++) {
+        array[i] = decodedStr.charCodeAt(i);
+    }
+    return arrayBuffer;
+};
+
+// ------------------------------------------------------------------------------
+
+/* This code is based on the performance tests at http://jsperf.com/b64tests
+ * This 12-bit-at-a-time algorithm was the best performing version on all
+ * platforms tested.
+ */
+
+var b64_6bit = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+var b64_12bit;
+
+var b64_12bitTable = function () {
+    b64_12bit = [];
+    for (var i = 0; i < 64; i++) {
+        for (var j = 0; j < 64; j++) {
+            b64_12bit[i * 64 + j] = b64_6bit[i] + b64_6bit[j];
+        }
+    }
+    b64_12bitTable = function () { return b64_12bit; };
+    return b64_12bit;
+};
+
+function uint8ToBase64 (rawData) {
+    var numBytes = rawData.byteLength;
+    var output = '';
+    var segment;
+    var table = b64_12bitTable();
+    for (var i = 0; i < numBytes - 2; i += 3) {
+        segment = (rawData[i] << 16) + (rawData[i + 1] << 8) + rawData[i + 2];
+        output += table[segment >> 12];
+        output += table[segment & 0xfff];
+    }
+    if (numBytes - i === 2) {
+        segment = (rawData[i] << 16) + (rawData[i + 1] << 8);
+        output += table[segment >> 12];
+        output += b64_6bit[(segment & 0xfff) >> 6];
+        output += '=';
+    } else if (numBytes - i === 1) {
+        segment = (rawData[i] << 16);
+        output += table[segment >> 12];
+        output += '==';
+    }
+    return output;
+}
+
+});
+
+// file: src/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);
+    var needsProperty = false;
+    try {
+        obj[key] = value;
+    } catch (e) {
+        needsProperty = true;
+    }
+    // Getters can only be overridden by getters.
+    if (needsProperty || 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: src/common/channel.js
+define("cordova/channel", function(require, exports, module) {
+
+var utils = require('cordova/utils');
+var 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.
+ * 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
+ *
+ * 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;
+};
+var 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;
+        var i = len;
+        var 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();
+    },
+    /* eslint-disable no-return-assign */
+    create: function (type) {
+        return channel[type] = new Channel(type, false);
+    },
+    createSticky: function (type) {
+        return channel[type] = new Channel(type, true);
+    },
+    /* eslint-enable no-return-assign */
+    /**
+     * 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 checkSubscriptionArgument (argument) {
+    if (typeof argument !== 'function' && typeof argument.handleEvent !== 'function') {
+        throw new Error(
+            'Must provide a function or an EventListener object ' +
+                'implementing the handleEvent interface.'
+        );
+    }
+}
+
+/**
+ * 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 (eventListenerOrFunction, eventListener) {
+    checkSubscriptionArgument(eventListenerOrFunction);
+    var handleEvent, guid;
+
+    if (eventListenerOrFunction && typeof eventListenerOrFunction === 'object') {
+        // Received an EventListener object implementing the handleEvent interface
+        handleEvent = eventListenerOrFunction.handleEvent;
+        eventListener = eventListenerOrFunction;
+    } else {
+        // Received a function to handle event
+        handleEvent = eventListenerOrFunction;
+    }
+
+    if (this.state === 2) {
+        handleEvent.apply(eventListener || this, this.fireArgs);
+        return;
+    }
+
+    guid = eventListenerOrFunction.observer_guid;
+    if (typeof eventListener === 'object') {
+        handleEvent = utils.close(eventListener, handleEvent);
+    }
+
+    if (!guid) {
+        // First time any channel has seen this subscriber
+        guid = '' + nextGuid++;
+    }
+    handleEvent.observer_guid = guid;
+    eventListenerOrFunction.observer_guid = guid;
+
+    // Don't add the same handler more than once.
+    if (!this.handlers[guid]) {
+        this.handlers[guid] = handleEvent;
+        this.numHandlers++;
+        if (this.numHandlers === 1) {
+            this.onHasSubscribersChange && this.onHasSubscribersChange();
+        }
+    }
+};
+
+/**
+ * Unsubscribes the function with the given guid from the channel.
+ */
+Channel.prototype.unsubscribe = function (eventListenerOrFunction) {
+    checkSubscriptionArgument(eventListenerOrFunction);
+    var handleEvent, guid, handler;
+
+    if (eventListenerOrFunction && typeof eventListenerOrFunction === 'object') {
+        // Received an EventListener object implementing the handleEvent interface
+        handleEvent = eventListenerOrFunction.handleEvent;
+    } else {
+        // Received a function to handle event
+        handleEvent = eventListenerOrFunction;
+    }
+
+    guid = handleEvent.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; // eslint-disable-line no-unused-vars
+    var 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 all automatically loaded JS plugins are loaded and ready.
+// FIXME remove this
+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');
+
+// Channels that must fire before "deviceready" is fired.
+channel.waitForInitialization('onCordovaReady');
+channel.waitForInitialization('onDOMContentLoaded');
+
+module.exports = channel;
+
+});
+
+// file: /Users/erisu/git/apache/cordova/cordova-browser/cordova-js-src/confighelper.js
+define("cordova/confighelper", function(require, exports, module) {
+
+var config;
+
+function Config(xhr) {
+    function loadPreferences(xhr) {
+       var parser = new DOMParser();
+       var doc = parser.parseFromString(xhr.responseText, "application/xml");
+
+       var preferences = doc.getElementsByTagName("preference");
+       return Array.prototype.slice.call(preferences);
+    }
+
+    this.xhr = xhr;
+    this.preferences = loadPreferences(this.xhr);
+}
+
+function readConfig(success, error) {
+    var xhr;
+
+    if(typeof config != 'undefined') {
+        success(config);
+    }
+
+    function fail(msg) {
+        console.error(msg);
+
+        if(error) {
+            error(msg);
+        }
+    }
+
+    var xhrStatusChangeHandler = function() {
+        if (xhr.readyState == 4) {
+            if (xhr.status == 200 || xhr.status == 304 || xhr.status === 0 /* file:// */) {
+                config = new Config(xhr);
+                success(config);
+            }
+            else {
+                fail('[Browser][cordova.js][xhrStatusChangeHandler] Could not XHR config.xml: ' + xhr.statusText);
+            }
+        }
+    };
+
+    xhr = new XMLHttpRequest();
+    xhr.addEventListener("load", xhrStatusChangeHandler);
+
+
+    try {
+        xhr.open("get", "config.xml", true);
+        xhr.send();
+    } catch(e) {
+        fail('[Browser][cordova.js][readConfig] Could not XHR config.xml: ' + JSON.stringify(e));
+    }
+}
+
+/**
+ * Reads a preference value from config.xml.
+ * Returns preference value or undefined if it does not exist.
+ * @param {String} preferenceName Preference name to read */
+Config.prototype.getPreferenceValue = function getPreferenceValue(preferenceName) {
+    var preferenceItem = this.preferences && this.preferences.filter(function(item) {
+        return item.attributes.name && item.attributes.name.value === preferenceName;
+    });
+
+    if(preferenceItem && preferenceItem[0] && preferenceItem[0].attributes && preferenceItem[0].attributes.value) {
+        return preferenceItem[0].attributes.value.value;
+    }
+};
+
+exports.readConfig = readConfig;
+
+});
+
+// file: /Users/erisu/git/apache/cordova/cordova-browser/cordova-js-src/exec.js
+define("cordova/exec", function(require, exports, module) {
+
+/*jslint sloppy:true, plusplus:true*/
+/*global require, module, console */
+
+var cordova = require('cordova');
+var execProxy = require('cordova/exec/proxy');
+
+/**
+ * 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 proxy = execProxy.get(service, action);
+
+    args = args || [];
+
+    if (proxy) {
+        
+        var callbackId = service + cordova.callbackId++;
+        
+        if (typeof success === "function" || typeof fail === "function") {
+            cordova.callbacks[callbackId] = {success: success, fail: fail};
+        }
+        try {
+
+            
+
+            // callbackOptions param represents additional optional parameters command could pass back, like keepCallback or
+            // custom callbackId, for example {callbackId: id, keepCallback: true, status: cordova.callbackStatus.JSON_EXCEPTION }
+            var onSuccess = function (result, callbackOptions) {
+                callbackOptions = callbackOptions || {};
+                var callbackStatus;
+                // covering both undefined and null.
+                // strict null comparison was causing callbackStatus to be undefined
+                // and then no callback was called because of the check in cordova.callbackFromNative
+                // see CB-8996 Mobilespec app hang on windows
+                if (callbackOptions.status !== undefined && callbackOptions.status !== null) {
+                    callbackStatus = callbackOptions.status;
+                }
+                else {
+                    callbackStatus = cordova.callbackStatus.OK;
+                }
+                cordova.callbackSuccess(callbackOptions.callbackId || callbackId,
+                    {
+                        status: callbackStatus,
+                        message: result,
+                        keepCallback: callbackOptions.keepCallback || false
+                    });
+            };
+            var onError = function (err, callbackOptions) {
+                callbackOptions = callbackOptions || {};
+                var callbackStatus;
+                // covering both undefined and null.
+                // strict null comparison was causing callbackStatus to be undefined
+                // and then no callback was called because of the check in cordova.callbackFromNative
+                // note: status can be 0
+                if (callbackOptions.status !== undefined && callbackOptions.status !== null) {
+                    callbackStatus = callbackOptions.status;
+                }
+                else {
+                    callbackStatus = cordova.callbackStatus.OK;
+                }
+                cordova.callbackError(callbackOptions.callbackId || callbackId,
+                {
+                    status: callbackStatus,
+                    message: err,
+                    keepCallback: callbackOptions.keepCallback || false
+                });
+            };
+            proxy(onSuccess, onError, args);
+
+        } catch (e) {
+            console.log("Exception calling native with command :: " + service + " :: " + action  + " ::exception=" + e);
+        }
+    } else {
+
+        console.log("Error: exec proxy not found for :: " + service + " :: " + action);
+        
+        if(typeof fail === "function" ) {
+            fail("Missing Command Error");
+        }
+    }
+};
+
+});
+
+// file: src/common/exec/proxy.js
+define("cordova/exec/proxy", 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: src/common/init.js
+define("cordova/init", function(require, exports, module) {
+
+var channel = require('cordova/channel');
+var cordova = require('cordova');
+var modulemapper = require('cordova/modulemapper');
+var platform = require('cordova/platform');
+var pluginloader = require('cordova/pluginloader');
+var utils = require('cordova/utils');
+
+var platformInitChannelsArray = [channel.onNativeReady, channel.onPluginsReady];
+
+function logUnfiredChannels (arr) {
+    for (var i = 0; i < arr.length; ++i) {
+        if (arr[i].state !== 2) {
+            console.log('Channel not fired: ' + arr[i].type);
+        }
+    }
+}
+
+window.setTimeout(function () {
+    if (channel.onDeviceReady.state !== 2) {
+        console.log('deviceready has not fired after 5 seconds.');
+        logUnfiredChannels(platformInitChannelsArray);
+        logUnfiredChannels(channel.deviceReadyChannelsArray);
+    }
+}, 5000);
+
+// Replace navigator before any modules are required(), to ensure it happens as soon as possible.
+// We replace it so that properties that can't be clobbered can instead be overridden.
+function replaceNavigator (origNavigator) {
+    var CordovaNavigator = function () {};
+    CordovaNavigator.prototype = origNavigator;
+    var newNavigator = new CordovaNavigator();
+    // This work-around really only applies to new APIs that are newer than Function.bind.
+    // Without it, APIs such as getGamepads() break.
+    if (CordovaNavigator.bind) {
+        for (var key in origNavigator) {
+            if (typeof origNavigator[key] === 'function') {
+                newNavigator[key] = origNavigator[key].bind(origNavigator);
+            } else {
+                (function (k) {
+                    utils.defineGetterSetter(newNavigator, key, function () {
+                        return origNavigator[k];
+                    });
+                })(key);
+            }
+        }
+    }
+    return newNavigator;
+}
+
+if (window.navigator) {
+    window.navigator = replaceNavigator(window.navigator);
+}
+
+if (!window.console) {
+    window.console = {
+        log: function () {}
+    };
+}
+if (!window.console.warn) {
+    window.console.warn = function (msg) {
+        this.log('warn: ' + msg);
+    };
+}
+
+// Register pause, resume and deviceready channels as events on document.
+channel.onPause = cordova.addDocumentEventHandler('pause');
+channel.onResume = cordova.addDocumentEventHandler('resume');
+channel.onActivated = cordova.addDocumentEventHandler('activated');
+channel.onDeviceReady = cordova.addStickyDocumentEventHandler('deviceready');
+
+// Listen for DOMContentLoaded and notify our channel subscribers.
+if (document.readyState === 'complete' || document.readyState === 'interactive') {
+    channel.onDOMContentLoaded.fire();
+} else {
+    document.addEventListener('DOMContentLoaded', function () {
+        channel.onDOMContentLoaded.fire();
+    }, false);
+}
+
+// _nativeReady is global variable that the native side can set
+// to signify that the native code is ready. It is a global since
+// it may be called before any cordova JS is ready.
+if (window._nativeReady) {
+    channel.onNativeReady.fire();
+}
+
+modulemapper.clobbers('cordova', 'cordova');
+modulemapper.clobbers('cordova/exec', 'cordova.exec');
+modulemapper.clobbers('cordova/exec', 'Cordova.exec');
+
+// Call the platform-specific initialization.
+platform.bootstrap && platform.bootstrap();
+
+// Wrap in a setTimeout to support the use-case of having plugin JS appended to cordova.js.
+// The delay allows the attached modules to be defined before the plugin loader looks for them.
+setTimeout(function () {
+    pluginloader.load(function () {
+        channel.onPluginsReady.fire();
+    });
+}, 0);
+
+/**
+ * Create all cordova objects once native side is ready.
+ */
+channel.join(function () {
+    modulemapper.mapModules(window);
+
+    platform.initialize && platform.initialize();
+
+    // Fire event to notify that all objects are created
+    channel.onCordovaReady.fire();
+
+    // Fire onDeviceReady event once page has fully loaded, all
+    // constructors have run and cordova info has been received from native
+    // side.
+    channel.join(function () {
+        require('cordova').fireDocumentEvent('deviceready');
+    }, channel.deviceReadyChannelsArray);
+
+}, platformInitChannelsArray);
+
+});
+
+// file: src/common/modulemapper.js
+define("cordova/modulemapper", function(require, exports, module) {
+
+var builder = require('cordova/builder');
+var moduleMap = define.moduleMap; // eslint-disable-line no-undef
+var symbolList;
+var 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);
+};
+
+exports.runs = function (moduleName) {
+    addEntry('r', moduleName, null);
+};
+
+function prepareNamespace (symbolPath, context) {
+    if (!symbolPath) {
+        return context;
+    }
+    var parts = symbolPath.split('.');
+    var cur = context;
+    for (var i = 0, part; part = parts[i]; ++i) { // eslint-disable-line no-cond-assign
+        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 module = require(moduleName);
+        // <runs/>
+        if (strategy === 'r') {
+            continue;
+        }
+        var symbolPath = symbolList[i + 2];
+        var lastDot = symbolPath.lastIndexOf('.');
+        var namespace = symbolPath.substr(0, lastDot);
+        var lastName = symbolPath.substr(lastDot + 1);
+
+        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.reset();
+
+});
+
+// file: /Users/erisu/git/apache/cordova/cordova-browser/cordova-js-src/platform.js
+define("cordova/platform", function(require, exports, module) {
+
+module.exports = {
+    id: 'browser',
+    cordovaVersion: '4.2.0', // cordova-js
+
+    bootstrap: function() {
+
+        var modulemapper = require('cordova/modulemapper');
+        var channel = require('cordova/channel');
+
+        modulemapper.clobbers('cordova/exec/proxy', 'cordova.commandProxy');
+
+        channel.onNativeReady.fire();
+
+        document.addEventListener("visibilitychange", function(){
+            if(document.hidden) {
+                channel.onPause.fire();
+            }
+            else {
+                channel.onResume.fire();
+            }
+        });
+
+    // End of bootstrap
+    }
+};
+
+});
+
+// file: src/common/pluginloader.js
+define("cordova/pluginloader", function(require, exports, module) {
+
+var modulemapper = require('cordova/modulemapper');
+
+// Helper function to inject a <script> tag.
+// Exported for testing.
+exports.injectScript = function (url, onload, onerror) {
+    var script = document.createElement('script');
+    // onload fires even when script fails loads with an error.
+    script.onload = onload;
+    // onerror fires for malformed URLs.
+    script.onerror = onerror;
+    script.src = url;
+    document.head.appendChild(script);
+};
+
+function injectIfNecessary (id, url, onload, onerror) {
+    onerror = onerror || onload;
+    if (id in define.moduleMap) { // eslint-disable-line no-undef
+        onload();
+    } else {
+        exports.injectScript(url, function () {
+            if (id in define.moduleMap) { // eslint-disable-line no-undef
+                onload();
+            } else {
+                onerror();
+            }
+        }, onerror);
+    }
+}
+
+function onScriptLoadingComplete (moduleList, finishPluginLoading) {
+    // Loop through all the plugins and then through their clobbers and merges.
+    for (var i = 0, module; module = moduleList[i]; i++) { // eslint-disable-line no-cond-assign
+        if (module.clobbers && module.clobbers.length) {
+            for (var j = 0; j < module.clobbers.length; j++) {
+                modulemapper.clobbers(module.id, module.clobbers[j]);
+            }
+        }
+
+        if (module.merges && module.merges.length) {
+            for (var k = 0; k < module.merges.length; k++) {
+                modulemapper.merges(module.id, module.merges[k]);
+            }
+        }
+
+        // Finally, if runs is truthy we want to simply require() the module.
+        if (module.runs) {
+            modulemapper.runs(module.id);
+        }
+    }
+
+    finishPluginLoading();
+}
+
+// Handler for the cordova_plugins.js content.
+// See plugman's plugin_loader.js for the details of this object.
+// This function is only called if the really is a plugins array that isn't empty.
+// Otherwise the onerror response handler will just call finishPluginLoading().
+function handlePluginsObject (path, moduleList, finishPluginLoading) {
+    // Now inject the scripts.
+    var scriptCounter = moduleList.length;
+
+    if (!scriptCounter) {
+        finishPluginLoading();
+        return;
+    }
+    function scriptLoadedCallback () {
+        if (!--scriptCounter) {
+            onScriptLoadingComplete(moduleList, finishPluginLoading);
+        }
+    }
+
+    for (var i = 0; i < moduleList.length; i++) {
+        injectIfNecessary(moduleList[i].id, path + moduleList[i].file, scriptLoadedCallback);
+    }
+}
+
+function findCordovaPath () {
+    var path = null;
+    var scripts = document.getElementsByTagName('script');
+    var term = '/cordova.js';
+    for (var n = scripts.length - 1; n > -1; n--) {
+        var src = scripts[n].src.replace(/\?.*$/, ''); // Strip any query param (CB-6007).
+        if (src.indexOf(term) === (src.length - term.length)) {
+            path = src.substring(0, src.length - term.length) + '/';
+            break;
+        }
+    }
+    return path;
+}
+
+// Tries to load all plugins' js-modules.
+// This is an async process, but onDeviceReady is blocked on onPluginsReady.
+// onPluginsReady is fired when there are no plugins to load, or they are all done.
+exports.load = function (callback) {
+    var pathPrefix = findCordovaPath();
+    if (pathPrefix === null) {
+        console.log('Could not find cordova.js script tag. Plugin loading may fail.');
+        pathPrefix = '';
+    }
+    injectIfNecessary('cordova/plugin_list', pathPrefix + 'cordova_plugins.js', function () {
+        var moduleList = require('cordova/plugin_list');
+        handlePluginsObject(pathPrefix, moduleList, callback);
+    }, callback);
+};
+
+});
+
+// file: src/common/urlutil.js
+define("cordova/urlutil", function(require, exports, module) {
+
+/**
+ * For already absolute URLs, returns what is passed in.
+ * For relative URLs, converts them to absolute ones.
+ */
+exports.makeAbsolute = function makeAbsolute (url) {
+    var anchorEl = document.createElement('a');
+    anchorEl.href = url;
+    return anchorEl.href;
+};
+
+});
+
+// file: src/common/utils.js
+define("cordova/utils", function(require, exports, module) {
+
+var utils = exports;
+
+/**
+ * Defines a property getter / setter for obj[key].
+ */
+utils.defineGetterSetter = function (obj, key, getFunc, opt_setFunc) {
+    if (Object.defineProperty) {
+        var desc = {
+            get: getFunc,
+            configurable: true
+        };
+        if (opt_setFunc) {
+            desc.set = opt_setFunc;
+        }
+        Object.defineProperty(obj, key, desc);
+    } else {
+        obj.__defineGetter__(key, getFunc);
+        if (opt_setFunc) {
+            obj.__defineSetter__(key, opt_setFunc);
+        }
+    }
+};
+
+/**
+ * Defines a property getter for obj[key].
+ */
+utils.defineGetter = utils.defineGetterSetter;
+
+utils.arrayIndexOf = function (a, item) {
+    if (a.indexOf) {
+        return a.indexOf(item);
+    }
+    var len = a.length;
+    for (var i = 0; i < len; ++i) {
+        if (a[i] === item) {
+            return i;
+        }
+    }
+    return -1;
+};
+
+/**
+ * Returns whether the item was found in the array.
+ */
+utils.arrayRemove = function (a, item) {
+    var index = utils.arrayIndexOf(a, item);
+    if (index !== -1) {
+        a.splice(index, 1);
+    }
+    return index !== -1;
+};
+
+utils.typeName = function (val) {
+    return Object.prototype.toString.call(val).slice(8, -1);
+};
+
+/**
+ * Returns an indication of whether the argument is an array or not
+ */
+utils.isArray = Array.isArray ||
+                function (a) { return utils.typeName(a) === 'Array'; };
+
+/**
+ * Returns an indication of whether the argument is a Date or not
+ */
+utils.isDate = function (d) {
+    return (d instanceof Date);
+};
+
+/**
+ * Does a deep clone of the object.
+ */
+utils.clone = function (obj) {
+    if (!obj || typeof obj === 'function' || utils.isDate(obj) || typeof obj !== 'object') {
+        return obj;
+    }
+
+    var retVal, i;
+
+    if (utils.isArray(obj)) {
+        retVal = [];
+        for (i = 0; i < obj.length; ++i) {
+            retVal.push(utils.clone(obj[i]));
+        }
+        return retVal;
+    }
+
+    retVal = {};
+    for (i in obj) {
+        // https://issues.apache.org/jira/browse/CB-11522 'unknown' type may be returned in
+        // custom protocol activation case on Windows Phone 8.1 causing "No such interface supported" exception
+        // on cloning.
+        if ((!(i in retVal) || retVal[i] !== obj[i]) && typeof obj[i] !== 'undefined' && typeof obj[i] !== 'unknown') { // eslint-disable-line valid-typeof
+            retVal[i] = utils.clone(obj[i]);
+        }
+    }
+    return retVal;
+};
+
+/**
+ * Returns a wrapped version of the function
+ */
+utils.close = function (context, func, params) {
+    return function () {
+        var args = params || arguments;
+        return func.apply(context, args);
+    };
+};
+
+// ------------------------------------------------------------------------------
+function UUIDcreatePart (length) {
+    var uuidpart = '';
+    for (var i = 0; i < length; i++) {
+        var uuidchar = parseInt((Math.random() * 256), 10).toString(16);
+        if (uuidchar.length === 1) {
+            uuidchar = '0' + uuidchar;
+        }
+        uuidpart += uuidchar;
+    }
+    return uuidpart;
+}
+
+/**
+ * Create a UUID
+ */
+utils.createUUID = function () {
+    return UUIDcreatePart(4) + '-' +
+        UUIDcreatePart(2) + '-' +
+        UUIDcreatePart(2) + '-' +
+        UUIDcreatePart(2) + '-' +
+        UUIDcreatePart(6);
+};
+
+/**
+ * Extends a child object from a parent object using classical inheritance
+ * pattern.
+ */
+utils.extend = (function () {
+    // proxy used to establish prototype chain
+    var F = function () {};
+    // extend Child from Parent
+    return function (Child, Parent) {
+
+        F.prototype = Parent.prototype;
+        Child.prototype = new F();
+        Child.__super__ = Parent.prototype;
+        Child.prototype.constructor = Child;
+    };
+}());
+
+/**
+ * Alerts a message in any available way: alert or console.log.
+ */
+utils.alert = function (msg) {
+    if (window.alert) {
+        window.alert(msg);
+    } else if (console && console.log) {
+        console.log(msg);
+    }
+};
+
+});
+
+window.cordova = require('cordova');
+// file: src/scripts/bootstrap.js
+
+require('cordova/init');
+
+})();

+ 10 - 0
platforms/browser/www/cordova_plugins.js

@@ -0,0 +1,10 @@
+cordova.define('cordova/plugin_list', function(require, exports, module) {
+module.exports = [];
+module.exports.metadata = 
+// TOP OF METADATA
+{
+    "cordova-plugin-whitelist": "1.3.4",
+    "cordova-plugin-webpack": "0.4.7"
+}
+// BOTTOM OF METADATA
+});

+ 107 - 0
platforms/browser/www/css/index.css

@@ -0,0 +1,107 @@
+/*
+ * 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-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -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-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    font-family: system-ui, -apple-system, -apple-system-font, 'Segoe UI', 'Roboto', sans-serif;
+    font-size:12px;
+    height:100vh;
+    margin:0px;
+    padding:0px;
+    /* Padding to avoid the "unsafe" areas behind notches in the screen */
+    padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-right, 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;
+}

BIN
platforms/browser/www/favicon.ico


BIN
platforms/browser/www/img/logo.png


+ 49 - 0
platforms/browser/www/index.html

@@ -0,0 +1,49 @@
+<!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>
+        <!--
+        Customize this policy to fit your own app's needs. For more guidance, see:
+            https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
+        Some notes:
+            * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
+            * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
+            * Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
+                * Enable inline JS: add 'unsafe-inline' to default-src
+        -->
+        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
+        <meta name="format-detection" content="telephone=no">
+        <meta name="msapplication-tap-highlight" content="no">
+        <meta name="viewport" content="initial-scale=1, width=device-width, viewport-fit=cover">
+        <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.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+    </body>
+</html>

+ 46 - 0
platforms/browser/www/js/index.js

@@ -0,0 +1,46 @@
+/*
+ * 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() {
+        document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
+    },
+
+    // deviceready Event Handler
+    //
+    // Bind any cordova events here. Common events are:
+    // 'pause', 'resume', etc.
+    onDeviceReady: function() {
+        this.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);
+    }
+};
+
+app.initialize();

+ 11 - 0
platforms/browser/www/manifest.json

@@ -0,0 +1,11 @@
+{
+  "background_color": "#FFF",
+  "display": "standalone",
+  "name": "twelve-heroes",
+  "short_name": "twelve-heroes",
+  "version": "com.jojo.twelve-heroes",
+  "description": "A sample Apache Cordova application that responds to the deviceready event.",
+  "author": "Apache Cordova Team",
+  "icons": [],
+  "start_url": "index.html"
+}

+ 18 - 0
plugins/browser.json

@@ -0,0 +1,18 @@
+{
+  "prepare_queue": {
+    "installed": [],
+    "uninstalled": []
+  },
+  "config_munge": {
+    "files": {}
+  },
+  "installed_plugins": {
+    "cordova-plugin-whitelist": {
+      "PACKAGE_NAME": "com.jojo.twelve-heroes"
+    },
+    "cordova-plugin-webpack": {
+      "PACKAGE_NAME": "com.jojo.twelve-heroes"
+    }
+  },
+  "dependent_plugins": {}
+}

+ 3 - 0
plugins/cordova-plugin-webpack/.eslintignore

@@ -0,0 +1,3 @@
+**/node_modules
+scripts/
+!.*.js

+ 35 - 0
plugins/cordova-plugin-webpack/.eslintrc.js

@@ -0,0 +1,35 @@
+module.exports = {
+  root: true,
+  env: {
+    node: true,
+    browser: true,
+  },
+  extends: ['airbnb-base', 'plugin:prettier/recommended'],
+  plugins: ['@typescript-eslint'],
+  parser: '@typescript-eslint/parser',
+  parserOptions: {
+    sourceType: 'module',
+    project: './tsconfig.json',
+    createDefaultProgram: true,
+  },
+  rules: {
+    'prettier/prettier': [
+      'error',
+      {
+        singleQuote: true,
+        trailingComma: 'all',
+      },
+    ],
+    'no-unused-vars': 'off',
+    '@typescript-eslint/no-unused-vars': 'error',
+    'no-useless-constructor': 'off',
+    '@typescript-eslint/no-useless-constructor': 'error',
+  },
+  settings: {
+    'import/resolver': {
+      node: {
+        extensions: ['.js', '.ts'],
+      },
+    },
+  },
+};

+ 8 - 0
plugins/cordova-plugin-webpack/.vscode/settings.json

@@ -0,0 +1,8 @@
+{
+  // "editor.formatOnSave": false,
+  "eslint.autoFixOnSave": true,
+  "eslint.validate": [
+    "javascript",
+    {"language": "typescript", "autoFix": true },
+  ]
+}

+ 76 - 0
plugins/cordova-plugin-webpack/CODE_OF_CONDUCT.md

@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at kotarella1110@gmail.com. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq

+ 7 - 0
plugins/cordova-plugin-webpack/CONTRIBUTING.md

@@ -0,0 +1,7 @@
+# Contributing
+
+1. Fork it (https://github.com/kotarella1110/cordova-plugin-webpack/fork)
+1. Create your feature branch (`git checkout -b feature/fooBar`)
+1. Commit your changes (`npm run commit`)
+1. Push to the branch (`git push origin feature/fooBar`)
+1. Create a new Pull Request

+ 201 - 0
plugins/cordova-plugin-webpack/LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2018 Kotaro Sugawara
+
+   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.

+ 241 - 0
plugins/cordova-plugin-webpack/README.md

@@ -0,0 +1,241 @@
+# cordova-plugin-webpack
+
+[![npm version](https://badge.fury.io/js/cordova-plugin-webpack.svg)](https://badge.fury.io/js/cordova-plugin-webpack)
+[![Downloads](https://img.shields.io/npm/dm/cordova-plugin-webpack.svg)](https://www.npmjs.com/package/cordova-plugin-webpack)
+[![dependencies Status](https://david-dm.org/kotarella1110/cordova-plugin-webpack/status.svg)](https://david-dm.org/kotarella1110/cordova-plugin-webpack)
+[![devDependencies Status](https://david-dm.org/kotarella1110/cordova-plugin-webpack/dev-status.svg)](https://david-dm.org/kotarella1110/cordova-plugin-webpack?type=dev)
+[![Maintainability](https://api.codeclimate.com/v1/badges/f51fd5b6e3c7f43649c2/maintainability)](https://codeclimate.com/github/kotarella1110/cordova-plugin-webpack/maintainability)
+[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
+[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/kotarella1110/cordova-plugin-webpack/issues)
+
+This plugin integrates [webpack](https://webpack.js.org "webpack") into your Cordova workflow.
+
+## Motivation
+
+Module bundlers such as webpack are essential for SPA (Single Page Application) development. Unfortunately, the Cordova workflow does not integrate webpack as a standard feature.
+
+Simply install this plugin to easily integrate webpack into your Cordova workflow.
+
+## Demo
+
+![Demo](./media/cordova-plugin-webpack-demo.gif)
+
+## Features
+
+* Ability to have build scripts by webpack when you build or run Cordova app
+* Ability to have LiveReload ([Hot Module Replacement](https://webpack.js.org/concepts/hot-module-replacement "Hot Module Replacement | webpack")) run by Webpack Dev Server when you’re testing on a device or emulator for Android and iOS
+
+## Supported Platforms
+
+* Browser
+* Android
+* iOS
+
+## Installation
+
+```shell
+$ cordova plugin add cordova-plugin-webpack
+```
+
+## CLI Reference
+
+### Syntax
+
+```shell
+$ cordova { prepare | platform add | build | run } [<platform> [...]]
+    [-- [--webpackConfig <webpackConfig> | --livereload]]
+```
+
+| Option | Description | Default | Aliases |
+|--------|-------------|---------|---------|
+| `--webpackConfig` | Path to a webpack configuration file | `webpack.config.js` or `webpackfile.js` in your project root directory. | `-w` |
+| `--livereload` | Enables LiveReload (HMR) | `false` | `-l` |
+
+### Examples
+
+#### Build
+
+**Before preparing** your Cordova app, build scripts by webpack.
+
+```shell
+$ cordova prepare
+$ cordova build -- --webpackConfig path/to/dir/webpack.config.js
+```
+
+#### Live Reload (HMR)
+
+**After preparing** your Cordova app, run LiveReload by Webpack Dev Server.
+
+```shell
+$ cordova prepare -- --livereload
+$ cordova run -- -w path/to/dir/webpack.config.babel.js -l
+```
+
+## Usage
+
+1. [Add this plugin](#Installation)
+
+2. Create a webpack configuration file (`webpack.config.js`) in your project root folder
+
+    ```js
+    const path = require('path');
+
+    module.exports = {
+      mode: 'development',
+      entry: './src/index.js',
+      output: {
+        path: path.resolve(__dirname, 'www'),
+        filename: 'index.bundle.js',
+      },
+      devtool: 'inline-source-map',
+    };
+    ```
+
+3. Execute the commands
+
+    ```shell
+    $ cordova build
+    $ cordova run -- --livereload
+    ```
+
+<details>
+<summary>For more information...</summary>
+
+1. Create a Cordova app
+
+    ```shell
+    $ cordova create cordova-plugin-webpack-example cordova.plugin.webpack.example CordovaPluginWebpackExample
+    ```
+
+2. Add platforms
+
+    ```shell
+    $ cd cordova-plugin-webpack-example
+    $ cordova platform add android ios
+    ```
+
+3. [Add this plugin](#Installation)
+
+4. Create a JavaScript file ([entry point](https://webpack.js.org/concepts/entry-points/ "entry points"))
+
+    ```shell
+    $ mkdir src
+    $ mv www/js/index.js src/index.js
+    ```
+
+5. Create a webpack configuration file (`webpack.config.js`) in your project root folder
+
+    ```js
+    const path = require('path');
+
+    module.exports = {
+      mode: 'development',
+      entry: './src/index.js',
+      output: {
+        path: path.resolve(__dirname, 'www'),
+        filename: 'index.bundle.js',
+      },
+      devtool: 'inline-source-map',
+    };
+    ```
+
+6. Fix a HTML file (`www/index.html`)
+
+    ```diff
+    -         <script type="text/javascript" src="js/index.js"></script>
+    +         <script type="text/javascript" src="index.bundle.js"></script>
+    ```
+
+7. Execute the commands
+
+    ```shell
+    $ cordova build
+    $ cordova run -- --livereload
+    ```
+
+</details>
+
+---
+
+**NOTE**
+
+Starting with Android 9 (API level 28), cleartext support is disabled by default. Therefore, LiveReload does not work on Android 9.0 and higher devices and emulators. Also see [this issue](https://github.com/kotarella1110/cordova-plugin-webpack/issues/9#issuecomment-495048614).
+
+To resolve this, you must modify your `config.xml` file to enable cleartext support.
+
+1. Add `xmlns:android="http://schemas.android.com/apk/res/android"` in `widget` root element
+
+    ```xml
+    <widget id="cordova.plugin.webpack.example" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    ```
+
+2. Enable `android:usesCleartextTraffic` attribute in `application` element
+
+    ```xml
+    <platform name="android">
+        <edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application">
+            <application android:usesCleartextTraffic="true" />
+        </edit-config>
+    </platform>
+    ```
+
+---
+
+## Custom webpack configuration
+
+Basically, it works according to your webpack configuration file.
+If you want to custom webpack configuration, modify your `webpack.config.js` file.
+
+```js
+...
+module.exports = {
+  ...
+  mode: 'production',
+  entry: './src/index.js',
+  output: {
+    path: path.resolve(__dirname, 'www'),
+    filename: 'bundle.js',
+  },
+  plugins: [
+    new HtmlWebpackPlugin(),
+  ],
+  ...
+  devServer: {
+    contentBase: path.join(__dirname, 'public'),
+    host: 'localhost',
+    port: '8000',
+    hot: false,
+  },
+  ...
+};
+```
+
+| Option | Default |
+|--------|---------|
+| `devServer.contentBase`  | `www` |
+| `devServer.historyApiFallBack` | `true` |
+| `devServer.host` | `0.0.0.0` |
+| `devServer.port` | `8080` |
+| `devServer.watchContentBase` | `true` |
+| `devServer.hot` | `true` |
+
+For example, if you want page reloading (LiveReload) instead of hot module reloading (HMR), specify the `devServer.hot` option as `false`.
+
+```js
+...
+module.exports = {
+  ...
+  devServer: {
+    hot: false,
+  },
+  ...
+};
+```
+
+## Contribute
+
+Contributions are always welcome! Please read the [contributing](./CONTRIBUTING.md) first.
+
+## License
+
+[Apache-2.0](./LICENSE) © [Kotaro Sugawara](https://twitter.com/kotarella1110)

+ 1 - 0
plugins/cordova-plugin-webpack/commitlint.config.js

@@ -0,0 +1 @@
+module.exports = { extends: ['@commitlint/config-conventional'] };

+ 4 - 0
plugins/cordova-plugin-webpack/jest.config.js

@@ -0,0 +1,4 @@
+module.exports = {
+  preset: 'ts-jest',
+  testEnvironment: 'node',
+};

BIN
plugins/cordova-plugin-webpack/media/cordova-plugin-webpack-demo.gif


+ 146 - 0
plugins/cordova-plugin-webpack/package.json

@@ -0,0 +1,146 @@
+{
+  "_from": "cordova-plugin-webpack",
+  "_id": "cordova-plugin-webpack@0.4.7",
+  "_inBundle": false,
+  "_integrity": "sha512-9lxEojdBgKrWgXYIqxehyUZGUVNFFAR1yd8OhZT2tWdI3v6GMXGPYIT2JwMCh8XEwy3EIT1+JeMzZhfhvdf9jQ==",
+  "_location": "/cordova-plugin-webpack",
+  "_phantomChildren": {
+    "camelcase": "5.3.1",
+    "decamelize": "1.2.0",
+    "detect-file": "1.0.0",
+    "is-glob": "4.0.1",
+    "picomatch": "2.0.7",
+    "resolve-dir": "1.0.1"
+  },
+  "_requested": {
+    "type": "tag",
+    "registry": true,
+    "raw": "cordova-plugin-webpack",
+    "name": "cordova-plugin-webpack",
+    "escapedName": "cordova-plugin-webpack",
+    "rawSpec": "",
+    "saveSpec": null,
+    "fetchSpec": "latest"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://registry.npmjs.org/cordova-plugin-webpack/-/cordova-plugin-webpack-0.4.7.tgz",
+  "_shasum": "2d9b9313fd4a8250ca33abb2730736c9216dce5e",
+  "_spec": "cordova-plugin-webpack",
+  "_where": "/home/dom1.taus.us.thales/t0124938/project/twelve-heroes",
+  "author": {
+    "name": "Kotaro Sugawara"
+  },
+  "bugs": {
+    "url": "https://github.com/kotarella1110/cordova-plugin-webpack/issues"
+  },
+  "bundleDependencies": false,
+  "cordova": {
+    "id": "cordova-plugin-webpack",
+    "platforms": [
+      "android",
+      "ios"
+    ]
+  },
+  "dependencies": {
+    "@types/elementtree": "^0.1.0",
+    "@types/findup-sync": "^2.0.2",
+    "@types/interpret": "^1.1.1",
+    "@types/ip": "^1.1.0",
+    "@types/node": "^12.7.5",
+    "@types/react-dev-utils": "^9.0.1",
+    "@types/rechoir": "^0.6.1",
+    "@types/webpack": "^4.39.1",
+    "@types/webpack-dev-server": "^3.1.7",
+    "@types/yargs-parser": "^13.1.0",
+    "cordova-common": "^3.2.0",
+    "create-html": "^4.1.0",
+    "csp-parse": "0.0.2",
+    "current-device": "^0.8.2",
+    "elementtree": "^0.1.7",
+    "express": "^4.17.1",
+    "findup-sync": "^4.0.0",
+    "glob": "^7.1.4",
+    "interpret": "^1.2.0",
+    "ip": "^1.1.5",
+    "is-ua-webview": "^1.0.1",
+    "react-dev-utils": "^9.0.3",
+    "rechoir": "^0.7.0",
+    "source-map-support": "^0.5.13",
+    "webpack": "^4.40.2",
+    "webpack-dev-server": "^3.8.1",
+    "webpack-inject-plugin": "^1.5.3",
+    "yargs-parser": "^14.0.0"
+  },
+  "deprecated": false,
+  "description": "Cordova Webpack Plugin",
+  "devDependencies": {
+    "@commitlint/cli": "^8.2.0",
+    "@commitlint/config-conventional": "^8.2.0",
+    "@commitlint/prompt-cli": "^8.2.0",
+    "@types/jest": "^24.0.18",
+    "@typescript-eslint/eslint-plugin": "^2.3.0",
+    "@typescript-eslint/parser": "^2.3.0",
+    "clean-webpack-plugin": "^3.0.0",
+    "cordova-set-version": "^8.1.3",
+    "eslint": "^6.4.0",
+    "eslint-config-airbnb-base": "^14.0.0",
+    "eslint-config-prettier": "^6.3.0",
+    "eslint-plugin-import": "^2.18.2",
+    "eslint-plugin-prettier": "^3.1.1",
+    "husky": "^3.0.5",
+    "install": "^0.13.0",
+    "jest": "^24.9.0",
+    "lint-staged": "^9.2.5",
+    "npm-check": "^5.9.0",
+    "prettier": "^1.18.2",
+    "rimraf": "^3.0.0",
+    "ts-jest": "^24.1.0",
+    "ts-loader": "^6.1.0",
+    "ts-node": "^8.4.1",
+    "typescript": "^3.6.3",
+    "webpack-cli": "^3.3.9"
+  },
+  "homepage": "https://github.com/kotarella1110/cordova-plugin-webpack#readme",
+  "husky": {
+    "hooks": {
+      "pre-commit": "lint-staged",
+      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
+    }
+  },
+  "keywords": [
+    "cordova",
+    "webpack",
+    "ecosystem:cordova",
+    "cordova-browser",
+    "cordova-android",
+    "cordova-ios"
+  ],
+  "license": "Apache-2.0",
+  "lint-staged": {
+    "*.{js,ts}": [
+      "npm run lint:fix",
+      "git add"
+    ]
+  },
+  "name": "cordova-plugin-webpack",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/kotarella1110/cordova-plugin-webpack.git"
+  },
+  "scripts": {
+    "build": "rimraf scripts/* && tsc && webpack",
+    "commit": "commit",
+    "lint": "eslint '**/*.{js,ts}'",
+    "lint:fix": "npm run lint -- --fix",
+    "precommit": "lint-staged",
+    "prepare": "npm run build",
+    "test": "jest --watch",
+    "test:coverage": "jest --coverage",
+    "upgrade-interactive": "npm-check --update",
+    "version": "cordova-set-version plugin.xml && git add plugin.xml"
+  },
+  "version": "0.4.7"
+}

+ 10 - 0
plugins/cordova-plugin-webpack/plugin.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<plugin id="cordova-plugin-webpack" version="0.4.7" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
+  <name>Webpack</name>
+  <description>Cordova Webpack Plugin</description>
+  <author>Kotaro Sugawara</author>
+  <keywords>cordova,webpack</keywords>
+  <license>Apache 2.0</license>
+  <hook src="scripts/webpackCompile.js" type="before_prepare"/>
+  <hook src="scripts/webpackServe.js" type="after_prepare"/>
+</plugin>

+ 5 - 0
plugins/cordova-plugin-webpack/renovate.json

@@ -0,0 +1,5 @@
+{
+  "extends": [
+    "config:base"
+  ]
+}

+ 52 - 0
plugins/cordova-plugin-webpack/scripts/utils/CordovaConfigParser.js

@@ -0,0 +1,52 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = function (d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+require("source-map-support/register");
+var cordova_common_1 = require("cordova-common");
+var elementtree_1 = __importDefault(require("elementtree"));
+var CordovaConfigParser = (function (_super) {
+    __extends(CordovaConfigParser, _super);
+    function CordovaConfigParser(path) {
+        var _this = _super.call(this, path) || this;
+        _this.path = path;
+        return _this;
+    }
+    CordovaConfigParser.prototype.findOrCreate = function (name) {
+        var ret = this.doc.find(name);
+        if (!ret) {
+            ret = elementtree_1.default.Element(name);
+            this.doc.getroot().append(ret);
+        }
+        return ret;
+    };
+    CordovaConfigParser.prototype.content = function () {
+        var el = this.doc.find('content');
+        return el.attrib.src;
+    };
+    CordovaConfigParser.prototype.setContent = function (src) {
+        var el = this.doc.find('content');
+        el.attrib.src = src;
+    };
+    CordovaConfigParser.prototype.setElement = function (name, attributes) {
+        var el = this.findOrCreate(name);
+        el.attrib = attributes;
+    };
+    return CordovaConfigParser;
+}(cordova_common_1.ConfigParser));
+exports.default = CordovaConfigParser;
+//# sourceMappingURL=CordovaConfigParser.js.map

+ 1 - 0
plugins/cordova-plugin-webpack/scripts/utils/CordovaConfigParser.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"CordovaConfigParser.js","sourceRoot":"","sources":["../../src/utils/CordovaConfigParser.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,uCAAqC;AACrC,iDAA8C;AAC9C,4DAA6B;AAE7B;IAAkC,uCAAY;IAC5C,6BAAmB,IAAY;QAA/B,YACE,kBAAM,IAAI,CAAC,SACZ;QAFkB,UAAI,GAAJ,IAAI,CAAQ;;IAE/B,CAAC;IAEO,0CAAY,GAApB,UAAqB,IAAY;QAC/B,IAAI,GAAG,GAAe,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG,EAAE;YACR,GAAG,GAAG,qBAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;SAChC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,qCAAO,GAAP;QACE,IAAM,EAAE,GAAe,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC;IACvB,CAAC;IAED,wCAAU,GAAV,UAAW,GAAW;QACpB,IAAM,EAAE,GAAe,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAChD,EAAE,CAAC,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC;IACtB,CAAC;IAED,wCAAU,GAAV,UAAW,IAAY,EAAE,UAAqC;QAC5D,IAAM,EAAE,GAAe,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAC/C,EAAE,CAAC,MAAM,GAAG,UAAU,CAAC;IACzB,CAAC;IACH,0BAAC;AAAD,CAAC,AA5BD,CAAkC,6BAAY,GA4B7C;AAED,kBAAe,mBAAmB,CAAC"}

+ 33 - 0
plugins/cordova-plugin-webpack/scripts/utils/webpackHelpers.js

@@ -0,0 +1,33 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+var path_1 = __importDefault(require("path"));
+var interpret_1 = __importDefault(require("interpret"));
+var rechoir_1 = __importDefault(require("rechoir"));
+var findup_sync_1 = __importDefault(require("findup-sync"));
+exports.defaultWebpackConfigPath = function (cwd) {
+    var extensions = Object.keys(interpret_1.default.extensions);
+    var defaultConfigFileNames = ['webpack.config', 'webpackfile'];
+    var configFileRegExp = "(" + defaultConfigFileNames.join('|') + ")(" + extensions.join('|') + ")";
+    var configPath = findup_sync_1.default(configFileRegExp, {
+        cwd: cwd,
+    }) || path_1.default.join(cwd, 'webpack.config.js');
+    return configPath;
+};
+exports.webpackConfig = function (cwd, configPath) {
+    var resolvedConfigPath = (function () {
+        if (!configPath) {
+            return exports.defaultWebpackConfigPath(cwd);
+        }
+        if (path_1.default.isAbsolute(configPath)) {
+            return path_1.default.resolve(configPath);
+        }
+        return path_1.default.resolve(cwd, configPath);
+    })();
+    rechoir_1.default.prepare(interpret_1.default.extensions, resolvedConfigPath);
+    var config = require(resolvedConfigPath);
+    return config.default || config;
+};
+//# sourceMappingURL=webpackHelpers.js.map

+ 1 - 0
plugins/cordova-plugin-webpack/scripts/utils/webpackHelpers.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"webpackHelpers.js","sourceRoot":"","sources":["../../src/utils/webpackHelpers.ts"],"names":[],"mappings":";;;;;AAAA,8CAAwB;AACxB,wDAAkC;AAClC,oDAA8B;AAC9B,4DAAiC;AAGpB,QAAA,wBAAwB,GAAG,UAAC,GAAW;IAClD,IAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,mBAAS,CAAC,UAAU,CAAC,CAAC;IACrD,IAAM,sBAAsB,GAAG,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;IACjE,IAAM,gBAAgB,GAAG,MAAI,sBAAsB,CAAC,IAAI,CACtD,GAAG,CACJ,UAAK,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,MAAG,CAAC;IAC9B,IAAM,UAAU,GACd,qBAAM,CAAC,gBAAgB,EAAE;QACvB,GAAG,KAAA;KACJ,CAAC,IAAI,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;IAE5C,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAEW,QAAA,aAAa,GAAG,UAAC,GAAW,EAAE,UAAmB;IAC5D,IAAM,kBAAkB,GAAG,CAAC;QAC1B,IAAI,CAAC,UAAU,EAAE;YACf,OAAO,gCAAwB,CAAC,GAAG,CAAC,CAAC;SACtC;QACD,IAAI,cAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE;YAC/B,OAAO,cAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;SACjC;QACD,OAAO,cAAI,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACvC,CAAC,CAAC,EAAE,CAAC;IAGL,iBAAO,CAAC,OAAO,CAAC,mBAAS,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;IAE1D,IAAM,MAAM,GAGR,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAEhC,OAAO,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;AAClC,CAAC,CAAC"}

+ 89 - 0
plugins/cordova-plugin-webpack/scripts/webpackCompile.js

@@ -0,0 +1,89 @@
+"use strict";
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+    function verb(n) { return function (v) { return step([n, v]); }; }
+    function step(op) {
+        if (f) throw new TypeError("Generator is already executing.");
+        while (_) try {
+            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+            if (y = 0, t) op = [op[0] & 2, t.value];
+            switch (op[0]) {
+                case 0: case 1: t = op; break;
+                case 4: _.label++; return { value: op[1], done: false };
+                case 5: _.label++; y = op[1]; op = [0]; continue;
+                case 7: op = _.ops.pop(); _.trys.pop(); continue;
+                default:
+                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+                    if (t[2]) _.ops.pop();
+                    _.trys.pop(); continue;
+            }
+            op = body.call(thisArg, _);
+        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+    }
+};
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
+    result["default"] = mod;
+    return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+require("source-map-support/register");
+var yargs_parser_1 = __importDefault(require("yargs-parser"));
+var webpack_1 = __importDefault(require("webpack"));
+var webpackHelpers = __importStar(require("./utils/webpackHelpers"));
+module.exports = function (ctx) { return __awaiter(void 0, void 0, void 0, function () {
+    var platforms, argv, customWebpackConfig, compiler;
+    return __generator(this, function (_a) {
+        switch (_a.label) {
+            case 0:
+                platforms = ['browser', 'android', 'ios'];
+                if (!platforms.some(function (platform) { return ctx.opts.platforms.includes(platform); })) {
+                    return [2];
+                }
+                if (!ctx.opts.options || !ctx.opts.options.argv) {
+                    return [2];
+                }
+                argv = yargs_parser_1.default(ctx.opts.options.argv.join(' '));
+                if (argv.livereload || argv.l) {
+                    return [2];
+                }
+                customWebpackConfig = webpackHelpers.webpackConfig(ctx.opts.projectRoot, argv.webpackConfig || argv.w);
+                compiler = webpack_1.default(customWebpackConfig);
+                return [4, new Promise(function (resolve, reject) {
+                        compiler.run(function (err, stats) {
+                            if (err) {
+                                reject(err);
+                            }
+                            console.log(stats.toString({
+                                chunks: false,
+                                colors: true,
+                            }));
+                            resolve();
+                        });
+                    })];
+            case 1:
+                _a.sent();
+                return [2];
+        }
+    });
+}); };
+//# sourceMappingURL=webpackCompile.js.map

+ 1 - 0
plugins/cordova-plugin-webpack/scripts/webpackCompile.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"webpackCompile.js","sourceRoot":"","sources":["../src/webpackCompile.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAqC;AACrC,8DAAqC;AACrC,oDAA8B;AAC9B,qEAAyD;AAEzD,MAAM,CAAC,OAAO,GAAG,UAAO,GAAQ;;;;;gBACxB,SAAS,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,CAAU,CAAC;gBACzD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,UAAA,QAAQ,IAAI,OAAA,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAArC,CAAqC,CAAC,EAAE;oBACtE,WAAO;iBACR;gBAED,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;oBAC/C,WAAO;iBACR;gBAEK,IAAI,GAAG,sBAAS,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBACxD,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC,EAAE;oBAC7B,WAAO;iBACR;gBAEK,mBAAmB,GAA0B,cAAc,CAAC,aAAa,CAC7E,GAAG,CAAC,IAAI,CAAC,WAAW,EACpB,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,CAAC,CAC7B,CAAC;gBACI,QAAQ,GAAG,iBAAO,CAAC,mBAAmB,CAAC,CAAC;gBAC9C,WAAM,IAAI,OAAO,CAAC,UAAC,OAAO,EAAE,MAAM;wBAChC,QAAQ,CAAC,GAAG,CAAC,UAAC,GAAG,EAAE,KAAK;4BACtB,IAAI,GAAG,EAAE;gCACP,MAAM,CAAC,GAAG,CAAC,CAAC;6BACb;4BACD,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,QAAQ,CAAC;gCACb,MAAM,EAAE,KAAK;gCACb,MAAM,EAAE,IAAI;6BACb,CAAC,CACH,CAAC;4BACF,OAAO,EAAE,CAAC;wBACZ,CAAC,CAAC,CAAC;oBACL,CAAC,CAAC,EAAA;;gBAbF,SAaE,CAAC;;;;KACJ,CAAC"}

+ 171 - 0
plugins/cordova-plugin-webpack/scripts/webpackServe.js

@@ -0,0 +1,171 @@
+"use strict";
+var __assign = (this && this.__assign) || function () {
+    __assign = Object.assign || function(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+                t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+    function verb(n) { return function (v) { return step([n, v]); }; }
+    function step(op) {
+        if (f) throw new TypeError("Generator is already executing.");
+        while (_) try {
+            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+            if (y = 0, t) op = [op[0] & 2, t.value];
+            switch (op[0]) {
+                case 0: case 1: t = op; break;
+                case 4: _.label++; return { value: op[1], done: false };
+                case 5: _.label++; y = op[1]; op = [0]; continue;
+                case 7: op = _.ops.pop(); _.trys.pop(); continue;
+                default:
+                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+                    if (t[2]) _.ops.pop();
+                    _.trys.pop(); continue;
+            }
+            op = body.call(thisArg, _);
+        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+    }
+};
+var __spreadArrays = (this && this.__spreadArrays) || function () {
+    for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
+    for (var r = Array(s), k = 0, i = 0; i < il; i++)
+        for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
+            r[k] = a[j];
+    return r;
+};
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
+    result["default"] = mod;
+    return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+require("source-map-support/register");
+var path_1 = __importDefault(require("path"));
+var fs_1 = __importDefault(require("fs"));
+var glob_1 = __importDefault(require("glob"));
+var yargs_parser_1 = __importDefault(require("yargs-parser"));
+var webpack_1 = __importDefault(require("webpack"));
+var webpack_inject_plugin_1 = __importDefault(require("webpack-inject-plugin"));
+var webpack_dev_server_1 = __importDefault(require("webpack-dev-server"));
+var express_1 = __importDefault(require("express"));
+var create_html_1 = __importDefault(require("create-html"));
+var WebpackDevServerUtils_1 = require("react-dev-utils/WebpackDevServerUtils");
+var webpackHelpers = __importStar(require("./utils/webpackHelpers"));
+var CordovaConfigParser_1 = __importDefault(require("./utils/CordovaConfigParser"));
+module.exports = function (ctx) { return __awaiter(void 0, void 0, void 0, function () {
+    var platforms, targetPlatforms, argv, customWebpackConfig, customDevServerConfig, protocol, host, defaultPort, port, urls, defaultAccessHost, webpackConfig, devServerConfig, compiler, server;
+    return __generator(this, function (_a) {
+        switch (_a.label) {
+            case 0:
+                platforms = ['browser', 'android', 'ios'];
+                targetPlatforms = platforms.filter(function (platform) {
+                    return ctx.opts.platforms.includes(platform);
+                });
+                if (!platforms.some(function (platform) { return ctx.opts.platforms.includes(platform); })) {
+                    return [2];
+                }
+                if (!ctx.opts.options || !ctx.opts.options.argv) {
+                    return [2];
+                }
+                argv = yargs_parser_1.default(ctx.opts.options.argv.join(' '));
+                if (!argv.livereload && !argv.l) {
+                    return [2];
+                }
+                customWebpackConfig = webpackHelpers.webpackConfig(ctx.opts.projectRoot, argv.webpackConfig || argv.w);
+                customDevServerConfig = customWebpackConfig.devServer || {};
+                protocol = customDevServerConfig.https ? 'https' : 'http';
+                host = customDevServerConfig.host || '0.0.0.0';
+                defaultPort = customDevServerConfig.port || 8080;
+                return [4, WebpackDevServerUtils_1.choosePort(host, defaultPort)];
+            case 1:
+                port = _a.sent();
+                if (!port) {
+                    return [2];
+                }
+                urls = WebpackDevServerUtils_1.prepareUrls(protocol, host, port);
+                defaultAccessHost = {
+                    android: "10.0.2.2",
+                    ios: "localhost",
+                };
+                webpackConfig = __assign(__assign({}, customWebpackConfig), { mode: 'development', plugins: __spreadArrays((customWebpackConfig.plugins || []), [
+                        new webpack_inject_plugin_1.default(function () {
+                            return fs_1.default.readFileSync(path_1.default.join(__dirname, 'www/injectCSP.js'), 'utf8');
+                        }),
+                        new webpack_inject_plugin_1.default(function () {
+                            return fs_1.default.readFileSync(path_1.default.join(__dirname, 'www/injectCordovaScript.js'), 'utf8');
+                        }),
+                    ]) });
+                devServerConfig = __assign(__assign({ contentBase: path_1.default.join(ctx.opts.projectRoot, 'www'), historyApiFallback: true, watchContentBase: true, hot: true }, customDevServerConfig), { host: host,
+                    port: port, before: function (app, server) {
+                        if (customDevServerConfig.before) {
+                            customDevServerConfig.before(app, server);
+                        }
+                        targetPlatforms.forEach(function (platform) {
+                            app.use("/" + platform, express_1.default.static(path_1.default.join(ctx.opts.projectRoot, 'platforms', platform, 'platform_www')));
+                        });
+                    } });
+                if (devServerConfig.hot)
+                    webpack_dev_server_1.default.addDevServerEntrypoints(webpackConfig, devServerConfig);
+                targetPlatforms.forEach(function (platform) {
+                    if (platform === 'browser') {
+                        var html = create_html_1.default({
+                            head: "<meta http-equiv=\"refresh\" content=\"0;URL=" + urls.localUrlForBrowser + "\">",
+                        });
+                        fs_1.default.writeFileSync(path_1.default.join(ctx.opts.projectRoot, 'platforms', platform, 'www/index.html'), html);
+                        return;
+                    }
+                    glob_1.default
+                        .sync(path_1.default.join(ctx.opts.projectRoot, 'platforms', platform, '**/config.xml'))
+                        .forEach(function (configXmlPath) {
+                        var configXml = new CordovaConfigParser_1.default(configXmlPath);
+                        configXml.setContent(protocol + "://" + (urls.lanUrlForConfig ||
+                            defaultAccessHost[platform]) + ":" + port);
+                        if (platform === 'ios') {
+                            configXml.setElement('allow-navigation', { href: '*' });
+                        }
+                        configXml.write();
+                    });
+                });
+                compiler = webpack_1.default(webpackConfig);
+                server = new webpack_dev_server_1.default(compiler, devServerConfig);
+                return [4, new Promise(function (resolve, reject) {
+                        server.listen(port, host, function (err) {
+                            if (err) {
+                                reject(err);
+                            }
+                            console.log('Starting the development server...\n');
+                            resolve();
+                        });
+                    })];
+            case 2:
+                _a.sent();
+                return [2];
+        }
+    });
+}); };
+//# sourceMappingURL=webpackServe.js.map

File diff suppressed because it is too large
+ 0 - 0
plugins/cordova-plugin-webpack/scripts/webpackServe.js.map


File diff suppressed because it is too large
+ 96 - 0
plugins/cordova-plugin-webpack/scripts/www/injectCSP.js


File diff suppressed because it is too large
+ 96 - 0
plugins/cordova-plugin-webpack/scripts/www/injectCordovaScript.js


+ 30 - 0
plugins/cordova-plugin-webpack/tsconfig.json

@@ -0,0 +1,30 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "module": "commonjs",
+    "allowJs": true,
+    "outDir": "scripts",
+    "removeComments": true,
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true,
+    "esModuleInterop": true,
+    "moduleResolution": "node",
+    "baseUrl": ".",
+    "paths": {
+      "*": [
+        "typings/*"
+      ]
+    },
+    "typeRoots": [
+      "./typings",
+      "./node_modules/@types"
+    ],
+    "sourceMap": true,
+    "noErrorTruncation": true,
+  },
+  "include": [
+    "src/**/*"
+  ]
+}

+ 31 - 0
plugins/cordova-plugin-webpack/webpack.config.ts

@@ -0,0 +1,31 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import webpack from 'webpack';
+import path from 'path';
+import { CleanWebpackPlugin } from 'clean-webpack-plugin';
+
+const config: webpack.Configuration = {
+  mode: 'development',
+  entry: {
+    injectCSP: './src/www/injectCSP.ts',
+    injectCordovaScript: './src/www/injectCordovaScript.ts',
+  },
+  output: {
+    filename: '[name].js',
+    path: path.join(__dirname, 'scripts/www'),
+  },
+  devtool: 'eval-source-map',
+  resolve: {
+    extensions: ['.ts', '.js'],
+  },
+  module: {
+    rules: [
+      {
+        test: /\.ts$/,
+        use: 'ts-loader',
+      },
+    ],
+  },
+  plugins: [new CleanWebpackPlugin()],
+};
+
+export default config;

+ 37 - 0
plugins/cordova-plugin-whitelist/CONTRIBUTING.md

@@ -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.
+#
+-->
+
+# Contributing to Apache Cordova
+
+Anyone can contribute to Cordova. And we need your contributions.
+
+There are multiple ways to contribute: report bugs, improve the docs, and
+contribute code.
+
+For instructions on this, start with the
+[contribution overview](http://cordova.apache.org/contribute/).
+
+The details are explained there, but the important items are:
+ - Sign and submit an Apache ICLA (Contributor License Agreement).
+ - Have a Jira issue open that corresponds to your contribution.
+ - Run the tests so your patch doesn't break existing functionality.
+
+We look forward to your contributions!

+ 202 - 0
plugins/cordova-plugin-whitelist/LICENSE

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.

+ 5 - 0
plugins/cordova-plugin-whitelist/NOTICE

@@ -0,0 +1,5 @@
+Apache Cordova
+Copyright 2012 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).

+ 167 - 0
plugins/cordova-plugin-whitelist/README.md

@@ -0,0 +1,167 @@
+---
+title: Whitelist
+description: Whitelist external content accessible by your app.
+---
+<!--
+# license: Licensed to the Apache Software Foundation (ASF) under one
+#         or more contributor license agreements.  See the NOTICE file
+#         distributed with this work for additional information
+#         regarding copyright ownership.  The ASF licenses this file
+#         to you under the Apache License, Version 2.0 (the
+#         "License"); you may not use this file except in compliance
+#         with the License.  You may obtain a copy of the License at
+#
+#           http://www.apache.org/licenses/LICENSE-2.0
+#
+#         Unless required by applicable law or agreed to in writing,
+#         software distributed under the License is distributed on an
+#         "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#         KIND, either express or implied.  See the License for the
+#         specific language governing permissions and limitations
+#         under the License.
+-->
+
+# cordova-plugin-whitelist
+
+This plugin implements a whitelist policy for navigating the application webview on Cordova 4.0
+
+## Installation
+
+You can install whitelist plugin with Cordova CLI, from npm:
+
+```
+$ cordova plugin add cordova-plugin-whitelist
+$ cordova prepare
+```
+
+## Supported Cordova Platforms
+
+* Android 4.0.0 or above
+
+## Navigation Whitelist
+
+Controls which URLs the WebView itself can be navigated to. Applies to
+top-level navigations only.
+
+By default navigations are only allowed to `file://` URLs. To allow others URLs, you must add `<allow-navigation>` tags to your `config.xml`:
+
+    <!-- Allow links to example.com -->
+    <allow-navigation href="http://example.com/*" />
+
+    <!-- Wildcards are allowed for the protocol, as a prefix
+         to the host, or as a suffix to the path -->
+    <allow-navigation href="*://*.example.com/*" />
+
+    <!-- A wildcard can be used to whitelist the entire network,
+         over HTTP and HTTPS.
+         *NOT RECOMMENDED* -->
+    <allow-navigation href="*" />
+
+    <!-- The above is equivalent to these three declarations -->
+    <allow-navigation href="http://*/*" />
+    <allow-navigation href="https://*/*" />
+    <allow-navigation href="data:*" />
+
+Quirks: on Android it also applies to iframes for non-http(s) schemes.
+
+## Intent Whitelist
+
+Controls which URLs the app is allowed to ask the system to open.
+
+In `config.xml`, add `<allow-intent>` tags, like this:
+
+    <!-- Allow links to web pages to open in a browser -->
+    <allow-intent href="http://*/*" />
+    <allow-intent href="https://*/*" />
+
+    <!-- Allow links to example.com to open in a browser -->
+    <allow-intent href="http://example.com/*" />
+
+    <!-- Wildcards are allowed for the protocol, as a prefix
+         to the host, or as a suffix to the path -->
+    <allow-intent href="*://*.example.com/*" />
+
+    <!-- Allow SMS links to open messaging app -->
+    <allow-intent href="sms:*" />
+
+    <!-- Allow tel: links to open the dialer -->
+    <allow-intent href="tel:*" />
+
+    <!-- Allow geo: links to open maps -->
+    <allow-intent href="geo:*" />
+
+    <!-- Allow all unrecognized URLs to open installed apps
+         *NOT RECOMMENDED* -->
+    <allow-intent href="*" />
+
+Without any `<allow-intent>` tags, no requests to external URLs are allowed. However, the default Cordova application includes a quite liberal set of `allow-intent` entries by default. It is advised to narrow this down based on each app's needs.
+
+On Android, this equates to sending an intent of type BROWSEABLE.
+
+This whitelist does not apply to plugins, only hyperlinks and calls to `window.open()`.
+
+Note: `allow-navigation` takes precedence over `allow-intent`. Allowing navigation to all URLs with `<allow-navigation href="*" />` for example has the side effect of "capturing" all intents, so the webview navigates to them instead of triggering e.g. external apps.
+
+## Network Request Whitelist
+
+Controls which network requests (images, XHRs, etc) are allowed to be made (via cordova native hooks).
+
+Note: We suggest you use a Content Security Policy (see below), which is more secure.  This whitelist is mostly historical for webviews which do not support CSP.
+
+In `config.xml`, add `<access>` tags, like this:
+
+    <!-- Allow images, xhrs, etc. to google.com -->
+    <access origin="http://google.com" />
+    <access origin="https://google.com" />
+
+    <!-- Access to the subdomain maps.google.com -->
+    <access origin="http://maps.google.com" />
+
+    <!-- Access to all the subdomains on google.com -->
+    <access origin="http://*.google.com" />
+
+    <!-- Enable requests to content: URLs -->
+    <access origin="content:///*" />
+
+    <!-- Don't block any requests -->
+    <access origin="*" />
+
+Without any `<access>` tags, only requests to `file://` URLs are allowed. However, the default Cordova application includes `<access origin="*">` by default.
+
+Note: Whitelist cannot block network redirects from a whitelisted remote website (i.e. http or https) to a non-whitelisted website. Use CSP rules to mitigate redirects to non-whitelisted websites for webviews that support CSP.
+
+Quirk: Android also allows requests to https://ssl.gstatic.com/accessibility/javascript/android/ by default, since this is required for TalkBack to function properly.
+
+### Content Security Policy
+
+Controls which network requests (images, XHRs, etc) are allowed to be made (via webview directly).
+
+On Android and iOS, the network request whitelist (see above) is not able to filter all types of requests (e.g. `<video>` & WebSockets are not blocked). So, in addition to the whitelist, you should use a [Content Security Policy](http://content-security-policy.com/) `<meta>` tag on all of your pages.
+
+On Android, support for CSP within the system webview starts with KitKat (but is available on all versions using Crosswalk WebView).
+
+Here are some example CSP declarations for your `.html` pages:
+
+    <!-- Good default declaration:
+        * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
+        * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
+        * Disables use of eval() and inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
+            * Enable inline JS: add 'unsafe-inline' to default-src
+            * Enable eval(): add 'unsafe-eval' to default-src
+    -->
+    <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com; style-src 'self' 'unsafe-inline'; media-src *">
+
+    <!-- Allow everything but only from the same origin and foo.com -->
+    <meta http-equiv="Content-Security-Policy" content="default-src 'self' foo.com">
+
+    <!-- This policy allows everything (eg CSS, AJAX, object, frame, media, etc) except that 
+        * CSS only from the same origin and inline styles,
+        * scripts only from the same origin and inline styles, and eval()
+    -->
+    <meta http-equiv="Content-Security-Policy" content="default-src *; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'">
+
+    <!-- Allows XHRs only over HTTPS on the same domain. -->
+    <meta http-equiv="Content-Security-Policy" content="default-src 'self' https:">
+
+    <!-- Allow iframe to https://cordova.apache.org/ -->
+    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; frame-src 'self' https://cordova.apache.org">

+ 87 - 0
plugins/cordova-plugin-whitelist/RELEASENOTES.md

@@ -0,0 +1,87 @@
+<!--
+#
+# 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.
+#
+-->
+# Release Notes
+
+### 1.3.4 (Jun 19, 2019)
+
+-   chore: manually fix lines breaks to LF ([`d804ef2`](https://github.com/apache/cordova-plugin-whitelist/commit/d804ef2))
+-   build: add `.gitattributes` to force LF (instead of possible CRLF on Windows) ([`ed0206b`](https://github.com/apache/cordova-plugin-whitelist/commit/ed0206b))
+-   build: add `.npmignore` to remove unneeded files from npm package ([`bf8fea5`](https://github.com/apache/cordova-plugin-whitelist/commit/bf8fea5))
+-   test,ci(travis): Tests (extracted from cordova-mobile-spec) ([#38](https://github.com/apache/cordova-plugin-whitelist/issues/38)) ([`e4f17b0`](https://github.com/apache/cordova-plugin-whitelist/commit/e4f17b0))
+-   docs: Added information on allow-navigation preceding allow-intent ([#41](https://github.com/apache/cordova-plugin-whitelist/issues/41)) ([`7725fed`](https://github.com/apache/cordova-plugin-whitelist/commit/7725fed))
+-   chore(github): Add or update GitHub pull request and issue template ([`86b3ee1`](https://github.com/apache/cordova-plugin-whitelist/commit/86b3ee1))
+-   fix: Remove information about cordova-android ([#27](https://github.com/apache/cordova-plugin-whitelist/issues/27)) ([`34ed9d0`](https://github.com/apache/cordova-plugin-whitelist/commit/34ed9d0))
+-   docs: remove JIRA link ([`fa78675`](https://github.com/apache/cordova-plugin-whitelist/commit/fa78675))
+-   docs: Clarify unconfigured Intent Whitelist behaviour ([#26](https://github.com/apache/cordova-plugin-whitelist/issues/26)) ([`8d3f86b`](https://github.com/apache/cordova-plugin-whitelist/commit/8d3f86b))
+
+### 1.3.3 (Nov 06, 2017)
+* [CB-12847](https://issues.apache.org/jira/browse/CB-12847) added `bugs` entry to `package.json`.
+
+### 1.3.2 (Feb 28, 2017)
+* [CB-12236](https://issues.apache.org/jira/browse/CB-12236) Fixed `RELEASENOTES` for `cordova-plugin-whitelist`
+
+### 1.3.1 (Dec 07, 2016)
+* [CB-12224](https://issues.apache.org/jira/browse/CB-12224) Updated version and RELEASENOTES.md for release 1.3.1
+* [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…"
+* Edit package.json license to match SPDX id
+* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version.
+
+### 1.3.0 (Sep 08, 2016)
+* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies
+* Updated installation section
+* Plugin uses `Android Log class` and not `Cordova LOG class`
+* Add pull request template.
+* [CB-10866](https://issues.apache.org/jira/browse/CB-10866) Adding engine info to `package.json`
+* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md
+
+### 1.2.2 (Apr 15, 2016)
+* add note about redirects
+* [CB-10624](https://issues.apache.org/jira/browse/CB-10624) remove error message from `whitelist.js`, which leaves it empty
+
+### 1.2.1 (Jan 15, 2016)
+* [CB-10194](https://issues.apache.org/jira/browse/CB-10194) info tag prints for ios when not applicable
+
+### 1.2.0 (Nov 18, 2015)
+* removed **iOS** engine check from `plugin.xml`
+* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest
+* [CB-9972](https://issues.apache.org/jira/browse/CB-9972) - Remove **iOS** whitelist
+* Updated the text, it should read 4.0.x and greater, since this plugin will be required for `cordova-android 5.0`
+* Fixing contribute link.
+* Updated `plugin.xml <info>` tag to remove warning about not needing this plugin if you are using the **iOS 9 SDK**
+* [CB-9738](https://issues.apache.org/jira/browse/CB-9738) - Disable whitelist use when runtime environment is **iOS 9**
+* [CB-9740](https://issues.apache.org/jira/browse/CB-9740) - Add `<info>` tag describing whitelist plugin not needed on `cordova-ios` and cordova-android 3.x`
+* [CB-9568](https://issues.apache.org/jira/browse/CB-9568) - Update whitelist plugin to allow all network access by default
+* [CB-9337](https://issues.apache.org/jira/browse/CB-9337) - enable use of `<access>` tags for native code network requests
+
+### 1.1.0 (Jun 17, 2015)
+* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-whitelist documentation translation: cordova-plugin-whitelist
+* fix npm md issue
+* Usage of CDVURLRequestFilter protocol.
+* [CB-9089](https://issues.apache.org/jira/browse/CB-9089) - iOS whitelist plugin does not compile
+* [CB-9090](https://issues.apache.org/jira/browse/CB-9090) - Enable whitelist plugin for cordova-ios 4.0.0
+* Fixed error in Content-Security-Policy example
+
+### 1.0.0 (Mar 25, 2015)
+* [CB-8739](https://issues.apache.org/jira/browse/CB-8739) added missing license headers
+* Add @Override to CustomConfigXmlParser methods
+* Change ID to cordova-plugin-whitelist rather than reverse-DNS-style
+* Tweak CSP examples in README
+* [CB-8660](https://issues.apache.org/jira/browse/CB-8660) remove extra commas from package.json

+ 67 - 0
plugins/cordova-plugin-whitelist/package.json

@@ -0,0 +1,67 @@
+{
+  "_from": "cordova-plugin-whitelist@1",
+  "_id": "cordova-plugin-whitelist@1.3.4",
+  "_inBundle": false,
+  "_integrity": "sha512-EYC5eQFVkoYXq39l7tYKE6lEjHJ04mvTmKXxGL7quHLdFPfJMNzru/UYpn92AOfpl3PQaZmou78C7EgmFOwFQQ==",
+  "_location": "/cordova-plugin-whitelist",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "cordova-plugin-whitelist@1",
+    "name": "cordova-plugin-whitelist",
+    "escapedName": "cordova-plugin-whitelist",
+    "rawSpec": "1",
+    "saveSpec": null,
+    "fetchSpec": "1"
+  },
+  "_requiredBy": [
+    "#DEV:/",
+    "#USER"
+  ],
+  "_resolved": "https://registry.npmjs.org/cordova-plugin-whitelist/-/cordova-plugin-whitelist-1.3.4.tgz",
+  "_shasum": "31938545c7c3e7de35c20ab08c2c3afa06e8a3f9",
+  "_spec": "cordova-plugin-whitelist@1",
+  "_where": "/home/dom1.taus.us.thales/t0124938/project/twelve-heroes",
+  "author": {
+    "name": "Apache Software Foundation"
+  },
+  "bugs": {
+    "url": "https://github.com/apache/cordova-plugin-whitelist/issues"
+  },
+  "bundleDependencies": false,
+  "cordova": {
+    "platforms": [
+      "android"
+    ]
+  },
+  "deprecated": false,
+  "description": "Cordova Whitelist Plugin",
+  "engines": {
+    "cordovaDependencies": {
+      "0.0.0": {
+        "cordova-android": ">=4.0.0"
+      },
+      "2.0.0": {
+        "cordova": ">100"
+      }
+    }
+  },
+  "homepage": "https://github.com/apache/cordova-plugin-whitelist#readme",
+  "keywords": [
+    "cordova",
+    "whitelist",
+    "ecosystem:cordova",
+    "cordova-android"
+  ],
+  "license": "Apache-2.0",
+  "name": "cordova-plugin-whitelist",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/apache/cordova-plugin-whitelist.git"
+  },
+  "scripts": {
+    "test": "echo \"no tests implemented for this plugin\""
+  },
+  "version": "1.3.4"
+}

+ 44 - 0
plugins/cordova-plugin-whitelist/plugin.xml

@@ -0,0 +1,44 @@
+<?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.
+-->
+
+<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
+           id="cordova-plugin-whitelist"
+      version="1.3.4">
+    <name>Whitelist</name>
+    <description>Cordova Network Whitelist Plugin</description>
+    <license>Apache 2.0</license>
+    <keywords>cordova,whitelist,policy</keywords>
+
+    <engines>
+      <engine name="cordova-android" version=">=4.0.0" />
+    </engines>
+
+    <platform name="android">
+        <config-file target="res/xml/config.xml" parent="/*">
+            <feature name="Whitelist" >
+                <param name="android-package" value="org.apache.cordova.whitelist.WhitelistPlugin"/>
+                <param name="onload" value="true" />
+            </feature>
+        </config-file>
+
+        <source-file src="src/android/WhitelistPlugin.java" target-dir="src/org/apache/cordova/whitelist" />
+    </platform>
+
+</plugin>

+ 161 - 0
plugins/cordova-plugin-whitelist/src/android/WhitelistPlugin.java

@@ -0,0 +1,161 @@
+/*
+       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.
+*/
+
+package org.apache.cordova.whitelist;
+
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.ConfigXmlParser;
+import org.apache.cordova.LOG;
+import org.apache.cordova.Whitelist;
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.Context;
+
+public class WhitelistPlugin extends CordovaPlugin {
+    private static final String LOG_TAG = "WhitelistPlugin";
+    private Whitelist allowedNavigations;
+    private Whitelist allowedIntents;
+    private Whitelist allowedRequests;
+
+    // Used when instantiated via reflection by PluginManager
+    public WhitelistPlugin() {
+    }
+    // These can be used by embedders to allow Java-configuration of whitelists.
+    public WhitelistPlugin(Context context) {
+        this(new Whitelist(), new Whitelist(), null);
+        new CustomConfigXmlParser().parse(context);
+    }
+    public WhitelistPlugin(XmlPullParser xmlParser) {
+        this(new Whitelist(), new Whitelist(), null);
+        new CustomConfigXmlParser().parse(xmlParser);
+    }
+    public WhitelistPlugin(Whitelist allowedNavigations, Whitelist allowedIntents, Whitelist allowedRequests) {
+        if (allowedRequests == null) {
+            allowedRequests = new Whitelist();
+            allowedRequests.addWhiteListEntry("file:///*", false);
+            allowedRequests.addWhiteListEntry("data:*", false);
+        }
+        this.allowedNavigations = allowedNavigations;
+        this.allowedIntents = allowedIntents;
+        this.allowedRequests = allowedRequests;
+    }
+    @Override
+    public void pluginInitialize() {
+        if (allowedNavigations == null) {
+            allowedNavigations = new Whitelist();
+            allowedIntents = new Whitelist();
+            allowedRequests = new Whitelist();
+            new CustomConfigXmlParser().parse(webView.getContext());
+        }
+    }
+
+    private class CustomConfigXmlParser extends ConfigXmlParser {
+        @Override
+        public void handleStartTag(XmlPullParser xml) {
+            String strNode = xml.getName();
+            if (strNode.equals("content")) {
+                String startPage = xml.getAttributeValue(null, "src");
+                allowedNavigations.addWhiteListEntry(startPage, false);
+            } else if (strNode.equals("allow-navigation")) {
+                String origin = xml.getAttributeValue(null, "href");
+                if ("*".equals(origin)) {
+                    allowedNavigations.addWhiteListEntry("http://*/*", false);
+                    allowedNavigations.addWhiteListEntry("https://*/*", false);
+                    allowedNavigations.addWhiteListEntry("data:*", false);
+                } else {
+                    allowedNavigations.addWhiteListEntry(origin, false);
+                }
+            } else if (strNode.equals("allow-intent")) {
+                String origin = xml.getAttributeValue(null, "href");
+                allowedIntents.addWhiteListEntry(origin, false);
+            } else if (strNode.equals("access")) {
+                String origin = xml.getAttributeValue(null, "origin");
+                String subdomains = xml.getAttributeValue(null, "subdomains");
+                boolean external = (xml.getAttributeValue(null, "launch-external") != null);
+                if (origin != null) {
+                    if (external) {
+                        LOG.w(LOG_TAG, "Found <access launch-external> within config.xml. Please use <allow-intent> instead.");
+                        allowedIntents.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
+                    } else {
+                        if ("*".equals(origin)) {
+                            allowedRequests.addWhiteListEntry("http://*/*", false);
+                            allowedRequests.addWhiteListEntry("https://*/*", false);
+                        } else {
+                            allowedRequests.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
+                        }
+                    }
+                }
+            }
+        }
+        @Override
+        public void handleEndTag(XmlPullParser xml) {
+        }
+    }
+
+    @Override
+    public Boolean shouldAllowNavigation(String url) {
+        if (allowedNavigations.isUrlWhiteListed(url)) {
+            return true;
+        }
+        return null; // Default policy
+    }
+
+    @Override
+    public Boolean shouldAllowRequest(String url) {
+        if (Boolean.TRUE == shouldAllowNavigation(url)) {
+            return true;
+        }
+        if (allowedRequests.isUrlWhiteListed(url)) {
+            return true;
+        }
+        return null; // Default policy
+    }
+
+    @Override
+    public Boolean shouldOpenExternalUrl(String url) {
+        if (allowedIntents.isUrlWhiteListed(url)) {
+            return true;
+        }
+        return null; // Default policy
+    }
+
+    public Whitelist getAllowedNavigations() {
+        return allowedNavigations;
+    }
+
+    public void setAllowedNavigations(Whitelist allowedNavigations) {
+        this.allowedNavigations = allowedNavigations;
+    }
+
+    public Whitelist getAllowedIntents() {
+        return allowedIntents;
+    }
+
+    public void setAllowedIntents(Whitelist allowedIntents) {
+        this.allowedIntents = allowedIntents;
+    }
+
+    public Whitelist getAllowedRequests() {
+        return allowedRequests;
+    }
+
+    public void setAllowedRequests(Whitelist allowedRequests) {
+        this.allowedRequests = allowedRequests;
+    }
+}

+ 53 - 0
plugins/cordova-plugin-whitelist/tests/README.md

@@ -0,0 +1,53 @@
+---
+ license: Licensed to the Apache Software Foundation (ASF) under one
+         or more contributor license agreements.  See the NOTICE file
+         distributed with this work for additional information
+         regarding copyright ownership.  The ASF licenses this file
+         to you under the Apache License, Version 2.0 (the
+         "License"); you may not use this file except in compliance
+         with the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+         Unless required by applicable law or agreed to in writing,
+         software distributed under the License is distributed on an
+         "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+         KIND, either express or implied.  See the License for the
+         specific language governing permissions and limitations
+         under the License.
+---
+
+# cordova-plugin-whitelist-tests
+
+This are an [optional JavaScript interface](#whitelist) to the core Cordova Whitelist functionality and the test plugin that uses it to test cordova-plugin-whitelist.
+
+## Whitelist
+
+> The `whitelist` object provides an interface for testing whether arbitrary
+> URLs are allowed by the currently active configuration, or would be allowed
+> by a given set of whitelist patterns.
+
+### Methods
+
+- cordova.whitelist.match
+- cordova.whitelist.test
+
+#### cordova.whitelist.match
+
+Indicates whether a given URL would be allowed by a set of Whitelist URL
+patterns.
+
+    cordova.whitelist.match(url, patterns, callback);
+
+`callback` will be invoked with a boolean argument indicating whether the
+url matches the set of patterns.
+
+#### cordova.whitelist.test
+
+Indicates whether a given URL would be allowed by the current application
+configuration.
+
+    cordova.whitelist.test(url, callback);
+
+`callback` will be invoked with a boolean argument indicating whether the
+url is currently whitelisted.

+ 15 - 0
plugins/cordova-plugin-whitelist/tests/package.json

@@ -0,0 +1,15 @@
+{
+  "name": "cordova-plugin-whitelist-tests",
+  "version": "1.3.4",
+  "description": "",
+  "cordova": {
+    "id": "cordova-plugin-whitelist-tests",
+    "platforms": []
+  },
+  "keywords": [
+    "ecosystem:cordova"
+  ],
+  "author": "",
+  "license": "Apache 2.0"
+}
+

+ 56 - 0
plugins/cordova-plugin-whitelist/tests/plugin.xml

@@ -0,0 +1,56 @@
+<?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.
+-->
+
+<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
+           id="cordova-plugin-whitelist-tests"
+      version="1.3.4">
+    <name>Cordova Plugin Whitelist Tests</name>
+    <license>Apache 2.0</license>
+
+    <js-module src="tests.js" name="tests">
+    </js-module>
+    
+    <js-module src="www/whitelist.js" name="whitelist">
+        <clobbers target="cordova.whitelist" />
+    </js-module>
+
+    <!-- android -->
+    <platform name="android">
+        <config-file target="res/xml/config.xml" parent="/*">
+            <feature name="WhitelistAPI" >
+                <param name="android-package" value="org.apache.cordova.test.WhitelistAPI"/>
+            </feature>
+        </config-file>
+
+        <source-file src="src/android/WhitelistAPI.java" target-dir="src/org/apache/cordova/test" />
+
+        <!-- remove <access origin="*" /> -->
+        <hook type="after_prepare" src="scripts/remove-access.js"/>
+
+        <!-- add whitelist configuration the tests test for -->
+        <config-file target="config.xml" parent="/*">
+            <allow-navigation href="http://*.apache.org" />
+            <allow-navigation href="https://*.apache.org" />
+        </config-file>
+
+    </platform>
+
+</plugin>

+ 19 - 0
plugins/cordova-plugin-whitelist/tests/scripts/remove-access.js

@@ -0,0 +1,19 @@
+var fs = require('fs');
+var path = require('path');
+var rootdir = "";
+var file = path.join(rootdir, "platforms/android/app/src/main/res/xml/config.xml");
+
+fs.readFile( file, "utf8", function( err, data )
+{
+    if (err)
+        return console.log( err );
+
+    var result = data;
+    result = result.replace( "<access origin=\"*\" />", "" );
+
+    fs.writeFile( file, result, "utf8", function( err )
+    {
+        if (err)
+            return console.log( err );
+    } );
+} );

+ 90 - 0
plugins/cordova-plugin-whitelist/tests/src/android/WhitelistAPI.java

@@ -0,0 +1,90 @@
+/*
+       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.
+*/
+package org.apache.cordova.test;
+
+import org.apache.cordova.Whitelist;
+import org.apache.cordova.Config;
+
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.PluginResult;
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import org.apache.cordova.PluginManager;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class WhitelistAPI extends CordovaPlugin {
+    /**
+     * Executes the request and returns PluginResult.
+     *
+     * @param action            The action to execute.
+     * @param args              JSONArry of arguments for the plugin.
+     * @param callbackContext   The callback id used when calling back into JavaScript.
+     * @return                  True if the action was valid, false if not.
+     */
+    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
+        if (action.equals("URLMatchesPatterns")) {
+            String url = args.getString(0);
+            JSONArray patterns = args.getJSONArray(1);
+            Whitelist whitelist = new Whitelist();
+            for (int i=0; i < patterns.length(); i++) {
+                String pattern = patterns.getString(i);
+                whitelist.addWhiteListEntry(pattern, false);
+            }
+            boolean isAllowed = whitelist.isUrlWhiteListed(url);
+            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, isAllowed));
+            return true;
+        } else if (action.equals("URLIsAllowed")) {
+            String url = args.getString(0);
+            /* This code exists for compatibility between 3.x and 4.x versions of Cordova.
+             * Previously the CordovaWebView class had a method, getWhitelist, which would
+             * return a Whitelist object. Since the fixed whitelist is removed in Cordova 4.x,
+             * the correct call now is to shouldAllowRequest from the plugin manager.
+             */
+            Boolean isAllowed = null;
+            try {
+                Method isUrlWhiteListed = Config.class.getDeclaredMethod("isUrlWhitelisted", String.class);
+                isAllowed = (Boolean)isUrlWhiteListed.invoke(url);
+            } catch (NoSuchMethodException e) {
+            } catch (IllegalAccessException e) {
+            } catch (InvocationTargetException e) {
+            }
+            if (isAllowed == null) {
+                try {
+                    Method gpm = webView.getClass().getMethod("getPluginManager");
+                    PluginManager pm = (PluginManager)gpm.invoke(webView);
+                    Method isAllowedMethod = pm.getClass().getMethod("shouldAllowRequest", String.class);
+                    isAllowed = (Boolean)isAllowedMethod.invoke(pm, url);
+                    if (isAllowed == null) {
+                        isAllowed = false;
+                    }
+                } catch (NoSuchMethodException e) {
+                } catch (IllegalAccessException e) {
+                } catch (InvocationTargetException e) {
+                }
+            }
+
+            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, isAllowed));
+            return true;
+        }
+        return false;
+    }
+}

+ 214 - 0
plugins/cordova-plugin-whitelist/tests/tests.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.
+ *
+ */
+
+exports.defineAutoTests = function () {
+
+    var isAndroid = (cordova.platformId === "android");
+
+    describe('Whitelist API (cordova.whitelist)', function () {
+
+        it("should exist", function () {
+            expect(cordova.whitelist).toBeDefined();
+        });
+
+        describe("Match function (cordova.whitelist.match) that checks URLs against patterns", function () {
+            function expectMatchWithResult(result) {
+                return (function (url, patterns, description) {
+                    description = description || ((result ? "should accept " : "should reject ") + url + " for " + JSON.stringify(patterns));
+                    this.result = result;
+
+                    describe("Match function", function () {
+                        if (!isAndroid) {
+                            pending("Whitelist Plugin only exists for Android");
+                        }
+
+                        // Timeout is 7.5 seconds to allow physical devices enough
+                        // time to query the response. This is important for some
+                        // Android devices.
+                        var originalTimeout,
+                            cb;
+
+                        beforeEach(function (done) {
+                            originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
+                            jasmine.DEFAULT_TIMEOUT_INTERVAL = 7500;
+                            
+                            cb = jasmine.createSpy('spy').and.callFake(function () {
+                                done();
+                            });
+                            cordova.whitelist.match(url, patterns, cb);
+                        });
+
+                        afterEach(function () {
+                            jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
+                        });
+
+                        it(description, function () {
+                            expect(cb).toHaveBeenCalledWith(result);
+                        });
+                    });
+                });
+            }
+
+            var itShouldMatch = expectMatchWithResult(true);
+            var itShouldNotMatch = expectMatchWithResult(false);
+
+            it("should exist", function () {
+                expect(cordova.whitelist.match).toBeDefined();
+                expect(typeof cordova.whitelist.match).toBe("function");
+            });
+
+            itShouldMatch('http://www.apache.org/',     ['*'],  "should accept any domain for *");
+            itShouldNotMatch('http://www.apache.org/',  [],     "should not accept any domain for []");
+
+            itShouldMatch('http://apache.org/',                                             ['http://*.apache.org']);
+            itShouldMatch('http://www.apache.org/',                                         ['http://*.apache.org']);
+            itShouldMatch('http://www.apache.org/some/path',                                ['http://*.apache.org']);
+            itShouldMatch('http://some.domain.under.apache.org/',                           ['http://*.apache.org']);
+            itShouldMatch('http://user:pass@apache.org/',                                   ['http://*.apache.org']);
+            itShouldMatch('http://user:pass@www.apache.org/',                               ['http://*.apache.org']);
+            itShouldMatch('http://www.apache.org/?some=params',                             ['http://*.apache.org']);
+            itShouldNotMatch('http://apache.com/',                                          ['http://*.apache.org']);
+            itShouldNotMatch('http://www.evil.com/?url=www.apache.org',                     ['http://*.apache.org']);
+            itShouldNotMatch('http://www.evil.com/?url=http://www.apache.org',              ['http://*.apache.org']);
+            itShouldNotMatch('http://www.evil.com/?url=http%3A%2F%2Fwww%2Eapache%2Eorg',    ['http://*.apache.org']);
+            itShouldNotMatch('https://apache.org/',                                         ['http://*.apache.org']);
+            itShouldNotMatch('http://www.apache.org:pass@evil.com/',                        ['http://*.apache.org']);
+            itShouldNotMatch('http://www.apache.org.evil.com/',                             ['http://*.apache.org']);
+
+            itShouldMatch('http://www.apache.org/',     ['http://*.apache.org', 'https://*.apache.org']);
+            itShouldMatch('https://www.apache.org/',    ['http://*.apache.org', 'https://*.apache.org']);
+            itShouldNotMatch('ftp://www.apache.org/',   ['http://*.apache.org', 'https://*.apache.org']);
+            itShouldNotMatch('http://www.apache.com/',  ['http://*.apache.org', 'https://*.apache.org']);
+
+            itShouldMatch('http://www.apache.org/',         ['http://www.apache.org']);
+            itShouldNotMatch('http://build.apache.org/',    ['http://www.apache.org']);
+            itShouldNotMatch('http://apache.org/',          ['http://www.apache.org']);
+
+            itShouldMatch('http://www.apache.org/',             ['http://*/*']);
+            itShouldMatch('http://www.apache.org/foo/bar.html', ['http://*/*']);
+
+            itShouldMatch('http://www.apache.org/foo',          ['http://*/foo*']);
+            itShouldMatch('http://www.apache.org/foo/bar.html', ['http://*/foo*']);
+            itShouldNotMatch('http://www.apache.org/',          ['http://*/foo*']);
+
+            itShouldMatch('file:///foo', ['file:///*']);
+
+            itShouldMatch('file:///foo',                ['file:///foo*']);
+            itShouldMatch('file:///foo/bar.html',       ['file:///foo*']);
+            itShouldNotMatch('file:///foo.html',        []);
+            itShouldNotMatch('http://www.apache.org/etc/foo',   ['http://www.apache.org/foo*']);
+            itShouldNotMatch('http://www.apache.org/foo',       ['file:///foo*']);
+
+            itShouldMatch('http://www.apache.org/',     ['*://www.apache.org/*']);
+            itShouldMatch('https://www.apache.org/',    ['*://www.apache.org/*']);
+            itShouldMatch('ftp://www.apache.org/',      ['*://www.apache.org/*']);
+            itShouldMatch('file://www.apache.org/',     ['*://www.apache.org/*']);
+            if (cordova.platformId == 'android')
+                itShouldMatch('content://www.apache.org/', ['*://www.apache.org/*']);
+            itShouldMatch('foo://www.apache.org/',      ['*://www.apache.org/*']);
+            itShouldNotMatch('http://www.apache.com/',  ['*://www.apache.org/*']);
+
+            itShouldMatch('http://www.apache.org/',     ['*.apache.org']);
+            itShouldMatch('https://www.apache.org/',    ['*.apache.org']);
+            itShouldNotMatch('ftp://www.apache.org/',   ['*.apache.org']);
+
+            itShouldMatch('http://www.apache.org:81/',                          ['http://www.apache.org:81/*']);
+            itShouldMatch('http://user:pass@www.apache.org:81/foo/bar.html',    ['http://www.apache.org:81/*']);
+            itShouldNotMatch('http://www.apache.org:80/',                       ['http://www.apache.org:81/*']);
+            itShouldNotMatch('http://www.apache.org/',                          ['http://www.apache.org:81/*']);
+            itShouldNotMatch('http://www.apache.org:foo/',                      ['http://www.apache.org:81/*']);
+            itShouldNotMatch('http://www.apache.org:81@www.apache.org/',        ['http://www.apache.org:81/*']);
+            itShouldNotMatch('http://www.apache.org:81@www.evil.com/',          ['http://www.apache.org:81/*']);
+
+            itShouldMatch('http://www.APAche.org/', ['*.apache.org']);
+            itShouldMatch('http://WWw.apache.org/', ['*.apache.org']);
+            itShouldMatch('http://www.apache.org/', ['*.APACHE.ORG']);
+            itShouldMatch('HTTP://www.apache.org/', ['*.apache.org']);
+            itShouldMatch('HTTP://www.apache.org/', ['http://*.apache.org']);
+            itShouldMatch('http://www.apache.org/', ['HTTP://*.apache.org']);
+
+            itShouldMatch('http://www.apache.org/foo/',         ['*://*.apache.org/foo/*']);
+            itShouldMatch('http://www.apache.org/foo/bar',      ['*://*.apache.org/foo/*']);
+            itShouldNotMatch('http://www.apache.org/bar/foo/',  ['*://*.apache.org/foo/*']);
+            itShouldNotMatch('http://www.apache.org/Foo/',      ['*://*.apache.org/foo/*']);
+            itShouldNotMatch('http://www.apache.org/Foo/bar',   ['*://*.apache.org/foo/*']);
+        });
+
+        describe("Test function (cordova.whitelist.test) that checks against config.xml", function () {
+            function expectTestWithResult(result) {
+                return (function (url, description) {
+                    description = description || ((result ? "should accept " : "should reject ") + url);
+
+                    describe("Test function", function () {
+                        if (!isAndroid) {
+                            pending("Whitelist Plugin only exists for Android");
+                        }
+
+                        var cb,
+                            originalTimeout;
+
+                        beforeEach(function (done) {
+                            originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
+                            jasmine.DEFAULT_TIMEOUT_INTERVAL = 7500;
+                            cb = jasmine.createSpy('spy').and.callFake(function (){
+                                done();
+                            });
+                            cordova.whitelist.test(url, cb);
+                        });
+
+                        afterEach(function () {
+                            jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
+                        });
+
+                        it(description, function () {
+                            expect(cb).toHaveBeenCalledWith(result);
+                        });
+                    });
+                });
+            }
+
+            var itShouldAccept = expectTestWithResult(true);
+            var itShouldReject = expectTestWithResult(false);
+
+            it("should exist", function () {
+                expect(cordova.whitelist.test).toBeDefined();
+                expect(typeof cordova.whitelist.test).toBe("function");
+            });
+
+            itShouldAccept('http://apache.org');
+            itShouldAccept('http://apache.org/');
+            itShouldAccept('http://www.apache.org/');
+            itShouldAccept('http://www.apache.org/some/path');
+            itShouldAccept('http://some.domain.under.apache.org/');
+            itShouldAccept('http://user:pass@apache.org/');
+            itShouldAccept('http://user:pass@www.apache.org/');
+            itShouldAccept('https://www.apache.org/');
+
+            itShouldReject('ftp://www.apache.org/');
+            itShouldReject('http://www.apache.com/');
+            itShouldReject('http://www.apache.org:pass@evil.com/');
+            itShouldReject('http://www.apache.org.evil.com/');
+
+            itShouldAccept('file:///foo');
+            if (cordova.platformId == 'android')
+                itShouldReject('content:///foo');
+        });
+    });
+}

+ 34 - 0
plugins/cordova-plugin-whitelist/tests/www/whitelist.js

@@ -0,0 +1,34 @@
+/*
+ *
+ * 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 argscheck = require('cordova/argscheck'),
+    exec = require('cordova/exec');
+
+module.exports = {
+    match: function(url, patterns, callback) {
+        //argscheck.checkArgs('fF', 'Whitelist.match', arguments);
+        exec(callback, callback, "WhitelistAPI", "URLMatchesPatterns", [url, patterns]);
+    },
+    test: function(url, callback) {
+        //argscheck.checkArgs('fF', 'Whitelist.test', arguments);
+        exec(callback, callback, "WhitelistAPI", "URLIsAllowed", [url]);
+    }
+}

+ 18 - 0
plugins/fetch.json

@@ -0,0 +1,18 @@
+{
+  "cordova-plugin-whitelist": {
+    "source": {
+      "type": "registry",
+      "id": "cordova-plugin-whitelist@1"
+    },
+    "is_top_level": true,
+    "variables": {}
+  },
+  "cordova-plugin-webpack": {
+    "source": {
+      "type": "registry",
+      "id": "cordova-plugin-webpack"
+    },
+    "is_top_level": true,
+    "variables": {}
+  }
+}

+ 29 - 1
src/main.js

@@ -18,10 +18,38 @@ import GameCreator from './GameCreator';
 
 // };
 // Object.freeze(Faction);
+var app = {
+  // Application Constructor
+  initialize: function() {
+      document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
+  },
+
+  // deviceready Event Handler
+  //
+  // Bind any cordova events here. Common events are:
+  // 'pause', 'resume', etc.
+  onDeviceReady: function() {
+      this.receivedEvent('deviceready');
+      let gameCreator = new GameCreator();
+  },
+
+  // 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);
+  }
+};
+
+app.initialize();
 
 
 
-let gameCreator = new GameCreator();
 
 
 // const config = {

+ 2 - 2
webpack.config.dev.js

@@ -10,7 +10,7 @@ export default {
   ],
   target: 'web',
   output: {
-    path: path.resolve(__dirname, 'src'),
+    path: path.resolve(__dirname, 'www'),
     publicPath: '/',
     filename: 'bundle.js'
   },
@@ -21,7 +21,7 @@ export default {
     }),
     // Create HTML file that includes reference to bundled JS.
     new HtmlWebpackPlugin({
-      template: 'src/index.html',
+      template: 'www/index.html',
       inject: true
     })
   ],

+ 107 - 0
www/css/index.css

@@ -0,0 +1,107 @@
+/*
+ * 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-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -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-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    font-family: system-ui, -apple-system, -apple-system-font, 'Segoe UI', 'Roboto', sans-serif;
+    font-size:12px;
+    height:100vh;
+    margin:0px;
+    padding:0px;
+    /* Padding to avoid the "unsafe" areas behind notches in the screen */
+    padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-right, 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;
+}

BIN
www/img/logo.png


+ 49 - 0
www/index.html

@@ -0,0 +1,49 @@
+<!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>
+        <!--
+        Customize this policy to fit your own app's needs. For more guidance, see:
+            https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
+        Some notes:
+            * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
+            * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
+            * Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
+                * Enable inline JS: add 'unsafe-inline' to default-src
+        -->
+        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
+        <meta name="format-detection" content="telephone=no">
+        <meta name="msapplication-tap-highlight" content="no">
+        <meta name="viewport" content="initial-scale=1, width=device-width, viewport-fit=cover">
+        <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.js"></script>
+        <!-- <script type="text/javascript" src="js/index.js"></script> -->
+    </body>
+</html>

+ 46 - 0
www/js/index.js

@@ -0,0 +1,46 @@
+/*
+ * 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() {
+        document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
+    },
+
+    // deviceready Event Handler
+    //
+    // Bind any cordova events here. Common events are:
+    // 'pause', 'resume', etc.
+    onDeviceReady: function() {
+        this.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);
+    }
+};
+
+app.initialize();

Some files were not shown because too many files changed in this diff