基础版本
diff --git a/platforms/browser/browser.json b/platforms/browser/browser.json
new file mode 100644
index 0000000..8ae268b
--- /dev/null
+++ b/platforms/browser/browser.json
@@ -0,0 +1,449 @@
+{
+  "prepare_queue": {
+    "installed": [],
+    "uninstalled": []
+  },
+  "config_munge": {
+    "files": {
+      "config.xml": {
+        "parents": {
+          "/*": [
+            {
+              "xml": "<feature name=\"CordovaHttpPlugin\"><param name=\"browser-package\" value=\"CordovaHttpPlugin\" /></feature>",
+              "count": 1
+            },
+            {
+              "xml": "<feature name=\"QRScanner\"><param name=\"browser-package\" value=\"QRScanner\" /></feature>",
+              "count": 1
+            },
+            {
+              "xml": "<feature name=\"Camera\"><param name=\"browser-package\" value=\"Camera\" /></feature>",
+              "count": 1
+            },
+            {
+              "xml": "<feature name=\"Device\"><param name=\"browser-package\" value=\"Device\" /></feature>",
+              "count": 1
+            }
+          ]
+        }
+      },
+      "*-Info.plist": {
+        "parents": {
+          "NSCameraUsageDescription": [
+            {
+              "xml": "<string>APP需要使用您的相机权限,没有该权限将无法完成扫一扫功能</string>",
+              "count": 1,
+              "mode": "merge",
+              "id": "config.xml"
+            }
+          ]
+        }
+      }
+    }
+  },
+  "installed_plugins": {
+    "cordova-plugin-add-swift-support": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-fingerprint-aio": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-touch-id": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-whitelist": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-file": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-advanced-http": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-statusbar": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-disable-ios11-statusbar": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-qrscanner": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-camera": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-inappbrowser": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-device": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-themeablebrowser": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    }
+  },
+  "dependent_plugins": {},
+  "modules": [
+    {
+      "file": "plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js",
+      "id": "cordova-plugin-fingerprint-aio.Fingerprint",
+      "pluginId": "cordova-plugin-fingerprint-aio",
+      "clobbers": [
+        "Fingerprint"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-touch-id/www/TouchID.js",
+      "id": "cordova-plugin-touch-id.TouchID",
+      "pluginId": "cordova-plugin-touch-id",
+      "clobbers": [
+        "window.plugins.touchid"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/DirectoryEntry.js",
+      "id": "cordova-plugin-file.DirectoryEntry",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.DirectoryEntry"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/DirectoryReader.js",
+      "id": "cordova-plugin-file.DirectoryReader",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.DirectoryReader"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/Entry.js",
+      "id": "cordova-plugin-file.Entry",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.Entry"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/File.js",
+      "id": "cordova-plugin-file.File",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.File"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/FileEntry.js",
+      "id": "cordova-plugin-file.FileEntry",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.FileEntry"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/FileError.js",
+      "id": "cordova-plugin-file.FileError",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.FileError"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/FileReader.js",
+      "id": "cordova-plugin-file.FileReader",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.FileReader"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/FileSystem.js",
+      "id": "cordova-plugin-file.FileSystem",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.FileSystem"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/FileUploadOptions.js",
+      "id": "cordova-plugin-file.FileUploadOptions",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.FileUploadOptions"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/FileUploadResult.js",
+      "id": "cordova-plugin-file.FileUploadResult",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.FileUploadResult"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/FileWriter.js",
+      "id": "cordova-plugin-file.FileWriter",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.FileWriter"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/Flags.js",
+      "id": "cordova-plugin-file.Flags",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.Flags"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/LocalFileSystem.js",
+      "id": "cordova-plugin-file.LocalFileSystem",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.LocalFileSystem"
+      ],
+      "merges": [
+        "window"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/Metadata.js",
+      "id": "cordova-plugin-file.Metadata",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.Metadata"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/ProgressEvent.js",
+      "id": "cordova-plugin-file.ProgressEvent",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.ProgressEvent"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/fileSystems.js",
+      "id": "cordova-plugin-file.fileSystems",
+      "pluginId": "cordova-plugin-file"
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/requestFileSystem.js",
+      "id": "cordova-plugin-file.requestFileSystem",
+      "pluginId": "cordova-plugin-file",
+      "clobbers": [
+        "window.requestFileSystem"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/resolveLocalFileSystemURI.js",
+      "id": "cordova-plugin-file.resolveLocalFileSystemURI",
+      "pluginId": "cordova-plugin-file",
+      "merges": [
+        "window"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/browser/isChrome.js",
+      "id": "cordova-plugin-file.isChrome",
+      "pluginId": "cordova-plugin-file",
+      "runs": true
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/browser/Preparing.js",
+      "id": "cordova-plugin-file.Preparing",
+      "pluginId": "cordova-plugin-file",
+      "runs": true
+    },
+    {
+      "file": "plugins/cordova-plugin-file/src/browser/FileProxy.js",
+      "id": "cordova-plugin-file.browserFileProxy",
+      "pluginId": "cordova-plugin-file",
+      "runs": true
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/fileSystemPaths.js",
+      "id": "cordova-plugin-file.fileSystemPaths",
+      "pluginId": "cordova-plugin-file",
+      "merges": [
+        "cordova"
+      ],
+      "runs": true
+    },
+    {
+      "file": "plugins/cordova-plugin-file/www/browser/FileSystem.js",
+      "id": "cordova-plugin-file.firefoxFileSystem",
+      "pluginId": "cordova-plugin-file",
+      "merges": [
+        "window.FileSystem"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/www/cookie-handler.js",
+      "id": "cordova-plugin-advanced-http.cookie-handler",
+      "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/www/global-configs.js",
+      "id": "cordova-plugin-advanced-http.global-configs",
+      "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/www/helpers.js",
+      "id": "cordova-plugin-advanced-http.helpers",
+      "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/www/js-util.js",
+      "id": "cordova-plugin-advanced-http.js-util",
+      "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/www/local-storage-store.js",
+      "id": "cordova-plugin-advanced-http.local-storage-store",
+      "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/www/lodash.js",
+      "id": "cordova-plugin-advanced-http.lodash",
+      "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/www/messages.js",
+      "id": "cordova-plugin-advanced-http.messages",
+      "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/www/public-interface.js",
+      "id": "cordova-plugin-advanced-http.public-interface",
+      "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/www/umd-tough-cookie.js",
+      "id": "cordova-plugin-advanced-http.tough-cookie",
+      "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/www/url-util.js",
+      "id": "cordova-plugin-advanced-http.url-util",
+      "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/www/advanced-http.js",
+      "id": "cordova-plugin-advanced-http.http",
+      "pluginId": "cordova-plugin-advanced-http",
+      "clobbers": [
+        "cordova.plugin.http"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-advanced-http/src/browser/cordova-http-plugin.js",
+      "id": "cordova-plugin-advanced-http.http-proxy",
+      "pluginId": "cordova-plugin-advanced-http",
+      "runs": true
+    },
+    {
+      "file": "plugins/cordova-plugin-statusbar/www/statusbar.js",
+      "id": "cordova-plugin-statusbar.statusbar",
+      "pluginId": "cordova-plugin-statusbar",
+      "clobbers": [
+        "window.StatusBar"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-statusbar/src/browser/StatusBarProxy.js",
+      "id": "cordova-plugin-statusbar.StatusBarProxy",
+      "pluginId": "cordova-plugin-statusbar",
+      "runs": true
+    },
+    {
+      "file": "plugins/cordova-plugin-qrscanner/www/www.min.js",
+      "id": "cordova-plugin-qrscanner.QRScanner",
+      "pluginId": "cordova-plugin-qrscanner",
+      "clobbers": [
+        "QRScanner"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-qrscanner/src/browser/plugin.min.js",
+      "id": "cordova-plugin-qrscanner.QRScannerProxy",
+      "pluginId": "cordova-plugin-qrscanner",
+      "runs": true
+    },
+    {
+      "file": "plugins/cordova-plugin-camera/www/CameraConstants.js",
+      "id": "cordova-plugin-camera.Camera",
+      "pluginId": "cordova-plugin-camera",
+      "clobbers": [
+        "Camera"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-camera/www/CameraPopoverOptions.js",
+      "id": "cordova-plugin-camera.CameraPopoverOptions",
+      "pluginId": "cordova-plugin-camera",
+      "clobbers": [
+        "CameraPopoverOptions"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-camera/www/Camera.js",
+      "id": "cordova-plugin-camera.camera",
+      "pluginId": "cordova-plugin-camera",
+      "clobbers": [
+        "navigator.camera"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-camera/src/browser/CameraProxy.js",
+      "id": "cordova-plugin-camera.CameraProxy",
+      "pluginId": "cordova-plugin-camera",
+      "runs": true
+    },
+    {
+      "file": "plugins/cordova-plugin-inappbrowser/www/inappbrowser.js",
+      "id": "cordova-plugin-inappbrowser.inappbrowser",
+      "pluginId": "cordova-plugin-inappbrowser",
+      "clobbers": [
+        "cordova.InAppBrowser.open",
+        "window.open"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-inappbrowser/src/browser/InAppBrowserProxy.js",
+      "id": "cordova-plugin-inappbrowser.InAppBrowserProxy",
+      "pluginId": "cordova-plugin-inappbrowser",
+      "runs": true
+    },
+    {
+      "file": "plugins/cordova-plugin-device/www/device.js",
+      "id": "cordova-plugin-device.device",
+      "pluginId": "cordova-plugin-device",
+      "clobbers": [
+        "device"
+      ]
+    },
+    {
+      "file": "plugins/cordova-plugin-device/src/browser/DeviceProxy.js",
+      "id": "cordova-plugin-device.DeviceProxy",
+      "pluginId": "cordova-plugin-device",
+      "runs": true
+    }
+  ],
+  "plugin_metadata": {
+    "cordova-plugin-add-swift-support": "2.0.2",
+    "cordova-plugin-fingerprint-aio": "1.7.0",
+    "cordova-plugin-touch-id": "3.3.1",
+    "cordova-plugin-whitelist": "1.3.3",
+    "cordova-plugin-file": "6.0.1",
+    "cordova-plugin-advanced-http": "2.1.1",
+    "cordova-plugin-statusbar": "2.4.2",
+    "cordova-plugin-disable-ios11-statusbar": "1.0.0",
+    "cordova-plugin-qrscanner": "3.0.1",
+    "cordova-plugin-camera": "4.0.3",
+    "cordova-plugin-inappbrowser": "3.0.0",
+    "cordova-plugin-device": "2.0.2",
+    "cordova-plugin-themeablebrowser": "0.2.17"
+  }
+}
diff --git a/platforms/browser/config.xml b/platforms/browser/config.xml
new file mode 100644
index 0000000..a7be4bc
--- /dev/null
+++ b/platforms/browser/config.xml
@@ -0,0 +1,33 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="com.supwisdom.dlapp" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>dlapp</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:*" />
+    <allow-navigation href="http://*/*" />
+    <allow-navigation href="https://*/*" />
+    <allow-navigation href="data:*" />
+    <access origin="http://ip*" />
+    <edit-config file="*-Info.plist" mode="merge" target="NSCameraUsageDescription">
+        <string>APP需要使用您的相机权限,没有该权限将无法完成扫一扫功能</string>
+    </edit-config>
+    <preference name="AutoHideSplashScreen" value="true" />
+    <preference name="SplashScreenDelay" value="0" />
+    <preference name="ErrorUrl" value="error.html" />
+    <preference name="Orientation" value="portrait" />
+    <preference name="UseSwiftLanguageVersion" value="4.0" />
+    <preference name="DisallowOverscroll" value="true" />
+    <preference name="UIWebViewBounce" value="false" />
+    <preference name="BackupWebStorage" value="local" />
+</widget>
diff --git a/platforms/browser/cordova/Api.js b/platforms/browser/cordova/Api.js
new file mode 100644
index 0000000..da0ec8a
--- /dev/null
+++ b/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;
diff --git a/platforms/browser/cordova/browser_handler.js b/platforms/browser/cordova/browser_handler.js
new file mode 100644
index 0000000..bccddb4
--- /dev/null
+++ b/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));
+        }
+    }
+};
diff --git a/platforms/browser/cordova/browser_parser.js b/platforms/browser/cordova/browser_parser.js
new file mode 100644
index 0000000..99397c3
--- /dev/null
+++ b/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;
+};
diff --git a/platforms/browser/cordova/build b/platforms/browser/cordova/build
new file mode 100755
index 0000000..e867ab1
--- /dev/null
+++ b/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();
+}
diff --git a/platforms/browser/cordova/build.bat b/platforms/browser/cordova/build.bat
new file mode 100644
index 0000000..02641bc
--- /dev/null
+++ b/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
+)
diff --git a/platforms/browser/cordova/clean b/platforms/browser/cordova/clean
new file mode 100644
index 0000000..1852d66
--- /dev/null
+++ b/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();
+}
+
diff --git a/platforms/browser/cordova/clean.bat b/platforms/browser/cordova/clean.bat
new file mode 100644
index 0000000..5c572aa
--- /dev/null
+++ b/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
+)
diff --git a/platforms/browser/cordova/defaults.xml b/platforms/browser/cordova/defaults.xml
new file mode 100644
index 0000000..a7b31c0
--- /dev/null
+++ b/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>
diff --git a/platforms/browser/cordova/lib/build.js b/platforms/browser/cordova/lib/build.js
new file mode 100644
index 0000000..01f3fb1
--- /dev/null
+++ b/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 + "'.");
+};
diff --git a/platforms/browser/cordova/lib/check_reqs.js b/platforms/browser/cordova/lib/check_reqs.js
new file mode 100644
index 0000000..c615e14
--- /dev/null
+++ b/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([]);
+};
diff --git a/platforms/browser/cordova/lib/clean.js b/platforms/browser/cordova/lib/clean.js
new file mode 100644
index 0000000..6ee7675
--- /dev/null
+++ b/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();
+};
diff --git a/platforms/browser/cordova/lib/run.js b/platforms/browser/cordova/lib/run.js
new file mode 100644
index 0000000..0846231
--- /dev/null
+++ b/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();
+            }
+        });
+};
diff --git a/platforms/browser/cordova/log b/platforms/browser/cordova/log
new file mode 100755
index 0000000..bb6fb8c
--- /dev/null
+++ b/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");
\ No newline at end of file
diff --git a/platforms/browser/cordova/run b/platforms/browser/cordova/run
new file mode 100755
index 0000000..b41e299
--- /dev/null
+++ b/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);
+}
diff --git a/platforms/browser/cordova/run.bat b/platforms/browser/cordova/run.bat
new file mode 100644
index 0000000..b9c4402
--- /dev/null
+++ b/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
+)
diff --git a/platforms/browser/cordova/version b/platforms/browser/cordova/version
new file mode 100755
index 0000000..814b805
--- /dev/null
+++ b/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);
diff --git a/platforms/browser/cordova/version.bat b/platforms/browser/cordova/version.bat
new file mode 100644
index 0000000..3610c17
--- /dev/null
+++ b/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
+)
diff --git a/platforms/browser/platform_www/cordova-js-src/confighelper.js b/platforms/browser/platform_www/cordova-js-src/confighelper.js
new file mode 100644
index 0000000..34d8b19
--- /dev/null
+++ b/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;
diff --git a/platforms/browser/platform_www/cordova-js-src/exec.js b/platforms/browser/platform_www/cordova-js-src/exec.js
new file mode 100644
index 0000000..97f736a
--- /dev/null
+++ b/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");
+        }
+    }
+};
diff --git a/platforms/browser/platform_www/cordova-js-src/platform.js b/platforms/browser/platform_www/cordova-js-src/platform.js
new file mode 100644
index 0000000..96eb943
--- /dev/null
+++ b/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
+    }
+};
diff --git a/platforms/browser/platform_www/cordova.js b/platforms/browser/platform_www/cordova.js
new file mode 100644
index 0000000..02de9c7
--- /dev/null
+++ b/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');
+
+})();
\ No newline at end of file
diff --git a/platforms/browser/platform_www/cordova_plugins.js b/platforms/browser/platform_www/cordova_plugins.js
new file mode 100644
index 0000000..2b5f2ff
--- /dev/null
+++ b/platforms/browser/platform_www/cordova_plugins.js
@@ -0,0 +1,368 @@
+cordova.define('cordova/plugin_list', function(require, exports, module) {
+module.exports = [
+    {
+        "file": "plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js",
+        "id": "cordova-plugin-fingerprint-aio.Fingerprint",
+        "pluginId": "cordova-plugin-fingerprint-aio",
+        "clobbers": [
+            "Fingerprint"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-touch-id/www/TouchID.js",
+        "id": "cordova-plugin-touch-id.TouchID",
+        "pluginId": "cordova-plugin-touch-id",
+        "clobbers": [
+            "window.plugins.touchid"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/DirectoryEntry.js",
+        "id": "cordova-plugin-file.DirectoryEntry",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.DirectoryEntry"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/DirectoryReader.js",
+        "id": "cordova-plugin-file.DirectoryReader",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.DirectoryReader"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/Entry.js",
+        "id": "cordova-plugin-file.Entry",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.Entry"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/File.js",
+        "id": "cordova-plugin-file.File",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.File"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/FileEntry.js",
+        "id": "cordova-plugin-file.FileEntry",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.FileEntry"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/FileError.js",
+        "id": "cordova-plugin-file.FileError",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.FileError"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/FileReader.js",
+        "id": "cordova-plugin-file.FileReader",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.FileReader"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/FileSystem.js",
+        "id": "cordova-plugin-file.FileSystem",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.FileSystem"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/FileUploadOptions.js",
+        "id": "cordova-plugin-file.FileUploadOptions",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.FileUploadOptions"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/FileUploadResult.js",
+        "id": "cordova-plugin-file.FileUploadResult",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.FileUploadResult"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/FileWriter.js",
+        "id": "cordova-plugin-file.FileWriter",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.FileWriter"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/Flags.js",
+        "id": "cordova-plugin-file.Flags",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.Flags"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/LocalFileSystem.js",
+        "id": "cordova-plugin-file.LocalFileSystem",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.LocalFileSystem"
+        ],
+        "merges": [
+            "window"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/Metadata.js",
+        "id": "cordova-plugin-file.Metadata",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.Metadata"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/ProgressEvent.js",
+        "id": "cordova-plugin-file.ProgressEvent",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.ProgressEvent"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/fileSystems.js",
+        "id": "cordova-plugin-file.fileSystems",
+        "pluginId": "cordova-plugin-file"
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/requestFileSystem.js",
+        "id": "cordova-plugin-file.requestFileSystem",
+        "pluginId": "cordova-plugin-file",
+        "clobbers": [
+            "window.requestFileSystem"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/resolveLocalFileSystemURI.js",
+        "id": "cordova-plugin-file.resolveLocalFileSystemURI",
+        "pluginId": "cordova-plugin-file",
+        "merges": [
+            "window"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/browser/isChrome.js",
+        "id": "cordova-plugin-file.isChrome",
+        "pluginId": "cordova-plugin-file",
+        "runs": true
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/browser/Preparing.js",
+        "id": "cordova-plugin-file.Preparing",
+        "pluginId": "cordova-plugin-file",
+        "runs": true
+    },
+    {
+        "file": "plugins/cordova-plugin-file/src/browser/FileProxy.js",
+        "id": "cordova-plugin-file.browserFileProxy",
+        "pluginId": "cordova-plugin-file",
+        "runs": true
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/fileSystemPaths.js",
+        "id": "cordova-plugin-file.fileSystemPaths",
+        "pluginId": "cordova-plugin-file",
+        "merges": [
+            "cordova"
+        ],
+        "runs": true
+    },
+    {
+        "file": "plugins/cordova-plugin-file/www/browser/FileSystem.js",
+        "id": "cordova-plugin-file.firefoxFileSystem",
+        "pluginId": "cordova-plugin-file",
+        "merges": [
+            "window.FileSystem"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/www/cookie-handler.js",
+        "id": "cordova-plugin-advanced-http.cookie-handler",
+        "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/www/global-configs.js",
+        "id": "cordova-plugin-advanced-http.global-configs",
+        "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/www/helpers.js",
+        "id": "cordova-plugin-advanced-http.helpers",
+        "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/www/js-util.js",
+        "id": "cordova-plugin-advanced-http.js-util",
+        "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/www/local-storage-store.js",
+        "id": "cordova-plugin-advanced-http.local-storage-store",
+        "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/www/lodash.js",
+        "id": "cordova-plugin-advanced-http.lodash",
+        "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/www/messages.js",
+        "id": "cordova-plugin-advanced-http.messages",
+        "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/www/public-interface.js",
+        "id": "cordova-plugin-advanced-http.public-interface",
+        "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/www/umd-tough-cookie.js",
+        "id": "cordova-plugin-advanced-http.tough-cookie",
+        "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/www/url-util.js",
+        "id": "cordova-plugin-advanced-http.url-util",
+        "pluginId": "cordova-plugin-advanced-http"
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/www/advanced-http.js",
+        "id": "cordova-plugin-advanced-http.http",
+        "pluginId": "cordova-plugin-advanced-http",
+        "clobbers": [
+            "cordova.plugin.http"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-advanced-http/src/browser/cordova-http-plugin.js",
+        "id": "cordova-plugin-advanced-http.http-proxy",
+        "pluginId": "cordova-plugin-advanced-http",
+        "runs": true
+    },
+    {
+        "file": "plugins/cordova-plugin-statusbar/www/statusbar.js",
+        "id": "cordova-plugin-statusbar.statusbar",
+        "pluginId": "cordova-plugin-statusbar",
+        "clobbers": [
+            "window.StatusBar"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-statusbar/src/browser/StatusBarProxy.js",
+        "id": "cordova-plugin-statusbar.StatusBarProxy",
+        "pluginId": "cordova-plugin-statusbar",
+        "runs": true
+    },
+    {
+        "file": "plugins/cordova-plugin-qrscanner/www/www.min.js",
+        "id": "cordova-plugin-qrscanner.QRScanner",
+        "pluginId": "cordova-plugin-qrscanner",
+        "clobbers": [
+            "QRScanner"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-qrscanner/src/browser/plugin.min.js",
+        "id": "cordova-plugin-qrscanner.QRScannerProxy",
+        "pluginId": "cordova-plugin-qrscanner",
+        "runs": true
+    },
+    {
+        "file": "plugins/cordova-plugin-camera/www/CameraConstants.js",
+        "id": "cordova-plugin-camera.Camera",
+        "pluginId": "cordova-plugin-camera",
+        "clobbers": [
+            "Camera"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-camera/www/CameraPopoverOptions.js",
+        "id": "cordova-plugin-camera.CameraPopoverOptions",
+        "pluginId": "cordova-plugin-camera",
+        "clobbers": [
+            "CameraPopoverOptions"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-camera/www/Camera.js",
+        "id": "cordova-plugin-camera.camera",
+        "pluginId": "cordova-plugin-camera",
+        "clobbers": [
+            "navigator.camera"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-camera/src/browser/CameraProxy.js",
+        "id": "cordova-plugin-camera.CameraProxy",
+        "pluginId": "cordova-plugin-camera",
+        "runs": true
+    },
+    {
+        "file": "plugins/cordova-plugin-inappbrowser/www/inappbrowser.js",
+        "id": "cordova-plugin-inappbrowser.inappbrowser",
+        "pluginId": "cordova-plugin-inappbrowser",
+        "clobbers": [
+            "cordova.InAppBrowser.open",
+            "window.open"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-inappbrowser/src/browser/InAppBrowserProxy.js",
+        "id": "cordova-plugin-inappbrowser.InAppBrowserProxy",
+        "pluginId": "cordova-plugin-inappbrowser",
+        "runs": true
+    },
+    {
+        "file": "plugins/cordova-plugin-device/www/device.js",
+        "id": "cordova-plugin-device.device",
+        "pluginId": "cordova-plugin-device",
+        "clobbers": [
+            "device"
+        ]
+    },
+    {
+        "file": "plugins/cordova-plugin-device/src/browser/DeviceProxy.js",
+        "id": "cordova-plugin-device.DeviceProxy",
+        "pluginId": "cordova-plugin-device",
+        "runs": true
+    }
+];
+module.exports.metadata = 
+// TOP OF METADATA
+{
+    "cordova-plugin-add-swift-support": "2.0.2",
+    "cordova-plugin-fingerprint-aio": "1.7.0",
+    "cordova-plugin-touch-id": "3.3.1",
+    "cordova-plugin-whitelist": "1.3.3",
+    "cordova-plugin-file": "6.0.1",
+    "cordova-plugin-advanced-http": "2.1.1",
+    "cordova-plugin-statusbar": "2.4.2",
+    "cordova-plugin-disable-ios11-statusbar": "1.0.0",
+    "cordova-plugin-qrscanner": "3.0.1",
+    "cordova-plugin-camera": "4.0.3",
+    "cordova-plugin-inappbrowser": "3.0.0",
+    "cordova-plugin-device": "2.0.2",
+    "cordova-plugin-themeablebrowser": "0.2.17"
+}
+// BOTTOM OF METADATA
+});
\ No newline at end of file
diff --git a/platforms/browser/platform_www/favicon.ico b/platforms/browser/platform_www/favicon.ico
new file mode 100644
index 0000000..fa7a758
--- /dev/null
+++ b/platforms/browser/platform_www/favicon.ico
Binary files differ
diff --git a/platforms/browser/platform_www/manifest.json b/platforms/browser/platform_www/manifest.json
new file mode 100644
index 0000000..5ad3f7b
--- /dev/null
+++ b/platforms/browser/platform_www/manifest.json
@@ -0,0 +1,24 @@
+{
+  "name": "大理市民卡",
+  "short_name": "大理市民卡",
+  "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"
+}
\ No newline at end of file
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/src/browser/cordova-http-plugin.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/src/browser/cordova-http-plugin.js
new file mode 100644
index 0000000..953386f
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/src/browser/cordova-http-plugin.js
@@ -0,0 +1,222 @@
+cordova.define("cordova-plugin-advanced-http.http-proxy", function(require, exports, module) { var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
+
+var cordovaProxy = require('cordova/exec/proxy');
+var jsUtil = require(pluginId + '.js-util');
+
+function serializeJsonData(data) {
+  try {
+    return JSON.stringify(data);
+  } catch (err) {
+    return null;
+  }
+}
+
+function serializePrimitive(key, value) {
+  if (value === null || value === undefined) {
+    return encodeURIComponent(key) + '=';
+  }
+
+  return encodeURIComponent(key) + '=' + encodeURIComponent(value);
+}
+
+function serializeArray(key, values) {
+  return values.map(function(value) {
+    return encodeURIComponent(key) + '[]=' + encodeURIComponent(value);
+  }).join('&');
+}
+
+function serializeParams(params) {
+  if (params === null) return '';
+
+  return Object.keys(params).map(function(key) {
+    if (jsUtil.getTypeOf(params[key]) === 'Array') {
+      return serializeArray(key, params[key]);
+    }
+
+    return serializePrimitive(key, params[key]);
+  }).join('&');
+}
+
+function deserializeResponseHeaders(headers) {
+  var headerMap = {};
+  var arr = headers.trim().split(/[\r\n]+/);
+
+  arr.forEach(function (line) {
+    var parts = line.split(': ');
+    var header = parts.shift().toLowerCase();
+    var value = parts.join(': ');
+
+    headerMap[header] = value;
+  });
+
+  return headerMap;
+}
+
+function getResponseData(xhr) {
+  if (xhr.responseType !== 'text' || jsUtil.getTypeOf(xhr.responseText) !== 'String') {
+    return xhr.response;
+  }
+
+  return xhr.responseText;
+}
+
+function createXhrSuccessObject(xhr) {
+  return {
+    url: xhr.responseURL,
+    status: xhr.status,
+    data: getResponseData(xhr),
+    headers: deserializeResponseHeaders(xhr.getAllResponseHeaders())
+  };
+}
+
+function createXhrFailureObject(xhr) {
+  var obj = {};
+
+  obj.headers = xhr.getAllResponseHeaders();
+  obj.error = getResponseData(xhr);
+  obj.error = obj.error || 'advanced-http: please check browser console for error messages';
+
+  if (xhr.responseURL) obj.url = xhr.responseURL;
+  if (xhr.status) obj.status = xhr.status;
+
+  return obj;
+}
+
+function getHeaderValue(headers, headerName) {
+  let result = null;
+
+  Object.keys(headers).forEach(function(key) {
+    if (key.toLowerCase() === headerName.toLowerCase()) {
+      result = headers[key];
+    }
+  });
+
+  return result;
+}
+
+function setDefaultContentType(headers, contentType) {
+  if (getHeaderValue(headers, 'Content-Type') === null) {
+    headers['Content-Type'] = contentType;
+  }
+}
+
+function setHeaders(xhr, headers) {
+  Object.keys(headers).forEach(function(key) {
+    if (key.toLowerCase() === 'cookie') return;
+
+    xhr.setRequestHeader(key, headers[key]);
+  });
+}
+
+function sendRequest(method, withData, opts, success, failure) {
+  var data, serializer, headers, timeout, followRedirect, responseType;
+  var url = opts[0];
+
+  if (withData) {
+    data = opts[1];
+    serializer = opts[2];
+    headers = opts[3];
+    timeout = opts[4];
+    followRedirect = opts[5];
+    responseType = opts[6];
+  } else {
+    headers = opts[1];
+    timeout = opts[2];
+    followRedirect = opts[3];
+    responseType = opts[4];
+
+  }
+
+  var processedData = null;
+  var xhr = new XMLHttpRequest();
+
+  xhr.open(method, url);
+
+  if (headers.Cookie && headers.Cookie.length > 0) {
+    return failure('advanced-http: custom cookies not supported on browser platform');
+  }
+
+  if (!followRedirect) {
+    return failure('advanced-http: disabling follow redirect not supported on browser platform');
+  }
+
+  switch (serializer) {
+    case 'json':
+      setDefaultContentType(headers, 'application/json; charset=utf8');
+      processedData = serializeJsonData(data);
+
+      if (processedData === null) {
+        return failure('advanced-http: failed serializing data');
+      }
+
+      break;
+
+    case 'utf8':
+      setDefaultContentType(headers, 'text/plain; charset=utf8');
+      processedData = data.text;
+      break;
+
+    case 'urlencoded':
+      setDefaultContentType(headers, 'application/x-www-form-urlencoded');
+      processedData = serializeParams(data);
+      break;
+  }
+
+  xhr.timeout = timeout * 1000;
+  xhr.responseType = responseType;
+  setHeaders(xhr, headers);
+
+  xhr.onerror = xhr.ontimeout = function () {
+    return failure(createXhrFailureObject(xhr));
+  };
+
+  xhr.onload = function () {
+    if (xhr.readyState !== xhr.DONE) return;
+
+    if (xhr.status < 200 || xhr.status > 299) {
+      return failure(createXhrFailureObject(xhr));
+    }
+
+    return success(createXhrSuccessObject(xhr));
+  };
+
+  xhr.send(processedData);
+}
+
+var browserInterface = {
+  get: function (success, failure, opts) {
+    return sendRequest('get', false, opts, success, failure);
+  },
+  head: function (success, failure, opts) {
+    return sendRequest('head', false, opts, success, failure);
+  },
+  delete: function (success, failure, opts) {
+    return sendRequest('delete', false, opts, success, failure);
+  },
+  post: function (success, failure, opts) {
+    return sendRequest('post', true, opts, success, failure);
+  },
+  put: function (success, failure, opts) {
+    return sendRequest('put', true, opts, success, failure);
+  },
+  patch: function (success, failure, opts) {
+    return sendRequest('patch', true, opts, success, failure);
+  },
+  uploadFile: function (success, failure, opts) {
+    return failure('advanced-http: function "uploadFile" not supported on browser platform');
+  },
+  downloadFile: function (success, failure, opts) {
+    return failure('advanced-http: function "downloadFile" not supported on browser platform');
+  },
+  setServerTrustMode: function (success, failure, opts) {
+    return failure('advanced-http: function "setServerTrustMode" not supported on browser platform');
+  },
+  setClientAuthMode: function (success, failure, opts) {
+    return failure('advanced-http: function "setClientAuthMode" not supported on browser platform');
+  }
+};
+
+module.exports = browserInterface;
+cordovaProxy.add('CordovaHttpPlugin', browserInterface);
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/advanced-http.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/advanced-http.js
new file mode 100644
index 0000000..e41e8af
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/advanced-http.js
@@ -0,0 +1,22 @@
+cordova.define("cordova-plugin-advanced-http.http", function(require, exports, module) { /*
+ * A native HTTP Plugin for Cordova / PhoneGap.
+ */
+
+var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
+
+var exec = require('cordova/exec');
+var base64 = require('cordova/base64');
+var messages = require(pluginId + '.messages');
+var globalConfigs = require(pluginId + '.global-configs');
+var jsUtil = require(pluginId + '.js-util');
+var ToughCookie = require(pluginId + '.tough-cookie');
+var lodash = require(pluginId + '.lodash');
+var WebStorageCookieStore = require(pluginId + '.local-storage-store')(ToughCookie, lodash);
+var cookieHandler = require(pluginId + '.cookie-handler')(window.localStorage, ToughCookie, WebStorageCookieStore);
+var helpers = require(pluginId + '.helpers')(jsUtil, cookieHandler, messages, base64);
+var urlUtil = require(pluginId + '.url-util')(jsUtil);
+var publicInterface = require(pluginId + '.public-interface')(exec, cookieHandler, urlUtil, helpers, globalConfigs);
+
+module.exports = publicInterface;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/cookie-handler.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/cookie-handler.js
new file mode 100644
index 0000000..61d60b8
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/cookie-handler.js
@@ -0,0 +1,72 @@
+cordova.define("cordova-plugin-advanced-http.cookie-handler", function(require, exports, module) { module.exports = function init(storage, ToughCookie, WebStorageCookieStore) {
+  var storeKey = '__advancedHttpCookieStore__';
+
+  var store = new WebStorageCookieStore(storage, storeKey);
+  var cookieJar = new ToughCookie.CookieJar(store);
+
+  return {
+    setCookieFromString: setCookieFromString,
+    setCookie: setCookie,
+    getCookieString: getCookieString,
+    clearCookies: clearCookies,
+    removeCookies: removeCookies
+  };
+
+  function splitCookieString(cookieStr) {
+    var cookieParts = cookieStr.split(',');
+    var splitCookies = [];
+    var processedCookie = null;
+
+    for (var i = 0; i < cookieParts.length; ++i) {
+      if (cookieParts[i].substr(-11, 8).toLowerCase() === 'expires=') {
+        processedCookie = cookieParts[i] + ',' + cookieParts[i + 1];
+        i++;
+      } else {
+        processedCookie = cookieParts[i];
+      }
+
+      processedCookie = processedCookie.trim();
+      splitCookies.push(processedCookie);
+    }
+
+    return splitCookies;
+  }
+
+  function setCookieFromString(url, cookieStr) {
+    if (!cookieStr) return;
+
+    var cookies = splitCookieString(cookieStr);
+
+    for (var i = 0; i < cookies.length; ++i) {
+      cookieJar.setCookieSync(cookies[i], url, { ignoreError: true });
+    }
+  }
+
+  function setCookie(url, cookie, options) {
+    options = options || {};
+    options.ignoreError = false;
+    cookieJar.setCookieSync(cookie, url, options);
+  }
+
+  function getCookieString(url) {
+    return cookieJar.getCookieStringSync(url);
+  }
+
+  function clearCookies() {
+    window.localStorage.removeItem(storeKey);
+  }
+
+  function removeCookies(url, cb) {
+    cookieJar.getCookies(url, function (error, cookies) {
+      if (!cookies || cookies.length === 0) {
+        return cb(null, []);
+      }
+
+      var domain = cookies[0].domain;
+
+      cookieJar.store.removeCookies(domain, null, cb);
+    });
+  }
+};
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/global-configs.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/global-configs.js
new file mode 100644
index 0000000..b9ff2ec
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/global-configs.js
@@ -0,0 +1,10 @@
+cordova.define("cordova-plugin-advanced-http.global-configs", function(require, exports, module) { var globalConfigs = {
+  headers: {},
+  serializer: 'urlencoded',
+  followRedirect: true,
+  timeout: 60.0,
+};
+
+module.exports = globalConfigs;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/helpers.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/helpers.js
new file mode 100644
index 0000000..e1cb118
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/helpers.js
@@ -0,0 +1,352 @@
+cordova.define("cordova-plugin-advanced-http.helpers", function(require, exports, module) { module.exports = function init(jsUtil, cookieHandler, messages, base64) {
+  var validSerializers = ['urlencoded', 'json', 'utf8'];
+  var validCertModes = ['default', 'nocheck', 'pinned', 'legacy'];
+  var validClientAuthModes = ['none', 'systemstore', 'buffer'];
+  var validHttpMethods = ['get', 'put', 'post', 'patch', 'head', 'delete', 'upload', 'download'];
+  var validResponseTypes = ['text','arraybuffer', 'blob'];
+
+  var interface = {
+    b64EncodeUnicode: b64EncodeUnicode,
+    checkSerializer: checkSerializer,
+    checkSSLCertMode: checkSSLCertMode,
+    checkClientAuthMode: checkClientAuthMode,
+    checkClientAuthOptions: checkClientAuthOptions,
+    checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey,
+    checkForInvalidHeaderValue: checkForInvalidHeaderValue,
+    checkTimeoutValue: checkTimeoutValue,
+    checkFollowRedirectValue: checkFollowRedirectValue,
+    injectCookieHandler: injectCookieHandler,
+    injectRawResponseHandler: injectRawResponseHandler,
+    injectFileEntryHandler: injectFileEntryHandler,
+    getMergedHeaders: getMergedHeaders,
+    getProcessedData: getProcessedData,
+    handleMissingCallbacks: handleMissingCallbacks,
+    handleMissingOptions: handleMissingOptions
+  };
+
+  // expose all functions for testing purposes
+  if (init.debug) {
+    interface.mergeHeaders = mergeHeaders;
+    interface.checkForValidStringValue = checkForValidStringValue;
+    interface.checkKeyValuePairObject = checkKeyValuePairObject;
+    interface.checkHttpMethod = checkHttpMethod;
+    interface.checkResponseType = checkResponseType;
+    interface.checkHeadersObject = checkHeadersObject;
+    interface.checkParamsObject = checkParamsObject;
+    interface.resolveCookieString = resolveCookieString;
+    interface.createFileEntry = createFileEntry;
+    interface.getCookieHeader = getCookieHeader;
+    interface.getMatchingHostHeaders = getMatchingHostHeaders;
+    interface.getAllowedDataTypes = getAllowedDataTypes;
+  }
+
+  return interface;
+
+  // Thanks Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22
+  function b64EncodeUnicode(str) {
+    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
+      return String.fromCharCode('0x' + p1);
+    }));
+  }
+
+  function mergeHeaders(globalHeaders, localHeaders) {
+    var globalKeys = Object.keys(globalHeaders);
+    var key;
+
+    for (var i = 0; i < globalKeys.length; i++) {
+      key = globalKeys[i];
+
+      if (!localHeaders.hasOwnProperty(key)) {
+        localHeaders[key] = globalHeaders[key];
+      }
+    }
+
+    return localHeaders;
+  }
+
+  function checkForValidStringValue(list, value, onInvalidValueMessage) {
+    if (jsUtil.getTypeOf(value) !== 'String') {
+      throw new Error(onInvalidValueMessage + ' ' + list.join(', '));
+    }
+
+    value = value.trim().toLowerCase();
+
+    if (list.indexOf(value) === -1) {
+      throw new Error(onInvalidValueMessage + ' ' + list.join(', '));
+    }
+
+    return value;
+  }
+
+  function checkKeyValuePairObject(obj, allowedChildren, onInvalidValueMessage) {
+    if (jsUtil.getTypeOf(obj) !== 'Object') {
+      throw new Error(onInvalidValueMessage);
+    }
+
+    var keys = Object.keys(obj);
+
+    for (var i = 0; i < keys.length; i++) {
+      if (allowedChildren.indexOf(jsUtil.getTypeOf(obj[keys[i]])) === -1) {
+        throw new Error(onInvalidValueMessage);
+      }
+    }
+
+    return obj;
+  }
+
+  function checkHttpMethod(method) {
+    return checkForValidStringValue(validHttpMethods, method, messages.INVALID_HTTP_METHOD);
+  }
+
+  function checkResponseType(type) {
+    return checkForValidStringValue(validResponseTypes, type, messages.INVALID_RESPONSE_TYPE);
+  }
+
+  function checkSerializer(serializer) {
+    return checkForValidStringValue(validSerializers, serializer, messages.INVALID_DATA_SERIALIZER);
+  }
+
+  function checkSSLCertMode(mode) {
+    return checkForValidStringValue(validCertModes, mode, messages.INVALID_SSL_CERT_MODE);
+  }
+
+  function checkClientAuthMode(mode) {
+    return checkForValidStringValue(validClientAuthModes, mode, messages.INVALID_CLIENT_AUTH_MODE);
+  }
+
+  function checkClientAuthOptions(mode, options) {
+    options = options || {};
+
+    // none
+    if (mode === validClientAuthModes[0]) {
+      return {
+        alias: null,
+        rawPkcs: null,
+        pkcsPassword: ''
+      };
+    }
+
+    if (jsUtil.getTypeOf(options) !== 'Object') {
+      throw new Error(messages.INVALID_CLIENT_AUTH_OPTIONS);
+    }
+
+    // systemstore
+    if (mode === validClientAuthModes[1]) {
+      if (jsUtil.getTypeOf(options.alias) !== 'String'
+        && jsUtil.getTypeOf(options.alias) !== 'Undefined') {
+        throw new Error(messages.INVALID_CLIENT_AUTH_ALIAS);
+      }
+
+      return {
+        alias: jsUtil.getTypeOf(options.alias) === 'Undefined' ? null : options.alias,
+        rawPkcs: null,
+        pkcsPassword: ''
+      };
+    }
+
+    // buffer
+    if (mode === validClientAuthModes[2]) {
+      if (jsUtil.getTypeOf(options.rawPkcs) !== 'ArrayBuffer') {
+        throw new Error(messages.INVALID_CLIENT_AUTH_RAW_PKCS);
+      }
+
+      if (jsUtil.getTypeOf(options.pkcsPassword) !== 'String') {
+        throw new Error(messages.INVALID_CLIENT_AUTH_PKCS_PASSWORD);
+      }
+
+      return {
+        alias: null,
+        rawPkcs: options.rawPkcs,
+        pkcsPassword: options.pkcsPassword
+      }
+    }
+  }
+
+  function checkForBlacklistedHeaderKey(key) {
+    if (key.toLowerCase() === 'cookie') {
+      throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED);
+    }
+
+    return key;
+  }
+
+  function checkForInvalidHeaderValue(value) {
+    if (jsUtil.getTypeOf(value) !== 'String') {
+      throw new Error(messages.INVALID_HEADERS_VALUE);
+    }
+
+    return value;
+  }
+
+  function checkTimeoutValue(timeout) {
+    if (jsUtil.getTypeOf(timeout) !== 'Number' || timeout < 0) {
+      throw new Error(messages.INVALID_TIMEOUT_VALUE);
+    }
+
+    return timeout;
+  }
+
+  function checkFollowRedirectValue(follow) {
+    if (jsUtil.getTypeOf(follow) !== 'Boolean') {
+      throw new Error(messages.INVALID_FOLLOW_REDIRECT_VALUE);
+    }
+
+    return follow;
+  }
+
+  function checkHeadersObject(headers) {
+    return checkKeyValuePairObject(headers, ['String'], messages.INVALID_HEADERS_VALUE);
+  }
+
+  function checkParamsObject(params) {
+    return checkKeyValuePairObject(params, ['String', 'Array'], messages.INVALID_PARAMS_VALUE);
+  }
+
+  function resolveCookieString(headers) {
+    var keys = Object.keys(headers || {});
+
+    for (var i = 0; i < keys.length; ++i) {
+      if (keys[i].match(/^set-cookie$/i)) {
+        return headers[keys[i]];
+      }
+    }
+
+    return null;
+  }
+
+  function createFileEntry(rawEntry) {
+    var entry = new (require('cordova-plugin-file.FileEntry'))();
+
+    entry.isDirectory = rawEntry.isDirectory;
+    entry.isFile = rawEntry.isFile;
+    entry.name = rawEntry.name;
+    entry.fullPath = rawEntry.fullPath;
+    entry.filesystem = new FileSystem(rawEntry.filesystemName || (rawEntry.filesystem == window.PERSISTENT ? 'persistent' : 'temporary'));
+    entry.nativeURL = rawEntry.nativeURL;
+
+    return entry;
+  }
+
+  function injectCookieHandler(url, cb) {
+    return function (response) {
+      cookieHandler.setCookieFromString(url, resolveCookieString(response.headers));
+      cb(response);
+    }
+  }
+
+  function injectRawResponseHandler(responseType, cb) {
+    return function (response) {
+      var dataType = jsUtil.getTypeOf(response.data);
+
+      // don't need post-processing if it's already binary type (on browser platform)
+      if (dataType === 'ArrayBuffer' || dataType === 'Blob') {
+        return cb(response);
+      }
+
+      // arraybuffer
+      if (responseType === validResponseTypes[1]) {
+        var buffer = base64.toArrayBuffer(response.data);
+        response.data = buffer;
+      }
+
+      // blob
+      if (responseType === validResponseTypes[2]) {
+        var buffer = base64.toArrayBuffer(response.data);
+        var type = response.headers['content-type'] || '';
+        var blob = new Blob([ buffer ], { type: type });
+        response.data = blob;
+      }
+
+      cb(response);
+    }
+  }
+
+  function injectFileEntryHandler(cb) {
+    return function (response) {
+      cb(createFileEntry(response.file));
+    }
+  }
+
+  function getCookieHeader(url) {
+    var cookieString = cookieHandler.getCookieString(url);
+
+    if (cookieString.length) {
+      return { Cookie: cookieHandler.getCookieString(url) };
+    }
+
+    return {};
+  }
+
+  function getMatchingHostHeaders(url, headersList) {
+    var matches = url.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
+    var domain = matches && matches[1];
+
+    return headersList[domain] || null;
+  }
+
+  function getMergedHeaders(url, requestHeaders, predefinedHeaders) {
+    var globalHeaders = predefinedHeaders['*'] || {};
+    var hostHeaders = getMatchingHostHeaders(url, predefinedHeaders) || {};
+    var mergedHeaders = mergeHeaders(globalHeaders, hostHeaders);
+
+    mergedHeaders = mergeHeaders(mergedHeaders, requestHeaders);
+    mergedHeaders = mergeHeaders(mergedHeaders, getCookieHeader(url));
+
+    return mergedHeaders;
+  }
+
+  function getAllowedDataTypes(dataSerializer) {
+    switch (dataSerializer) {
+      case 'utf8':
+        return ['String'];
+      case 'urlencoded':
+        return ['Object'];
+      default:
+        return ['Array', 'Object'];
+    }
+  }
+
+  function getProcessedData(data, dataSerializer) {
+    var currentDataType = jsUtil.getTypeOf(data);
+    var allowedDataTypes = getAllowedDataTypes(dataSerializer);
+
+    if (allowedDataTypes.indexOf(currentDataType) === -1) {
+      throw new Error(messages.DATA_TYPE_MISMATCH + ' ' + allowedDataTypes.join(', '));
+    }
+
+    if (dataSerializer === 'utf8') {
+      data = { text: data };
+    }
+
+    return data;
+  }
+
+  function handleMissingCallbacks(successFn, failFn) {
+    if (jsUtil.getTypeOf(successFn) !== 'Function') {
+      throw new Error(messages.MANDATORY_SUCCESS);
+    }
+
+    if (jsUtil.getTypeOf(failFn) !== 'Function') {
+      throw new Error(messages.MANDATORY_FAIL);
+    }
+  }
+
+  function handleMissingOptions(options, globals) {
+    options = options || {};
+
+    return {
+      method: checkHttpMethod(options.method || validHttpMethods[0]),
+      responseType: checkResponseType(options.responseType || validResponseTypes[0]),
+      serializer: checkSerializer(options.serializer || globals.serializer),
+      timeout: checkTimeoutValue(options.timeout || globals.timeout),
+      followRedirect: checkFollowRedirectValue(options.followRedirect || globals.followRedirect),
+      headers: checkHeadersObject(options.headers || {}),
+      params: checkParamsObject(options.params || {}),
+      data: jsUtil.getTypeOf(options.data) === 'Undefined' ? null : options.data,
+      filePath: options.filePath || '',
+      name: options.name || ''
+    };
+  }
+};
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/js-util.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/js-util.js
new file mode 100644
index 0000000..ff0db76
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/js-util.js
@@ -0,0 +1,32 @@
+cordova.define("cordova-plugin-advanced-http.js-util", function(require, exports, module) { module.exports = {
+  // typeof is not working reliably in JS
+  getTypeOf: function (object) {
+    switch (Object.prototype.toString.call(object)) {
+      case '[object Array]':
+        return 'Array';
+      case '[object Blob]':
+        return 'Blob';
+      case '[object ArrayBuffer]':
+        return 'ArrayBuffer';
+      case '[object Boolean]':
+        return 'Boolean';
+      case '[object Function]':
+        return 'Function';
+      case '[object Null]':
+        return 'Null';
+      case '[object Number]':
+        return 'Number';
+      case '[object Object]':
+        return 'Object';
+      case '[object String]':
+        return 'String';
+      case '[object Undefined]':
+        return 'Undefined';
+      default:
+        return 'Unknown';
+    }
+  }
+}
+
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/local-storage-store.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/local-storage-store.js
new file mode 100644
index 0000000..05cd38a
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/local-storage-store.js
@@ -0,0 +1,183 @@
+cordova.define("cordova-plugin-advanced-http.local-storage-store", function(require, exports, module) { /*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Exponent
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Based on "tough-cookie-web-storage-store" v1.0.0
+ * Thanks James Ide: https://github.com/exponentjs/tough-cookie-web-storage-store
+ *
+ * Modified by Sefa Ilkimen for cordova plugin integration
+ *
+ */
+
+'use strict';
+
+module.exports = function init(ToughCookie, _) {
+  function WebStorageCookieStore(storage, storeKey) {
+    ToughCookie.Store.call(this);
+    this._storage = storage;
+    this._storeKey = storeKey || '__cookieStore__';
+    this.synchronous = true;
+  }
+
+  WebStorageCookieStore.prototype = Object.create(ToughCookie.Store);
+
+  WebStorageCookieStore.prototype.findCookie = function (domain, path, key, callback) {
+    var store = this._readStore();
+    var cookie = _.get(store, [domain, path, key], null);
+
+    callback(null, ToughCookie.Cookie.fromJSON(cookie));
+  };
+
+  WebStorageCookieStore.prototype.findCookies = function (domain, path, callback) {
+    if (!domain) {
+      callback(null, []);
+      return;
+    }
+
+    var that = this;
+    var cookies = [];
+    var store = this._readStore();
+    var domains = ToughCookie.permuteDomain(domain) || [domain];
+
+    domains.forEach(function (domain) {
+      if (!store[domain]) {
+        return;
+      }
+
+      var matchingPaths = Object.keys(store[domain]);
+
+      if (path != null) {
+        matchingPaths = matchingPaths.filter(function (cookiePath) {
+          return that._isOnPath(cookiePath, path);
+        });
+      }
+
+      matchingPaths.forEach(function (path) {
+        Array.prototype.push.apply(cookies, _.values(store[domain][path]));
+      });
+    });
+
+    cookies = cookies.map(function (cookie) {
+      return ToughCookie.Cookie.fromJSON(cookie);
+    });
+
+    callback(null, cookies);
+  };
+
+  /**
+   * Returns whether `cookiePath` is on the given `urlPath`
+   */
+  WebStorageCookieStore.prototype._isOnPath = function (cookiePath, urlPath) {
+    if (!cookiePath) {
+      return false;
+    }
+
+    if (cookiePath === urlPath) {
+      return true;
+    }
+
+    if (urlPath.indexOf(cookiePath) !== 0) {
+      return false;
+    }
+
+    if (cookiePath[cookiePath.length - 1] !== '/' && urlPath[cookiePath.length] !== '/') {
+      return false;
+    }
+
+    return true;
+  };
+
+  WebStorageCookieStore.prototype.putCookie = function (cookie, callback) {
+    var store = this._readStore();
+
+    _.set(store, [cookie.domain, cookie.path, cookie.key], cookie);
+    this._writeStore(store);
+    callback(null);
+  };
+
+  WebStorageCookieStore.prototype.updateCookie = function (oldCookie, newCookie, callback) {
+    this.putCookie(newCookie, callback);
+  };
+
+
+  WebStorageCookieStore.prototype.removeCookie = function (domain, path, key, callback) {
+    var store = this._readStore();
+
+    _.unset(store, [domain, path, key]);
+    this._writeStore(store);
+    callback(null);
+  };
+
+  WebStorageCookieStore.prototype.removeCookies = function (domain, path, callback) {
+    var store = this._readStore();
+
+    if (path == null) {
+      _.unset(store, [domain]);
+    } else {
+      _.unset(store, [domain, path]);
+    }
+
+    this._writeStore(store);
+    callback(null);
+  };
+
+  WebStorageCookieStore.prototype.getAllCookies = function (callback) {
+    var cookies = [];
+    var store = this._readStore();
+
+    Object.keys(store).forEach(function (domain) {
+      Object.keys(store[domain]).forEach(function (path) {
+        Array.protype.push.apply(cookies, _.values(store[domain][path]));
+      });
+    });
+
+    cookies = cookies.map(function (cookie) {
+      return ToughCookie.Cookie.fromJSON(cookie);
+    });
+
+    cookies.sort(function (c1, c2) {
+      return (c1.creationIndex || 0) - (c2.creationIndex || 0);
+    });
+
+    callback(null, cookies);
+  };
+
+  WebStorageCookieStore.prototype._readStore = function () {
+    var json = this._storage.getItem(this._storeKey);
+
+    if (json !== null) {
+      try {
+        return JSON.parse(json);
+      } catch (e) { }
+    }
+
+    return {};
+  };
+
+  WebStorageCookieStore.prototype._writeStore = function (store) {
+    this._storage.setItem(this._storeKey, JSON.stringify(store));
+  };
+
+  return WebStorageCookieStore;
+};
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/lodash.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/lodash.js
new file mode 100644
index 0000000..956c885
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/lodash.js
@@ -0,0 +1,21 @@
+cordova.define("cordova-plugin-advanced-http.lodash", function(require, exports, module) { /**
+ * @license
+ * Lodash (Custom Build) lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE
+ * Build: `lodash include="get,set,unset,values" exports="node"`
+ */
+;(function(){function t(t,e){for(var r=-1,n=null==t?0:t.length,o=Array(n);++r<n;)o[r]=e(t[r],r,t);return o}function e(t){return function(e){return t(e)}}function r(e,r){return t(r,function(t){return e[t]})}function n(){}function o(t){var e=-1,r=null==t?0:t.length;for(this.clear();++e<r;){var n=t[e];this.set(n[0],n[1])}}function u(t){var e=-1,r=null==t?0:t.length;for(this.clear();++e<r;){var n=t[e];this.set(n[0],n[1])}}function i(t){var e=-1,r=null==t?0:t.length;for(this.clear();++e<r;){var n=t[e];this.set(n[0],n[1]);
+}}function c(t,e){for(var r=t.length;r--;)if(m(t[r][0],e))return r;return-1}function a(t,e){e=h(e,t);for(var r=0,n=e.length;null!=t&&r<n;)t=t[j(e[r++])];return r&&r==n?t:E}function l(t){if(null==t)return t===E?"[object Undefined]":"[object Null]";t=Object(t);var e;if(nt&&nt in t){var r=Q.call(t,nt),n=t[nt];try{t[nt]=E,e=true}catch(t){}var o=Y.call(t);e&&(r?t[nt]=n:delete t[nt]),e=o}else e=Y.call(t);return e}function s(t){return w(t)&&"[object Arguments]"==l(t)}function f(t){return w(t)&&z(t.length)&&!!M[l(t)];
+}function p(e){if(typeof e=="string")return e;if(ft(e))return t(e,p)+"";if(x(e))return at?at.call(e):"";var r=e+"";return"0"==r&&1/e==-T?"-0":r}function h(t,e){var r;return ft(t)?r=t:(ft(t)?r=false:(r=typeof t,r=!("number"!=r&&"symbol"!=r&&"boolean"!=r&&null!=t&&!x(t))||(B.test(t)||!I.test(t)||null!=e&&t in Object(e))),r=r?[t]:lt(F(t))),r}function y(t,e){var r=t.__data__,n=typeof e;return("string"==n||"number"==n||"symbol"==n||"boolean"==n?"__proto__"!==e:null===e)?r[typeof e=="string"?"string":"hash"]:r.map;
+}function b(t,e){var r=null==t?E:t[e];return(!S(r)||X&&X in r?0:(O(r)?Z:L).test(g(r)))?r:E}function _(t,e){return e=null==e?9007199254740991:e,!!e&&(typeof t=="number"||R.test(t))&&-1<t&&0==t%1&&t<e}function j(t){if(typeof t=="string"||x(t))return t;var e=t+"";return"0"==e&&1/t==-T?"-0":e}function g(t){if(null!=t){try{return K.call(t)}catch(t){}return t+""}return""}function v(t){var e=null==t?0:t.length;return e?t[e-1]:E}function d(t,e){function r(){var n=arguments,o=e?e.apply(this,n):n[0],u=r.cache;
+return u.has(o)?u.get(o):(n=t.apply(this,n),r.cache=u.set(o,n)||u,n)}if(typeof t!="function"||null!=e&&typeof e!="function")throw new TypeError("Expected a function");return r.cache=new(d.Cache||i),r}function m(t,e){return t===e||t!==t&&e!==e}function A(t){return null!=t&&z(t.length)&&!O(t)}function O(t){return!!S(t)&&(t=l(t),"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t)}function z(t){return typeof t=="number"&&-1<t&&0==t%1&&9007199254740991>=t;
+}function S(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}function w(t){return null!=t&&typeof t=="object"}function x(t){return typeof t=="symbol"||w(t)&&"[object Symbol]"==l(t)}function F(t){return null==t?"":p(t)}function $(t){if(A(t)){var e=ft(t),r=!e&&st(t),n=!e&&!r&&pt(t),o=!e&&!r&&!n&&ht(t);if(e=e||r||n||o){for(var r=t.length,u=String,i=-1,c=Array(r);++i<r;)c[i]=u(i);r=c}else r=[];var a,u=r.length;for(a in t)!Q.call(t,a)||e&&("length"==a||n&&("offset"==a||"parent"==a)||o&&("buffer"==a||"byteLength"==a||"byteOffset"==a)||_(a,u))||r.push(a);
+t=r}else if(a=t&&t.constructor,t===(typeof a=="function"&&a.prototype||H)){a=[];for(n in Object(t))Q.call(t,n)&&"constructor"!=n&&a.push(n);t=a}else t=ut(t);return t}function k(){return false}var E,T=1/0,I=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,B=/^\w*$/,P=/^\./,U=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,C=/\\(\\)?/g,L=/^\[object .+?Constructor\]$/,R=/^(?:0|[1-9]\d*)$/,M={};M["[object Float32Array]"]=M["[object Float64Array]"]=M["[object Int8Array]"]=M["[object Int16Array]"]=M["[object Int32Array]"]=M["[object Uint8Array]"]=M["[object Uint8ClampedArray]"]=M["[object Uint16Array]"]=M["[object Uint32Array]"]=true,
+M["[object Arguments]"]=M["[object Array]"]=M["[object ArrayBuffer]"]=M["[object Boolean]"]=M["[object DataView]"]=M["[object Date]"]=M["[object Error]"]=M["[object Function]"]=M["[object Map]"]=M["[object Number]"]=M["[object Object]"]=M["[object RegExp]"]=M["[object Set]"]=M["[object String]"]=M["[object WeakMap]"]=false;var N,D=typeof global=="object"&&global&&global.Object===Object&&global,V=typeof self=="object"&&self&&self.Object===Object&&self,q=D||V||Function("return this")(),G=(V=typeof exports=="object"&&exports&&!exports.nodeType&&exports)&&typeof module=="object"&&module&&!module.nodeType&&module,W=G&&G.exports===V,D=W&&D.process;
+t:{try{N=D&&D.binding&&D.binding("util");break t}catch(t){}N=void 0}N=N&&N.isTypedArray;var D=Array.prototype,H=Object.prototype,J=q["__core-js_shared__"],K=Function.prototype.toString,Q=H.hasOwnProperty,X=function(){var t=/[^.]+$/.exec(J&&J.keys&&J.keys.IE_PROTO||"");return t?"Symbol(src)_1."+t:""}(),Y=H.toString,Z=RegExp("^"+K.call(Q).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),tt=W?q.Buffer:E,W=q.Symbol,et=H.propertyIsEnumerable,rt=D.splice,nt=W?W.toStringTag:E,ot=function(){
+try{var t=b(Object,"defineProperty");return t({},"",{}),t}catch(t){}}(),D=tt?tt.isBuffer:E,ut=function(t,e){return function(r){return t(e(r))}}(Object.keys,Object),it=b(q,"Map"),ct=b(Object,"create"),at=(q=W?W.prototype:E)?q.toString:E;o.prototype.clear=function(){this.__data__=ct?ct(null):{},this.size=0},o.prototype.delete=function(t){return t=this.has(t)&&delete this.__data__[t],this.size-=t?1:0,t},o.prototype.get=function(t){var e=this.__data__;return ct?(t=e[t],"__lodash_hash_undefined__"===t?E:t):Q.call(e,t)?e[t]:E;
+},o.prototype.has=function(t){var e=this.__data__;return ct?e[t]!==E:Q.call(e,t)},o.prototype.set=function(t,e){var r=this.__data__;return this.size+=this.has(t)?0:1,r[t]=ct&&e===E?"__lodash_hash_undefined__":e,this},u.prototype.clear=function(){this.__data__=[],this.size=0},u.prototype.delete=function(t){var e=this.__data__;return t=c(e,t),!(0>t)&&(t==e.length-1?e.pop():rt.call(e,t,1),--this.size,true)},u.prototype.get=function(t){var e=this.__data__;return t=c(e,t),0>t?E:e[t][1]},u.prototype.has=function(t){
+return-1<c(this.__data__,t)},u.prototype.set=function(t,e){var r=this.__data__,n=c(r,t);return 0>n?(++this.size,r.push([t,e])):r[n][1]=e,this},i.prototype.clear=function(){this.size=0,this.__data__={hash:new o,map:new(it||u),string:new o}},i.prototype.delete=function(t){return t=y(this,t).delete(t),this.size-=t?1:0,t},i.prototype.get=function(t){return y(this,t).get(t)},i.prototype.has=function(t){return y(this,t).has(t)},i.prototype.set=function(t,e){var r=y(this,t),n=r.size;return r.set(t,e),this.size+=r.size==n?0:1,
+this};var lt=function(t){t=d(t,function(t){return 500===e.size&&e.clear(),t});var e=t.cache;return t}(function(t){var e=[];return P.test(t)&&e.push(""),t.replace(U,function(t,r,n,o){e.push(n?o.replace(C,"$1"):r||t)}),e});d.Cache=i;var st=s(function(){return arguments}())?s:function(t){return w(t)&&Q.call(t,"callee")&&!et.call(t,"callee")},ft=Array.isArray,pt=D||k,ht=N?e(N):f;n.keys=$,n.memoize=d,n.set=function(t,e,r){if(null!=t&&S(t)){e=h(e,t);for(var n=-1,o=e.length,u=o-1,i=t;null!=i&&++n<o;){var c=j(e[n]),a=r;
+if(n!=u){var l=i[c],a=E;a===E&&(a=S(l)?l:_(e[n+1])?[]:{})}var s=i,l=c,f=s[l];Q.call(s,l)&&m(f,a)&&(a!==E||l in s)||("__proto__"==l&&ot?ot(s,l,{configurable:true,enumerable:true,value:a,writable:true}):s[l]=a),i=i[c]}}return t},n.unset=function(t,e){var r;if(null==t)r=true;else{var n=t,o=r=h(e,n);if(!(2>o.length)){var u=0,i=-1,c=-1,l=o.length;for(0>u&&(u=-u>l?0:l+u),i=i>l?l:i,0>i&&(i+=l),l=u>i?0:i-u>>>0,u>>>=0,i=Array(l);++c<l;)i[c]=o[c+u];n=a(n,i)}r=j(v(r)),r=!(null!=n&&Q.call(n,r))||delete n[r]}return r;
+},n.values=function(t){return null==t?[]:r(t,$(t))},n.eq=m,n.get=function(t,e,r){return t=null==t?E:a(t,e),t===E?r:t},n.isArguments=st,n.isArray=ft,n.isArrayLike=A,n.isBuffer=pt,n.isFunction=O,n.isLength=z,n.isObject=S,n.isObjectLike=w,n.isSymbol=x,n.isTypedArray=ht,n.last=v,n.stubFalse=k,n.toString=F,n.VERSION="4.17.1",G&&((G.exports=n)._=n,V._=n)}).call(this);
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/messages.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/messages.js
new file mode 100644
index 0000000..0feefaf
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/messages.js
@@ -0,0 +1,21 @@
+cordova.define("cordova-plugin-advanced-http.messages", function(require, exports, module) { module.exports = {
+  ADDING_COOKIES_NOT_SUPPORTED: 'advanced-http: "setHeader" does not support adding cookies, please use "setCookie" function instead',
+  DATA_TYPE_MISMATCH: 'advanced-http: "data" argument supports only following data types:',
+  INVALID_CLIENT_AUTH_ALIAS: 'advanced-http: invalid client certificate alias, needs to be a string or undefined',
+  INVALID_CLIENT_AUTH_MODE: 'advanced-http: invalid client certificate authentication mode, supported modes are:',
+  INVALID_CLIENT_AUTH_OPTIONS: 'advanced-http: invalid client certificate authentication options, needs to be an object',
+  INVALID_CLIENT_AUTH_PKCS_PASSWORD: 'advanced-http: invalid PKCS12 container password, needs to be a string',
+  INVALID_CLIENT_AUTH_RAW_PKCS: 'advanced-http: invalid PKCS12 container, needs to be an array buffer',
+  INVALID_DATA_SERIALIZER: 'advanced-http: invalid serializer, supported serializers are:',
+  INVALID_FOLLOW_REDIRECT_VALUE: 'advanced-http: invalid follow redirect value, needs to be a boolean value',
+  INVALID_HEADERS_VALUE: 'advanced-http: header values must be strings',
+  INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:',
+  INVALID_PARAMS_VALUE: 'advanced-http: invalid params object, needs to be an object with strings',
+  INVALID_RESPONSE_TYPE: 'advanced-http: invalid response type, supported types are:',
+  INVALID_SSL_CERT_MODE: 'advanced-http: invalid SSL cert mode, supported modes are:',
+  INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive numeric value',
+  MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function',
+  MANDATORY_SUCCESS: 'advanced-http: missing mandatory "onSuccess" callback function',
+};
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/public-interface.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/public-interface.js
new file mode 100644
index 0000000..a7dea53
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/public-interface.js
@@ -0,0 +1,201 @@
+cordova.define("cordova-plugin-advanced-http.public-interface", function(require, exports, module) { module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConfigs) {
+  const publicInterface = {
+    getBasicAuthHeader: getBasicAuthHeader,
+    useBasicAuth: useBasicAuth,
+    getHeaders: getHeaders,
+    setHeader: setHeader,
+    getDataSerializer: getDataSerializer,
+    setDataSerializer: setDataSerializer,
+    setCookie: setCookie,
+    clearCookies: clearCookies,
+    removeCookies: removeCookies,
+    getCookieString: getCookieString,
+    getRequestTimeout: getRequestTimeout,
+    setRequestTimeout: setRequestTimeout,
+    getFollowRedirect: getFollowRedirect,
+    setFollowRedirect: setFollowRedirect,
+    // @DEPRECATED
+    disableRedirect: disableRedirect,
+    // @DEPRECATED
+    setSSLCertMode: setServerTrustMode,
+    setServerTrustMode: setServerTrustMode,
+    setClientAuthMode: setClientAuthMode,
+    sendRequest: sendRequest,
+    post: post,
+    get: get,
+    put: put,
+    patch: patch,
+    delete: del,
+    head: head,
+    uploadFile: uploadFile,
+    downloadFile: downloadFile
+  };
+
+  function getBasicAuthHeader(username, password) {
+    return { 'Authorization': 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password) };
+  }
+
+  function useBasicAuth(username, password) {
+    this.setHeader('*', 'Authorization', 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password));
+  }
+
+  function getHeaders(host) {
+    return globalConfigs.headers[host || '*'] || null;
+  }
+
+  function setHeader() {
+    // this one is for being backward compatible
+    var host = '*';
+    var header = arguments[0];
+    var value = arguments[1];
+
+    if (arguments.length === 3) {
+      host = arguments[0];
+      header = arguments[1];
+      value = arguments[2];
+    }
+
+    helpers.checkForBlacklistedHeaderKey(header);
+    helpers.checkForInvalidHeaderValue(value);
+
+    globalConfigs.headers[host] = globalConfigs.headers[host] || {};
+    globalConfigs.headers[host][header] = value;
+  }
+
+  function getDataSerializer() {
+    return globalConfigs.serializer;
+  }
+
+  function setDataSerializer(serializer) {
+    globalConfigs.serializer = helpers.checkSerializer(serializer);
+  }
+
+  function setCookie(url, cookie, options) {
+    cookieHandler.setCookie(url, cookie, options);
+  }
+
+  function clearCookies() {
+    cookieHandler.clearCookies();
+  }
+
+  function removeCookies(url, callback) {
+    cookieHandler.removeCookies(url, callback);
+  }
+
+  function getCookieString(url) {
+    return cookieHandler.getCookieString(url);
+  }
+
+  function getRequestTimeout() {
+    return globalConfigs.timeout;
+  }
+
+  function setRequestTimeout(timeout) {
+    globalConfigs.timeout = helpers.checkTimeoutValue(timeout);
+  }
+
+  function getFollowRedirect() {
+    return globalConfigs.followRedirect;
+  }
+
+  function setFollowRedirect(follow) {
+    globalConfigs.followRedirect = helpers.checkFollowRedirectValue(follow);
+  }
+
+  // @DEPRECATED
+  function disableRedirect(disable, success, failure) {
+    helpers.handleMissingCallbacks(success, failure);
+
+    setFollowRedirect(!disable);
+    success();
+  }
+
+  function setServerTrustMode(mode, success, failure) {
+    helpers.handleMissingCallbacks(success, failure);
+
+    return exec(success, failure, 'CordovaHttpPlugin', 'setServerTrustMode', [helpers.checkSSLCertMode(mode)]);
+  }
+
+  function setClientAuthMode() {
+    var mode = arguments[0];
+    var options = null;
+    var success = arguments[1];
+    var failure = arguments[2];
+
+    if (arguments.length === 4) {
+      options = arguments[1];
+      success = arguments[2];
+      failure = arguments[3];
+    }
+
+    mode = helpers.checkClientAuthMode(mode);
+    options = helpers.checkClientAuthOptions(mode, options);
+
+    helpers.handleMissingCallbacks(success, failure);
+
+    return exec(success, failure, 'CordovaHttpPlugin', 'setClientAuthMode', [mode, options.alias, options.rawPkcs, options.pkcsPassword]);
+  }
+
+  function sendRequest(url, options, success, failure) {
+    helpers.handleMissingCallbacks(success, failure);
+
+    options = helpers.handleMissingOptions(options, globalConfigs);
+    url = urlUtil.appendQueryParamsString(url, urlUtil.serializeQueryParams(options.params, true));
+
+    var headers = helpers.getMergedHeaders(url, options.headers, globalConfigs.headers);
+
+    var onFail = helpers.injectCookieHandler(url, failure);
+    var onSuccess = helpers.injectCookieHandler(url, helpers.injectRawResponseHandler(options.responseType, success));
+
+    switch (options.method) {
+      case 'post':
+      case 'put':
+      case 'patch':
+        var data = helpers.getProcessedData(options.data, options.serializer);
+        return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, data, options.serializer, headers, options.timeout, options.followRedirect, options.responseType]);
+      case 'upload':
+        return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [url, headers, options.filePath, options.name, options.timeout, options.followRedirect, options.responseType]);
+      case 'download':
+        var onDownloadSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success));
+        return exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, headers, options.filePath, options.timeout, options.followRedirect]);
+      default:
+        return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, headers, options.timeout, options.followRedirect, options.responseType]);
+    }
+  }
+
+  function post(url, data, headers, success, failure) {
+    return publicInterface.sendRequest(url, { method: 'post', data: data, headers: headers }, success, failure);
+  };
+
+  function get(url, params, headers, success, failure) {
+    return publicInterface.sendRequest(url, { method: 'get', params: params, headers: headers }, success, failure);
+  };
+
+  function put(url, data, headers, success, failure) {
+    return publicInterface.sendRequest(url, { method: 'put', data: data, headers: headers }, success, failure);
+  }
+
+  function patch(url, data, headers, success, failure) {
+    return publicInterface.sendRequest(url, { method: 'patch', data: data, headers: headers }, success, failure);
+  }
+
+  function del(url, params, headers, success, failure) {
+    return publicInterface.sendRequest(url, { method: 'delete', params: params, headers: headers }, success, failure);
+  }
+
+  function head(url, params, headers, success, failure) {
+    return publicInterface.sendRequest(url, { method: 'head', params: params, headers: headers }, success, failure);
+  }
+
+  function uploadFile(url, params, headers, filePath, name, success, failure) {
+    return publicInterface.sendRequest(url, { method: 'upload', params: params, headers: headers, filePath: filePath, name: name }, success, failure);
+  }
+
+  function downloadFile(url, params, headers, filePath, success, failure) {
+    return publicInterface.sendRequest(url, { method: 'download', params: params, headers: headers, filePath: filePath }, success, failure);
+  }
+
+  return publicInterface;
+}
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/umd-tough-cookie.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/umd-tough-cookie.js
new file mode 100644
index 0000000..47c162e
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/umd-tough-cookie.js
@@ -0,0 +1,5090 @@
+cordova.define("cordova-plugin-advanced-http.tough-cookie", function(require, exports, module) { (function webpackUniversalModuleDefinition(root, factory) {
+	if(typeof exports === 'object' && typeof module === 'object')
+		module.exports = factory();
+	else if(typeof define === 'function' && define.amd)
+		define([], factory);
+	else if(typeof exports === 'object')
+		exports["ToughCookie"] = factory();
+	else
+		root["ToughCookie"] = factory();
+})(this, function() {
+return /******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId])
+/******/ 			return installedModules[moduleId].exports;
+
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			exports: {},
+/******/ 			id: moduleId,
+/******/ 			loaded: false
+/******/ 		};
+
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+
+/******/ 		// Flag the module as loaded
+/******/ 		module.loaded = true;
+
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+
+
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(0);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+
+	/*!
+	 * Copyright (c) 2015, Salesforce.com, Inc.
+	 * All rights reserved.
+	 *
+	 * Redistribution and use in source and binary forms, with or without
+	 * modification, are permitted provided that the following conditions are met:
+	 *
+	 * 1. Redistributions of source code must retain the above copyright notice,
+	 * this list of conditions and the following disclaimer.
+	 *
+	 * 2. Redistributions in binary form must reproduce the above copyright notice,
+	 * this list of conditions and the following disclaimer in the documentation
+	 * and/or other materials provided with the distribution.
+	 *
+	 * 3. Neither the name of Salesforce.com nor the names of its contributors may
+	 * be used to endorse or promote products derived from this software without
+	 * specific prior written permission.
+	 *
+	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+	 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+	 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+	 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+	 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+	 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+	 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+	 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+	 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+	 * POSSIBILITY OF SUCH DAMAGE.
+	 */
+	'use strict';
+	var net = __webpack_require__(1);
+	var urlParse = __webpack_require__(2).parse;
+	var util = __webpack_require__(9);
+	var pubsuffix = __webpack_require__(13);
+	var Store = __webpack_require__(17).Store;
+	var MemoryCookieStore = __webpack_require__(18).MemoryCookieStore;
+	var pathMatch = __webpack_require__(20).pathMatch;
+	var VERSION = __webpack_require__(21).version;
+
+	var punycode;
+	try {
+	  punycode = __webpack_require__(15);
+	} catch(e) {
+	  console.warn("tough-cookie: can't load punycode; won't use punycode for domain normalization");
+	}
+
+	// From RFC6265 S4.1.1
+	// note that it excludes \x3B ";"
+	var COOKIE_OCTETS = /^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]+$/;
+
+	var CONTROL_CHARS = /[\x00-\x1F]/;
+
+	// From Chromium // '\r', '\n' and '\0' should be treated as a terminator in
+	// the "relaxed" mode, see:
+	// https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L60
+	var TERMINATORS = ['\n', '\r', '\0'];
+
+	// RFC6265 S4.1.1 defines path value as 'any CHAR except CTLs or ";"'
+	// Note ';' is \x3B
+	var PATH_VALUE = /[\x20-\x3A\x3C-\x7E]+/;
+
+	// date-time parsing constants (RFC6265 S5.1.1)
+
+	var DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/;
+
+	var MONTH_TO_NUM = {
+	  jan:0, feb:1, mar:2, apr:3, may:4, jun:5,
+	  jul:6, aug:7, sep:8, oct:9, nov:10, dec:11
+	};
+	var NUM_TO_MONTH = [
+	  'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'
+	];
+	var NUM_TO_DAY = [
+	  'Sun','Mon','Tue','Wed','Thu','Fri','Sat'
+	];
+
+	var MAX_TIME = 2147483647000; // 31-bit max
+	var MIN_TIME = 0; // 31-bit min
+
+	/*
+	 * Parses a Natural number (i.e., non-negative integer) with either the
+	 *    <min>*<max>DIGIT ( non-digit *OCTET )
+	 * or
+	 *    <min>*<max>DIGIT
+	 * grammar (RFC6265 S5.1.1).
+	 *
+	 * The "trailingOK" boolean controls if the grammar accepts a
+	 * "( non-digit *OCTET )" trailer.
+	 */
+	function parseDigits(token, minDigits, maxDigits, trailingOK) {
+	  var count = 0;
+	  while (count < token.length) {
+	    var c = token.charCodeAt(count);
+	    // "non-digit = %x00-2F / %x3A-FF"
+	    if (c <= 0x2F || c >= 0x3A) {
+	      break;
+	    }
+	    count++;
+	  }
+
+	  // constrain to a minimum and maximum number of digits.
+	  if (count < minDigits || count > maxDigits) {
+	    return null;
+	  }
+
+	  if (!trailingOK && count != token.length) {
+	    return null;
+	  }
+
+	  return parseInt(token.substr(0,count), 10);
+	}
+
+	function parseTime(token) {
+	  var parts = token.split(':');
+	  var result = [0,0,0];
+
+	  /* RF6256 S5.1.1:
+	   *      time            = hms-time ( non-digit *OCTET )
+	   *      hms-time        = time-field ":" time-field ":" time-field
+	   *      time-field      = 1*2DIGIT
+	   */
+
+	  if (parts.length !== 3) {
+	    return null;
+	  }
+
+	  for (var i = 0; i < 3; i++) {
+	    // "time-field" must be strictly "1*2DIGIT", HOWEVER, "hms-time" can be
+	    // followed by "( non-digit *OCTET )" so therefore the last time-field can
+	    // have a trailer
+	    var trailingOK = (i == 2);
+	    var num = parseDigits(parts[i], 1, 2, trailingOK);
+	    if (num === null) {
+	      return null;
+	    }
+	    result[i] = num;
+	  }
+
+	  return result;
+	}
+
+	function parseMonth(token) {
+	  token = String(token).substr(0,3).toLowerCase();
+	  var num = MONTH_TO_NUM[token];
+	  return num >= 0 ? num : null;
+	}
+
+	/*
+	 * RFC6265 S5.1.1 date parser (see RFC for full grammar)
+	 */
+	function parseDate(str) {
+	  if (!str) {
+	    return;
+	  }
+
+	  /* RFC6265 S5.1.1:
+	   * 2. Process each date-token sequentially in the order the date-tokens
+	   * appear in the cookie-date
+	   */
+	  var tokens = str.split(DATE_DELIM);
+	  if (!tokens) {
+	    return;
+	  }
+
+	  var hour = null;
+	  var minute = null;
+	  var second = null;
+	  var dayOfMonth = null;
+	  var month = null;
+	  var year = null;
+
+	  for (var i=0; i<tokens.length; i++) {
+	    var token = tokens[i].trim();
+	    if (!token.length) {
+	      continue;
+	    }
+
+	    var result;
+
+	    /* 2.1. If the found-time flag is not set and the token matches the time
+	     * production, set the found-time flag and set the hour- value,
+	     * minute-value, and second-value to the numbers denoted by the digits in
+	     * the date-token, respectively.  Skip the remaining sub-steps and continue
+	     * to the next date-token.
+	     */
+	    if (second === null) {
+	      result = parseTime(token);
+	      if (result) {
+	        hour = result[0];
+	        minute = result[1];
+	        second = result[2];
+	        continue;
+	      }
+	    }
+
+	    /* 2.2. If the found-day-of-month flag is not set and the date-token matches
+	     * the day-of-month production, set the found-day-of- month flag and set
+	     * the day-of-month-value to the number denoted by the date-token.  Skip
+	     * the remaining sub-steps and continue to the next date-token.
+	     */
+	    if (dayOfMonth === null) {
+	      // "day-of-month = 1*2DIGIT ( non-digit *OCTET )"
+	      result = parseDigits(token, 1, 2, true);
+	      if (result !== null) {
+	        dayOfMonth = result;
+	        continue;
+	      }
+	    }
+
+	    /* 2.3. If the found-month flag is not set and the date-token matches the
+	     * month production, set the found-month flag and set the month-value to
+	     * the month denoted by the date-token.  Skip the remaining sub-steps and
+	     * continue to the next date-token.
+	     */
+	    if (month === null) {
+	      result = parseMonth(token);
+	      if (result !== null) {
+	        month = result;
+	        continue;
+	      }
+	    }
+
+	    /* 2.4. If the found-year flag is not set and the date-token matches the
+	     * year production, set the found-year flag and set the year-value to the
+	     * number denoted by the date-token.  Skip the remaining sub-steps and
+	     * continue to the next date-token.
+	     */
+	    if (year === null) {
+	      // "year = 2*4DIGIT ( non-digit *OCTET )"
+	      result = parseDigits(token, 2, 4, true);
+	      if (result !== null) {
+	        year = result;
+	        /* From S5.1.1:
+	         * 3.  If the year-value is greater than or equal to 70 and less
+	         * than or equal to 99, increment the year-value by 1900.
+	         * 4.  If the year-value is greater than or equal to 0 and less
+	         * than or equal to 69, increment the year-value by 2000.
+	         */
+	        if (year >= 70 && year <= 99) {
+	          year += 1900;
+	        } else if (year >= 0 && year <= 69) {
+	          year += 2000;
+	        }
+	      }
+	    }
+	  }
+
+	  /* RFC 6265 S5.1.1
+	   * "5. Abort these steps and fail to parse the cookie-date if:
+	   *     *  at least one of the found-day-of-month, found-month, found-
+	   *        year, or found-time flags is not set,
+	   *     *  the day-of-month-value is less than 1 or greater than 31,
+	   *     *  the year-value is less than 1601,
+	   *     *  the hour-value is greater than 23,
+	   *     *  the minute-value is greater than 59, or
+	   *     *  the second-value is greater than 59.
+	   *     (Note that leap seconds cannot be represented in this syntax.)"
+	   *
+	   * So, in order as above:
+	   */
+	  if (
+	    dayOfMonth === null || month === null || year === null || second === null ||
+	    dayOfMonth < 1 || dayOfMonth > 31 ||
+	    year < 1601 ||
+	    hour > 23 ||
+	    minute > 59 ||
+	    second > 59
+	  ) {
+	    return;
+	  }
+
+	  return new Date(Date.UTC(year, month, dayOfMonth, hour, minute, second));
+	}
+
+	function formatDate(date) {
+	  var d = date.getUTCDate(); d = d >= 10 ? d : '0'+d;
+	  var h = date.getUTCHours(); h = h >= 10 ? h : '0'+h;
+	  var m = date.getUTCMinutes(); m = m >= 10 ? m : '0'+m;
+	  var s = date.getUTCSeconds(); s = s >= 10 ? s : '0'+s;
+	  return NUM_TO_DAY[date.getUTCDay()] + ', ' +
+	    d+' '+ NUM_TO_MONTH[date.getUTCMonth()] +' '+ date.getUTCFullYear() +' '+
+	    h+':'+m+':'+s+' GMT';
+	}
+
+	// S5.1.2 Canonicalized Host Names
+	function canonicalDomain(str) {
+	  if (str == null) {
+	    return null;
+	  }
+	  str = str.trim().replace(/^\./,''); // S4.1.2.3 & S5.2.3: ignore leading .
+
+	  // convert to IDN if any non-ASCII characters
+	  if (punycode && /[^\u0001-\u007f]/.test(str)) {
+	    str = punycode.toASCII(str);
+	  }
+
+	  return str.toLowerCase();
+	}
+
+	// S5.1.3 Domain Matching
+	function domainMatch(str, domStr, canonicalize) {
+	  if (str == null || domStr == null) {
+	    return null;
+	  }
+	  if (canonicalize !== false) {
+	    str = canonicalDomain(str);
+	    domStr = canonicalDomain(domStr);
+	  }
+
+	  /*
+	   * "The domain string and the string are identical. (Note that both the
+	   * domain string and the string will have been canonicalized to lower case at
+	   * this point)"
+	   */
+	  if (str == domStr) {
+	    return true;
+	  }
+
+	  /* "All of the following [three] conditions hold:" (order adjusted from the RFC) */
+
+	  /* "* The string is a host name (i.e., not an IP address)." */
+	  if (net.isIP(str)) {
+	    return false;
+	  }
+
+	  /* "* The domain string is a suffix of the string" */
+	  var idx = str.indexOf(domStr);
+	  if (idx <= 0) {
+	    return false; // it's a non-match (-1) or prefix (0)
+	  }
+
+	  // e.g "a.b.c".indexOf("b.c") === 2
+	  // 5 === 3+2
+	  if (str.length !== domStr.length + idx) { // it's not a suffix
+	    return false;
+	  }
+
+	  /* "* The last character of the string that is not included in the domain
+	  * string is a %x2E (".") character." */
+	  if (str.substr(idx-1,1) !== '.') {
+	    return false;
+	  }
+
+	  return true;
+	}
+
+
+	// RFC6265 S5.1.4 Paths and Path-Match
+
+	/*
+	 * "The user agent MUST use an algorithm equivalent to the following algorithm
+	 * to compute the default-path of a cookie:"
+	 *
+	 * Assumption: the path (and not query part or absolute uri) is passed in.
+	 */
+	function defaultPath(path) {
+	  // "2. If the uri-path is empty or if the first character of the uri-path is not
+	  // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps.
+	  if (!path || path.substr(0,1) !== "/") {
+	    return "/";
+	  }
+
+	  // "3. If the uri-path contains no more than one %x2F ("/") character, output
+	  // %x2F ("/") and skip the remaining step."
+	  if (path === "/") {
+	    return path;
+	  }
+
+	  var rightSlash = path.lastIndexOf("/");
+	  if (rightSlash === 0) {
+	    return "/";
+	  }
+
+	  // "4. Output the characters of the uri-path from the first character up to,
+	  // but not including, the right-most %x2F ("/")."
+	  return path.slice(0, rightSlash);
+	}
+
+	function trimTerminator(str) {
+	  for (var t = 0; t < TERMINATORS.length; t++) {
+	    var terminatorIdx = str.indexOf(TERMINATORS[t]);
+	    if (terminatorIdx !== -1) {
+	      str = str.substr(0,terminatorIdx);
+	    }
+	  }
+
+	  return str;
+	}
+
+	function parseCookiePair(cookiePair, looseMode) {
+	  cookiePair = trimTerminator(cookiePair);
+
+	  var firstEq = cookiePair.indexOf('=');
+	  if (looseMode) {
+	    if (firstEq === 0) { // '=' is immediately at start
+	      cookiePair = cookiePair.substr(1);
+	      firstEq = cookiePair.indexOf('='); // might still need to split on '='
+	    }
+	  } else { // non-loose mode
+	    if (firstEq <= 0) { // no '=' or is at start
+	      return; // needs to have non-empty "cookie-name"
+	    }
+	  }
+
+	  var cookieName, cookieValue;
+	  if (firstEq <= 0) {
+	    cookieName = "";
+	    cookieValue = cookiePair.trim();
+	  } else {
+	    cookieName = cookiePair.substr(0, firstEq).trim();
+	    cookieValue = cookiePair.substr(firstEq+1).trim();
+	  }
+
+	  if (CONTROL_CHARS.test(cookieName) || CONTROL_CHARS.test(cookieValue)) {
+	    return;
+	  }
+
+	  var c = new Cookie();
+	  c.key = cookieName;
+	  c.value = cookieValue;
+	  return c;
+	}
+
+	function parse(str, options) {
+	  if (!options || typeof options !== 'object') {
+	    options = {};
+	  }
+	  str = str.trim();
+
+	  // We use a regex to parse the "name-value-pair" part of S5.2
+	  var firstSemi = str.indexOf(';'); // S5.2 step 1
+	  var cookiePair = (firstSemi === -1) ? str : str.substr(0, firstSemi);
+	  var c = parseCookiePair(cookiePair, !!options.loose);
+	  if (!c) {
+	    return;
+	  }
+
+	  if (firstSemi === -1) {
+	    return c;
+	  }
+
+	  // S5.2.3 "unparsed-attributes consist of the remainder of the set-cookie-string
+	  // (including the %x3B (";") in question)." plus later on in the same section
+	  // "discard the first ";" and trim".
+	  var unparsed = str.slice(firstSemi + 1).trim();
+
+	  // "If the unparsed-attributes string is empty, skip the rest of these
+	  // steps."
+	  if (unparsed.length === 0) {
+	    return c;
+	  }
+
+	  /*
+	   * S5.2 says that when looping over the items "[p]rocess the attribute-name
+	   * and attribute-value according to the requirements in the following
+	   * subsections" for every item.  Plus, for many of the individual attributes
+	   * in S5.3 it says to use the "attribute-value of the last attribute in the
+	   * cookie-attribute-list".  Therefore, in this implementation, we overwrite
+	   * the previous value.
+	   */
+	  var cookie_avs = unparsed.split(';');
+	  while (cookie_avs.length) {
+	    var av = cookie_avs.shift().trim();
+	    if (av.length === 0) { // happens if ";;" appears
+	      continue;
+	    }
+	    var av_sep = av.indexOf('=');
+	    var av_key, av_value;
+
+	    if (av_sep === -1) {
+	      av_key = av;
+	      av_value = null;
+	    } else {
+	      av_key = av.substr(0,av_sep);
+	      av_value = av.substr(av_sep+1);
+	    }
+
+	    av_key = av_key.trim().toLowerCase();
+
+	    if (av_value) {
+	      av_value = av_value.trim();
+	    }
+
+	    switch(av_key) {
+	    case 'expires': // S5.2.1
+	      if (av_value) {
+	        var exp = parseDate(av_value);
+	        // "If the attribute-value failed to parse as a cookie date, ignore the
+	        // cookie-av."
+	        if (exp) {
+	          // over and underflow not realistically a concern: V8's getTime() seems to
+	          // store something larger than a 32-bit time_t (even with 32-bit node)
+	          c.expires = exp;
+	        }
+	      }
+	      break;
+
+	    case 'max-age': // S5.2.2
+	      if (av_value) {
+	        // "If the first character of the attribute-value is not a DIGIT or a "-"
+	        // character ...[or]... If the remainder of attribute-value contains a
+	        // non-DIGIT character, ignore the cookie-av."
+	        if (/^-?[0-9]+$/.test(av_value)) {
+	          var delta = parseInt(av_value, 10);
+	          // "If delta-seconds is less than or equal to zero (0), let expiry-time
+	          // be the earliest representable date and time."
+	          c.setMaxAge(delta);
+	        }
+	      }
+	      break;
+
+	    case 'domain': // S5.2.3
+	      // "If the attribute-value is empty, the behavior is undefined.  However,
+	      // the user agent SHOULD ignore the cookie-av entirely."
+	      if (av_value) {
+	        // S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E
+	        // (".") character."
+	        var domain = av_value.trim().replace(/^\./, '');
+	        if (domain) {
+	          // "Convert the cookie-domain to lower case."
+	          c.domain = domain.toLowerCase();
+	        }
+	      }
+	      break;
+
+	    case 'path': // S5.2.4
+	      /*
+	       * "If the attribute-value is empty or if the first character of the
+	       * attribute-value is not %x2F ("/"):
+	       *   Let cookie-path be the default-path.
+	       * Otherwise:
+	       *   Let cookie-path be the attribute-value."
+	       *
+	       * We'll represent the default-path as null since it depends on the
+	       * context of the parsing.
+	       */
+	      c.path = av_value && av_value[0] === "/" ? av_value : null;
+	      break;
+
+	    case 'secure': // S5.2.5
+	      /*
+	       * "If the attribute-name case-insensitively matches the string "Secure",
+	       * the user agent MUST append an attribute to the cookie-attribute-list
+	       * with an attribute-name of Secure and an empty attribute-value."
+	       */
+	      c.secure = true;
+	      break;
+
+	    case 'httponly': // S5.2.6 -- effectively the same as 'secure'
+	      c.httpOnly = true;
+	      break;
+
+	    default:
+	      c.extensions = c.extensions || [];
+	      c.extensions.push(av);
+	      break;
+	    }
+	  }
+
+	  return c;
+	}
+
+	// avoid the V8 deoptimization monster!
+	function jsonParse(str) {
+	  var obj;
+	  try {
+	    obj = JSON.parse(str);
+	  } catch (e) {
+	    return e;
+	  }
+	  return obj;
+	}
+
+	function fromJSON(str) {
+	  if (!str) {
+	    return null;
+	  }
+
+	  var obj;
+	  if (typeof str === 'string') {
+	    obj = jsonParse(str);
+	    if (obj instanceof Error) {
+	      return null;
+	    }
+	  } else {
+	    // assume it's an Object
+	    obj = str;
+	  }
+
+	  var c = new Cookie();
+	  for (var i=0; i<Cookie.serializableProperties.length; i++) {
+	    var prop = Cookie.serializableProperties[i];
+	    if (obj[prop] === undefined ||
+	        obj[prop] === Cookie.prototype[prop])
+	    {
+	      continue; // leave as prototype default
+	    }
+
+	    if (prop === 'expires' ||
+	        prop === 'creation' ||
+	        prop === 'lastAccessed')
+	    {
+	      if (obj[prop] === null) {
+	        c[prop] = null;
+	      } else {
+	        c[prop] = obj[prop] == "Infinity" ?
+	          "Infinity" : new Date(obj[prop]);
+	      }
+	    } else {
+	      c[prop] = obj[prop];
+	    }
+	  }
+
+	  return c;
+	}
+
+	/* Section 5.4 part 2:
+	 * "*  Cookies with longer paths are listed before cookies with
+	 *     shorter paths.
+	 *
+	 *  *  Among cookies that have equal-length path fields, cookies with
+	 *     earlier creation-times are listed before cookies with later
+	 *     creation-times."
+	 */
+
+	function cookieCompare(a,b) {
+	  var cmp = 0;
+
+	  // descending for length: b CMP a
+	  var aPathLen = a.path ? a.path.length : 0;
+	  var bPathLen = b.path ? b.path.length : 0;
+	  cmp = bPathLen - aPathLen;
+	  if (cmp !== 0) {
+	    return cmp;
+	  }
+
+	  // ascending for time: a CMP b
+	  var aTime = a.creation ? a.creation.getTime() : MAX_TIME;
+	  var bTime = b.creation ? b.creation.getTime() : MAX_TIME;
+	  cmp = aTime - bTime;
+	  if (cmp !== 0) {
+	    return cmp;
+	  }
+
+	  // break ties for the same millisecond (precision of JavaScript's clock)
+	  cmp = a.creationIndex - b.creationIndex;
+
+	  return cmp;
+	}
+
+	// Gives the permutation of all possible pathMatch()es of a given path. The
+	// array is in longest-to-shortest order.  Handy for indexing.
+	function permutePath(path) {
+	  if (path === '/') {
+	    return ['/'];
+	  }
+	  if (path.lastIndexOf('/') === path.length-1) {
+	    path = path.substr(0,path.length-1);
+	  }
+	  var permutations = [path];
+	  while (path.length > 1) {
+	    var lindex = path.lastIndexOf('/');
+	    if (lindex === 0) {
+	      break;
+	    }
+	    path = path.substr(0,lindex);
+	    permutations.push(path);
+	  }
+	  permutations.push('/');
+	  return permutations;
+	}
+
+	function getCookieContext(url) {
+	  if (url instanceof Object) {
+	    return url;
+	  }
+	  // NOTE: decodeURI will throw on malformed URIs (see GH-32).
+	  // Therefore, we will just skip decoding for such URIs.
+	  try {
+	    url = decodeURI(url);
+	  }
+	  catch(err) {
+	    // Silently swallow error
+	  }
+
+	  return urlParse(url);
+	}
+
+	function Cookie(options) {
+	  options = options || {};
+
+	  Object.keys(options).forEach(function(prop) {
+	    if (Cookie.prototype.hasOwnProperty(prop) &&
+	        Cookie.prototype[prop] !== options[prop] &&
+	        prop.substr(0,1) !== '_')
+	    {
+	      this[prop] = options[prop];
+	    }
+	  }, this);
+
+	  this.creation = this.creation || new Date();
+
+	  // used to break creation ties in cookieCompare():
+	  Object.defineProperty(this, 'creationIndex', {
+	    configurable: false,
+	    enumerable: false, // important for assert.deepEqual checks
+	    writable: true,
+	    value: ++Cookie.cookiesCreated
+	  });
+	}
+
+	Cookie.cookiesCreated = 0; // incremented each time a cookie is created
+
+	Cookie.parse = parse;
+	Cookie.fromJSON = fromJSON;
+
+	Cookie.prototype.key = "";
+	Cookie.prototype.value = "";
+
+	// the order in which the RFC has them:
+	Cookie.prototype.expires = "Infinity"; // coerces to literal Infinity
+	Cookie.prototype.maxAge = null; // takes precedence over expires for TTL
+	Cookie.prototype.domain = null;
+	Cookie.prototype.path = null;
+	Cookie.prototype.secure = false;
+	Cookie.prototype.httpOnly = false;
+	Cookie.prototype.extensions = null;
+
+	// set by the CookieJar:
+	Cookie.prototype.hostOnly = null; // boolean when set
+	Cookie.prototype.pathIsDefault = null; // boolean when set
+	Cookie.prototype.creation = null; // Date when set; defaulted by Cookie.parse
+	Cookie.prototype.lastAccessed = null; // Date when set
+	Object.defineProperty(Cookie.prototype, 'creationIndex', {
+	  configurable: true,
+	  enumerable: false,
+	  writable: true,
+	  value: 0
+	});
+
+	Cookie.serializableProperties = Object.keys(Cookie.prototype)
+	  .filter(function(prop) {
+	    return !(
+	      Cookie.prototype[prop] instanceof Function ||
+	      prop === 'creationIndex' ||
+	      prop.substr(0,1) === '_'
+	    );
+	  });
+
+	Cookie.prototype.inspect = function inspect() {
+	  var now = Date.now();
+	  return 'Cookie="'+this.toString() +
+	    '; hostOnly='+(this.hostOnly != null ? this.hostOnly : '?') +
+	    '; aAge='+(this.lastAccessed ? (now-this.lastAccessed.getTime())+'ms' : '?') +
+	    '; cAge='+(this.creation ? (now-this.creation.getTime())+'ms' : '?') +
+	    '"';
+	};
+
+	// Use the new custom inspection symbol to add the custom inspect function if
+	// available.
+	if (util.inspect.custom) {
+	  Cookie.prototype[util.inspect.custom] = Cookie.prototype.inspect;
+	}
+
+	Cookie.prototype.toJSON = function() {
+	  var obj = {};
+
+	  var props = Cookie.serializableProperties;
+	  for (var i=0; i<props.length; i++) {
+	    var prop = props[i];
+	    if (this[prop] === Cookie.prototype[prop]) {
+	      continue; // leave as prototype default
+	    }
+
+	    if (prop === 'expires' ||
+	        prop === 'creation' ||
+	        prop === 'lastAccessed')
+	    {
+	      if (this[prop] === null) {
+	        obj[prop] = null;
+	      } else {
+	        obj[prop] = this[prop] == "Infinity" ? // intentionally not ===
+	          "Infinity" : this[prop].toISOString();
+	      }
+	    } else if (prop === 'maxAge') {
+	      if (this[prop] !== null) {
+	        // again, intentionally not ===
+	        obj[prop] = (this[prop] == Infinity || this[prop] == -Infinity) ?
+	          this[prop].toString() : this[prop];
+	      }
+	    } else {
+	      if (this[prop] !== Cookie.prototype[prop]) {
+	        obj[prop] = this[prop];
+	      }
+	    }
+	  }
+
+	  return obj;
+	};
+
+	Cookie.prototype.clone = function() {
+	  return fromJSON(this.toJSON());
+	};
+
+	Cookie.prototype.validate = function validate() {
+	  if (!COOKIE_OCTETS.test(this.value)) {
+	    return false;
+	  }
+	  if (this.expires != Infinity && !(this.expires instanceof Date) && !parseDate(this.expires)) {
+	    return false;
+	  }
+	  if (this.maxAge != null && this.maxAge <= 0) {
+	    return false; // "Max-Age=" non-zero-digit *DIGIT
+	  }
+	  if (this.path != null && !PATH_VALUE.test(this.path)) {
+	    return false;
+	  }
+
+	  var cdomain = this.cdomain();
+	  if (cdomain) {
+	    if (cdomain.match(/\.$/)) {
+	      return false; // S4.1.2.3 suggests that this is bad. domainMatch() tests confirm this
+	    }
+	    var suffix = pubsuffix.getPublicSuffix(cdomain);
+	    if (suffix == null) { // it's a public suffix
+	      return false;
+	    }
+	  }
+	  return true;
+	};
+
+	Cookie.prototype.setExpires = function setExpires(exp) {
+	  if (exp instanceof Date) {
+	    this.expires = exp;
+	  } else {
+	    this.expires = parseDate(exp) || "Infinity";
+	  }
+	};
+
+	Cookie.prototype.setMaxAge = function setMaxAge(age) {
+	  if (age === Infinity || age === -Infinity) {
+	    this.maxAge = age.toString(); // so JSON.stringify() works
+	  } else {
+	    this.maxAge = age;
+	  }
+	};
+
+	// gives Cookie header format
+	Cookie.prototype.cookieString = function cookieString() {
+	  var val = this.value;
+	  if (val == null) {
+	    val = '';
+	  }
+	  if (this.key === '') {
+	    return val;
+	  }
+	  return this.key+'='+val;
+	};
+
+	// gives Set-Cookie header format
+	Cookie.prototype.toString = function toString() {
+	  var str = this.cookieString();
+
+	  if (this.expires != Infinity) {
+	    if (this.expires instanceof Date) {
+	      str += '; Expires='+formatDate(this.expires);
+	    } else {
+	      str += '; Expires='+this.expires;
+	    }
+	  }
+
+	  if (this.maxAge != null && this.maxAge != Infinity) {
+	    str += '; Max-Age='+this.maxAge;
+	  }
+
+	  if (this.domain && !this.hostOnly) {
+	    str += '; Domain='+this.domain;
+	  }
+	  if (this.path) {
+	    str += '; Path='+this.path;
+	  }
+
+	  if (this.secure) {
+	    str += '; Secure';
+	  }
+	  if (this.httpOnly) {
+	    str += '; HttpOnly';
+	  }
+	  if (this.extensions) {
+	    this.extensions.forEach(function(ext) {
+	      str += '; '+ext;
+	    });
+	  }
+
+	  return str;
+	};
+
+	// TTL() partially replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
+	// elsewhere)
+	// S5.3 says to give the "latest representable date" for which we use Infinity
+	// For "expired" we use 0
+	Cookie.prototype.TTL = function TTL(now) {
+	  /* RFC6265 S4.1.2.2 If a cookie has both the Max-Age and the Expires
+	   * attribute, the Max-Age attribute has precedence and controls the
+	   * expiration date of the cookie.
+	   * (Concurs with S5.3 step 3)
+	   */
+	  if (this.maxAge != null) {
+	    return this.maxAge<=0 ? 0 : this.maxAge*1000;
+	  }
+
+	  var expires = this.expires;
+	  if (expires != Infinity) {
+	    if (!(expires instanceof Date)) {
+	      expires = parseDate(expires) || Infinity;
+	    }
+
+	    if (expires == Infinity) {
+	      return Infinity;
+	    }
+
+	    return expires.getTime() - (now || Date.now());
+	  }
+
+	  return Infinity;
+	};
+
+	// expiryTime() replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
+	// elsewhere)
+	Cookie.prototype.expiryTime = function expiryTime(now) {
+	  if (this.maxAge != null) {
+	    var relativeTo = now || this.creation || new Date();
+	    var age = (this.maxAge <= 0) ? -Infinity : this.maxAge*1000;
+	    return relativeTo.getTime() + age;
+	  }
+
+	  if (this.expires == Infinity) {
+	    return Infinity;
+	  }
+	  return this.expires.getTime();
+	};
+
+	// expiryDate() replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
+	// elsewhere), except it returns a Date
+	Cookie.prototype.expiryDate = function expiryDate(now) {
+	  var millisec = this.expiryTime(now);
+	  if (millisec == Infinity) {
+	    return new Date(MAX_TIME);
+	  } else if (millisec == -Infinity) {
+	    return new Date(MIN_TIME);
+	  } else {
+	    return new Date(millisec);
+	  }
+	};
+
+	// This replaces the "persistent-flag" parts of S5.3 step 3
+	Cookie.prototype.isPersistent = function isPersistent() {
+	  return (this.maxAge != null || this.expires != Infinity);
+	};
+
+	// Mostly S5.1.2 and S5.2.3:
+	Cookie.prototype.cdomain =
+	Cookie.prototype.canonicalizedDomain = function canonicalizedDomain() {
+	  if (this.domain == null) {
+	    return null;
+	  }
+	  return canonicalDomain(this.domain);
+	};
+
+	function CookieJar(store, options) {
+	  if (typeof options === "boolean") {
+	    options = {rejectPublicSuffixes: options};
+	  } else if (options == null) {
+	    options = {};
+	  }
+	  if (options.rejectPublicSuffixes != null) {
+	    this.rejectPublicSuffixes = options.rejectPublicSuffixes;
+	  }
+	  if (options.looseMode != null) {
+	    this.enableLooseMode = options.looseMode;
+	  }
+
+	  if (!store) {
+	    store = new MemoryCookieStore();
+	  }
+	  this.store = store;
+	}
+	CookieJar.prototype.store = null;
+	CookieJar.prototype.rejectPublicSuffixes = true;
+	CookieJar.prototype.enableLooseMode = false;
+	var CAN_BE_SYNC = [];
+
+	CAN_BE_SYNC.push('setCookie');
+	CookieJar.prototype.setCookie = function(cookie, url, options, cb) {
+	  var err;
+	  var context = getCookieContext(url);
+	  if (options instanceof Function) {
+	    cb = options;
+	    options = {};
+	  }
+
+	  var host = canonicalDomain(context.hostname);
+	  var loose = this.enableLooseMode;
+	  if (options.loose != null) {
+	    loose = options.loose;
+	  }
+
+	  // S5.3 step 1
+	  if (!(cookie instanceof Cookie)) {
+	    cookie = Cookie.parse(cookie, { loose: loose });
+	  }
+	  if (!cookie) {
+	    err = new Error("Cookie failed to parse");
+	    return cb(options.ignoreError ? null : err);
+	  }
+
+	  // S5.3 step 2
+	  var now = options.now || new Date(); // will assign later to save effort in the face of errors
+
+	  // S5.3 step 3: NOOP; persistent-flag and expiry-time is handled by getCookie()
+
+	  // S5.3 step 4: NOOP; domain is null by default
+
+	  // S5.3 step 5: public suffixes
+	  if (this.rejectPublicSuffixes && cookie.domain) {
+	    var suffix = pubsuffix.getPublicSuffix(cookie.cdomain());
+	    if (suffix == null) { // e.g. "com"
+	      err = new Error("Cookie has domain set to a public suffix");
+	      return cb(options.ignoreError ? null : err);
+	    }
+	  }
+
+	  // S5.3 step 6:
+	  if (cookie.domain) {
+	    if (!domainMatch(host, cookie.cdomain(), false)) {
+	      err = new Error("Cookie not in this host's domain. Cookie:"+cookie.cdomain()+" Request:"+host);
+	      return cb(options.ignoreError ? null : err);
+	    }
+
+	    if (cookie.hostOnly == null) { // don't reset if already set
+	      cookie.hostOnly = false;
+	    }
+
+	  } else {
+	    cookie.hostOnly = true;
+	    cookie.domain = host;
+	  }
+
+	  //S5.2.4 If the attribute-value is empty or if the first character of the
+	  //attribute-value is not %x2F ("/"):
+	  //Let cookie-path be the default-path.
+	  if (!cookie.path || cookie.path[0] !== '/') {
+	    cookie.path = defaultPath(context.pathname);
+	    cookie.pathIsDefault = true;
+	  }
+
+	  // S5.3 step 8: NOOP; secure attribute
+	  // S5.3 step 9: NOOP; httpOnly attribute
+
+	  // S5.3 step 10
+	  if (options.http === false && cookie.httpOnly) {
+	    err = new Error("Cookie is HttpOnly and this isn't an HTTP API");
+	    return cb(options.ignoreError ? null : err);
+	  }
+
+	  var store = this.store;
+
+	  if (!store.updateCookie) {
+	    store.updateCookie = function(oldCookie, newCookie, cb) {
+	      this.putCookie(newCookie, cb);
+	    };
+	  }
+
+	  function withCookie(err, oldCookie) {
+	    if (err) {
+	      return cb(err);
+	    }
+
+	    var next = function(err) {
+	      if (err) {
+	        return cb(err);
+	      } else {
+	        cb(null, cookie);
+	      }
+	    };
+
+	    if (oldCookie) {
+	      // S5.3 step 11 - "If the cookie store contains a cookie with the same name,
+	      // domain, and path as the newly created cookie:"
+	      if (options.http === false && oldCookie.httpOnly) { // step 11.2
+	        err = new Error("old Cookie is HttpOnly and this isn't an HTTP API");
+	        return cb(options.ignoreError ? null : err);
+	      }
+	      cookie.creation = oldCookie.creation; // step 11.3
+	      cookie.creationIndex = oldCookie.creationIndex; // preserve tie-breaker
+	      cookie.lastAccessed = now;
+	      // Step 11.4 (delete cookie) is implied by just setting the new one:
+	      store.updateCookie(oldCookie, cookie, next); // step 12
+
+	    } else {
+	      cookie.creation = cookie.lastAccessed = now;
+	      store.putCookie(cookie, next); // step 12
+	    }
+	  }
+
+	  store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie);
+	};
+
+	// RFC6365 S5.4
+	CAN_BE_SYNC.push('getCookies');
+	CookieJar.prototype.getCookies = function(url, options, cb) {
+	  var context = getCookieContext(url);
+	  if (options instanceof Function) {
+	    cb = options;
+	    options = {};
+	  }
+
+	  var host = canonicalDomain(context.hostname);
+	  var path = context.pathname || '/';
+
+	  var secure = options.secure;
+	  if (secure == null && context.protocol &&
+	      (context.protocol == 'https:' || context.protocol == 'wss:'))
+	  {
+	    secure = true;
+	  }
+
+	  var http = options.http;
+	  if (http == null) {
+	    http = true;
+	  }
+
+	  var now = options.now || Date.now();
+	  var expireCheck = options.expire !== false;
+	  var allPaths = !!options.allPaths;
+	  var store = this.store;
+
+	  function matchingCookie(c) {
+	    // "Either:
+	    //   The cookie's host-only-flag is true and the canonicalized
+	    //   request-host is identical to the cookie's domain.
+	    // Or:
+	    //   The cookie's host-only-flag is false and the canonicalized
+	    //   request-host domain-matches the cookie's domain."
+	    if (c.hostOnly) {
+	      if (c.domain != host) {
+	        return false;
+	      }
+	    } else {
+	      if (!domainMatch(host, c.domain, false)) {
+	        return false;
+	      }
+	    }
+
+	    // "The request-uri's path path-matches the cookie's path."
+	    if (!allPaths && !pathMatch(path, c.path)) {
+	      return false;
+	    }
+
+	    // "If the cookie's secure-only-flag is true, then the request-uri's
+	    // scheme must denote a "secure" protocol"
+	    if (c.secure && !secure) {
+	      return false;
+	    }
+
+	    // "If the cookie's http-only-flag is true, then exclude the cookie if the
+	    // cookie-string is being generated for a "non-HTTP" API"
+	    if (c.httpOnly && !http) {
+	      return false;
+	    }
+
+	    // deferred from S5.3
+	    // non-RFC: allow retention of expired cookies by choice
+	    if (expireCheck && c.expiryTime() <= now) {
+	      store.removeCookie(c.domain, c.path, c.key, function(){}); // result ignored
+	      return false;
+	    }
+
+	    return true;
+	  }
+
+	  store.findCookies(host, allPaths ? null : path, function(err,cookies) {
+	    if (err) {
+	      return cb(err);
+	    }
+
+	    cookies = cookies.filter(matchingCookie);
+
+	    // sorting of S5.4 part 2
+	    if (options.sort !== false) {
+	      cookies = cookies.sort(cookieCompare);
+	    }
+
+	    // S5.4 part 3
+	    var now = new Date();
+	    cookies.forEach(function(c) {
+	      c.lastAccessed = now;
+	    });
+	    // TODO persist lastAccessed
+
+	    cb(null,cookies);
+	  });
+	};
+
+	CAN_BE_SYNC.push('getCookieString');
+	CookieJar.prototype.getCookieString = function(/*..., cb*/) {
+	  var args = Array.prototype.slice.call(arguments,0);
+	  var cb = args.pop();
+	  var next = function(err,cookies) {
+	    if (err) {
+	      cb(err);
+	    } else {
+	      cb(null, cookies
+	        .sort(cookieCompare)
+	        .map(function(c){
+	          return c.cookieString();
+	        })
+	        .join('; '));
+	    }
+	  };
+	  args.push(next);
+	  this.getCookies.apply(this,args);
+	};
+
+	CAN_BE_SYNC.push('getSetCookieStrings');
+	CookieJar.prototype.getSetCookieStrings = function(/*..., cb*/) {
+	  var args = Array.prototype.slice.call(arguments,0);
+	  var cb = args.pop();
+	  var next = function(err,cookies) {
+	    if (err) {
+	      cb(err);
+	    } else {
+	      cb(null, cookies.map(function(c){
+	        return c.toString();
+	      }));
+	    }
+	  };
+	  args.push(next);
+	  this.getCookies.apply(this,args);
+	};
+
+	CAN_BE_SYNC.push('serialize');
+	CookieJar.prototype.serialize = function(cb) {
+	  var type = this.store.constructor.name;
+	  if (type === 'Object') {
+	    type = null;
+	  }
+
+	  // update README.md "Serialization Format" if you change this, please!
+	  var serialized = {
+	    // The version of tough-cookie that serialized this jar. Generally a good
+	    // practice since future versions can make data import decisions based on
+	    // known past behavior. When/if this matters, use `semver`.
+	    version: 'tough-cookie@'+VERSION,
+
+	    // add the store type, to make humans happy:
+	    storeType: type,
+
+	    // CookieJar configuration:
+	    rejectPublicSuffixes: !!this.rejectPublicSuffixes,
+
+	    // this gets filled from getAllCookies:
+	    cookies: []
+	  };
+
+	  if (!(this.store.getAllCookies &&
+	        typeof this.store.getAllCookies === 'function'))
+	  {
+	    return cb(new Error('store does not support getAllCookies and cannot be serialized'));
+	  }
+
+	  this.store.getAllCookies(function(err,cookies) {
+	    if (err) {
+	      return cb(err);
+	    }
+
+	    serialized.cookies = cookies.map(function(cookie) {
+	      // convert to serialized 'raw' cookies
+	      cookie = (cookie instanceof Cookie) ? cookie.toJSON() : cookie;
+
+	      // Remove the index so new ones get assigned during deserialization
+	      delete cookie.creationIndex;
+
+	      return cookie;
+	    });
+
+	    return cb(null, serialized);
+	  });
+	};
+
+	// well-known name that JSON.stringify calls
+	CookieJar.prototype.toJSON = function() {
+	  return this.serializeSync();
+	};
+
+	// use the class method CookieJar.deserialize instead of calling this directly
+	CAN_BE_SYNC.push('_importCookies');
+	CookieJar.prototype._importCookies = function(serialized, cb) {
+	  var jar = this;
+	  var cookies = serialized.cookies;
+	  if (!cookies || !Array.isArray(cookies)) {
+	    return cb(new Error('serialized jar has no cookies array'));
+	  }
+	  cookies = cookies.slice(); // do not modify the original
+
+	  function putNext(err) {
+	    if (err) {
+	      return cb(err);
+	    }
+
+	    if (!cookies.length) {
+	      return cb(err, jar);
+	    }
+
+	    var cookie;
+	    try {
+	      cookie = fromJSON(cookies.shift());
+	    } catch (e) {
+	      return cb(e);
+	    }
+
+	    if (cookie === null) {
+	      return putNext(null); // skip this cookie
+	    }
+
+	    jar.store.putCookie(cookie, putNext);
+	  }
+
+	  putNext();
+	};
+
+	CookieJar.deserialize = function(strOrObj, store, cb) {
+	  if (arguments.length !== 3) {
+	    // store is optional
+	    cb = store;
+	    store = null;
+	  }
+
+	  var serialized;
+	  if (typeof strOrObj === 'string') {
+	    serialized = jsonParse(strOrObj);
+	    if (serialized instanceof Error) {
+	      return cb(serialized);
+	    }
+	  } else {
+	    serialized = strOrObj;
+	  }
+
+	  var jar = new CookieJar(store, serialized.rejectPublicSuffixes);
+	  jar._importCookies(serialized, function(err) {
+	    if (err) {
+	      return cb(err);
+	    }
+	    cb(null, jar);
+	  });
+	};
+
+	CookieJar.deserializeSync = function(strOrObj, store) {
+	  var serialized = typeof strOrObj === 'string' ?
+	    JSON.parse(strOrObj) : strOrObj;
+	  var jar = new CookieJar(store, serialized.rejectPublicSuffixes);
+
+	  // catch this mistake early:
+	  if (!jar.store.synchronous) {
+	    throw new Error('CookieJar store is not synchronous; use async API instead.');
+	  }
+
+	  jar._importCookiesSync(serialized);
+	  return jar;
+	};
+	CookieJar.fromJSON = CookieJar.deserializeSync;
+
+	CAN_BE_SYNC.push('clone');
+	CookieJar.prototype.clone = function(newStore, cb) {
+	  if (arguments.length === 1) {
+	    cb = newStore;
+	    newStore = null;
+	  }
+
+	  this.serialize(function(err,serialized) {
+	    if (err) {
+	      return cb(err);
+	    }
+	    CookieJar.deserialize(newStore, serialized, cb);
+	  });
+	};
+
+	// Use a closure to provide a true imperative API for synchronous stores.
+	function syncWrap(method) {
+	  return function() {
+	    if (!this.store.synchronous) {
+	      throw new Error('CookieJar store is not synchronous; use async API instead.');
+	    }
+
+	    var args = Array.prototype.slice.call(arguments);
+	    var syncErr, syncResult;
+	    args.push(function syncCb(err, result) {
+	      syncErr = err;
+	      syncResult = result;
+	    });
+	    this[method].apply(this, args);
+
+	    if (syncErr) {
+	      throw syncErr;
+	    }
+	    return syncResult;
+	  };
+	}
+
+	// wrap all declared CAN_BE_SYNC methods in the sync wrapper
+	CAN_BE_SYNC.forEach(function(method) {
+	  CookieJar.prototype[method+'Sync'] = syncWrap(method);
+	});
+
+	exports.CookieJar = CookieJar;
+	exports.Cookie = Cookie;
+	exports.Store = Store;
+	exports.MemoryCookieStore = MemoryCookieStore;
+	exports.parseDate = parseDate;
+	exports.formatDate = formatDate;
+	exports.parse = parse;
+	exports.fromJSON = fromJSON;
+	exports.domainMatch = domainMatch;
+	exports.defaultPath = defaultPath;
+	exports.pathMatch = pathMatch;
+	exports.getPublicSuffix = pubsuffix.getPublicSuffix;
+	exports.cookieCompare = cookieCompare;
+	exports.permuteDomain = __webpack_require__(19).permuteDomain;
+	exports.permutePath = permutePath;
+	exports.canonicalDomain = canonicalDomain;
+
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports) {
+
+	/*
+	* Tests if a given ip or host string is a valid IPv4 or IPv6 address.
+	* Regex found at: https://stackoverflow.com/questions/9208814/validate-ipv4-ipv6-and-hostname
+	*/
+
+	var patternIPv4 = /^\s*((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\s*$/g;
+	var patternIPv6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/g;
+
+	function isIPv4(hostOrIp) {
+	  return hostOrIp.match(patternIPv4) ? true : false;
+	}
+
+	function isIPv6(hostOrIp) {
+	  return hostOrIp.match(patternIPv6) ? true : false;
+	}
+
+	function isIP(hostOrIp) {
+	  if (isIPv4(hostOrIp)) {
+	    return 4;
+	  }
+
+	  if (isIPv6(hostOrIp)) {
+	    return 6;
+	  }
+
+	  return 0;
+	}
+
+	module.exports = {
+	  isIPv4: isIPv4,
+	  isIPv6: isIPv6,
+	  isIP: isIP
+	};
+
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports, __webpack_require__) {
+
+	// Copyright Joyent, Inc. and other Node contributors.
+	//
+	// Permission is hereby granted, free of charge, to any person obtaining a
+	// copy of this software and associated documentation files (the
+	// "Software"), to deal in the Software without restriction, including
+	// without limitation the rights to use, copy, modify, merge, publish,
+	// distribute, sublicense, and/or sell copies of the Software, and to permit
+	// persons to whom the Software is furnished to do so, subject to the
+	// following conditions:
+	//
+	// The above copyright notice and this permission notice shall be included
+	// in all copies or substantial portions of the Software.
+	//
+	// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+	// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+	// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+	// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+	// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+	// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+	// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+	'use strict';
+
+	var punycode = __webpack_require__(3);
+	var util = __webpack_require__(5);
+
+	exports.parse = urlParse;
+	exports.resolve = urlResolve;
+	exports.resolveObject = urlResolveObject;
+	exports.format = urlFormat;
+
+	exports.Url = Url;
+
+	function Url() {
+	  this.protocol = null;
+	  this.slashes = null;
+	  this.auth = null;
+	  this.host = null;
+	  this.port = null;
+	  this.hostname = null;
+	  this.hash = null;
+	  this.search = null;
+	  this.query = null;
+	  this.pathname = null;
+	  this.path = null;
+	  this.href = null;
+	}
+
+	// Reference: RFC 3986, RFC 1808, RFC 2396
+
+	// define these here so at least they only have to be
+	// compiled once on the first module load.
+	var protocolPattern = /^([a-z0-9.+-]+:)/i,
+	    portPattern = /:[0-9]*$/,
+
+	    // Special case for a simple path URL
+	    simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,
+
+	    // RFC 2396: characters reserved for delimiting URLs.
+	    // We actually just auto-escape these.
+	    delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],
+
+	    // RFC 2396: characters not allowed for various reasons.
+	    unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),
+
+	    // Allowed by RFCs, but cause of XSS attacks.  Always escape these.
+	    autoEscape = ['\''].concat(unwise),
+	    // Characters that are never ever allowed in a hostname.
+	    // Note that any invalid chars are also handled, but these
+	    // are the ones that are *expected* to be seen, so we fast-path
+	    // them.
+	    nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
+	    hostEndingChars = ['/', '?', '#'],
+	    hostnameMaxLen = 255,
+	    hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,
+	    hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,
+	    // protocols that can allow "unsafe" and "unwise" chars.
+	    unsafeProtocol = {
+	      'javascript': true,
+	      'javascript:': true
+	    },
+	    // protocols that never have a hostname.
+	    hostlessProtocol = {
+	      'javascript': true,
+	      'javascript:': true
+	    },
+	    // protocols that always contain a // bit.
+	    slashedProtocol = {
+	      'http': true,
+	      'https': true,
+	      'ftp': true,
+	      'gopher': true,
+	      'file': true,
+	      'http:': true,
+	      'https:': true,
+	      'ftp:': true,
+	      'gopher:': true,
+	      'file:': true
+	    },
+	    querystring = __webpack_require__(6);
+
+	function urlParse(url, parseQueryString, slashesDenoteHost) {
+	  if (url && util.isObject(url) && url instanceof Url) return url;
+
+	  var u = new Url;
+	  u.parse(url, parseQueryString, slashesDenoteHost);
+	  return u;
+	}
+
+	Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
+	  if (!util.isString(url)) {
+	    throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
+	  }
+
+	  // Copy chrome, IE, opera backslash-handling behavior.
+	  // Back slashes before the query string get converted to forward slashes
+	  // See: https://code.google.com/p/chromium/issues/detail?id=25916
+	  var queryIndex = url.indexOf('?'),
+	      splitter =
+	          (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',
+	      uSplit = url.split(splitter),
+	      slashRegex = /\\/g;
+	  uSplit[0] = uSplit[0].replace(slashRegex, '/');
+	  url = uSplit.join(splitter);
+
+	  var rest = url;
+
+	  // trim before proceeding.
+	  // This is to support parse stuff like "  http://foo.com  \n"
+	  rest = rest.trim();
+
+	  if (!slashesDenoteHost && url.split('#').length === 1) {
+	    // Try fast path regexp
+	    var simplePath = simplePathPattern.exec(rest);
+	    if (simplePath) {
+	      this.path = rest;
+	      this.href = rest;
+	      this.pathname = simplePath[1];
+	      if (simplePath[2]) {
+	        this.search = simplePath[2];
+	        if (parseQueryString) {
+	          this.query = querystring.parse(this.search.substr(1));
+	        } else {
+	          this.query = this.search.substr(1);
+	        }
+	      } else if (parseQueryString) {
+	        this.search = '';
+	        this.query = {};
+	      }
+	      return this;
+	    }
+	  }
+
+	  var proto = protocolPattern.exec(rest);
+	  if (proto) {
+	    proto = proto[0];
+	    var lowerProto = proto.toLowerCase();
+	    this.protocol = lowerProto;
+	    rest = rest.substr(proto.length);
+	  }
+
+	  // figure out if it's got a host
+	  // user@server is *always* interpreted as a hostname, and url
+	  // resolution will treat //foo/bar as host=foo,path=bar because that's
+	  // how the browser resolves relative URLs.
+	  if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
+	    var slashes = rest.substr(0, 2) === '//';
+	    if (slashes && !(proto && hostlessProtocol[proto])) {
+	      rest = rest.substr(2);
+	      this.slashes = true;
+	    }
+	  }
+
+	  if (!hostlessProtocol[proto] &&
+	      (slashes || (proto && !slashedProtocol[proto]))) {
+
+	    // there's a hostname.
+	    // the first instance of /, ?, ;, or # ends the host.
+	    //
+	    // If there is an @ in the hostname, then non-host chars *are* allowed
+	    // to the left of the last @ sign, unless some host-ending character
+	    // comes *before* the @-sign.
+	    // URLs are obnoxious.
+	    //
+	    // ex:
+	    // http://a@b@c/ => user:a@b host:c
+	    // http://a@b?@c => user:a host:c path:/?@c
+
+	    // v0.12 TODO(isaacs): This is not quite how Chrome does things.
+	    // Review our test case against browsers more comprehensively.
+
+	    // find the first instance of any hostEndingChars
+	    var hostEnd = -1;
+	    for (var i = 0; i < hostEndingChars.length; i++) {
+	      var hec = rest.indexOf(hostEndingChars[i]);
+	      if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
+	        hostEnd = hec;
+	    }
+
+	    // at this point, either we have an explicit point where the
+	    // auth portion cannot go past, or the last @ char is the decider.
+	    var auth, atSign;
+	    if (hostEnd === -1) {
+	      // atSign can be anywhere.
+	      atSign = rest.lastIndexOf('@');
+	    } else {
+	      // atSign must be in auth portion.
+	      // http://a@b/c@d => host:b auth:a path:/c@d
+	      atSign = rest.lastIndexOf('@', hostEnd);
+	    }
+
+	    // Now we have a portion which is definitely the auth.
+	    // Pull that off.
+	    if (atSign !== -1) {
+	      auth = rest.slice(0, atSign);
+	      rest = rest.slice(atSign + 1);
+	      this.auth = decodeURIComponent(auth);
+	    }
+
+	    // the host is the remaining to the left of the first non-host char
+	    hostEnd = -1;
+	    for (var i = 0; i < nonHostChars.length; i++) {
+	      var hec = rest.indexOf(nonHostChars[i]);
+	      if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
+	        hostEnd = hec;
+	    }
+	    // if we still have not hit it, then the entire thing is a host.
+	    if (hostEnd === -1)
+	      hostEnd = rest.length;
+
+	    this.host = rest.slice(0, hostEnd);
+	    rest = rest.slice(hostEnd);
+
+	    // pull out port.
+	    this.parseHost();
+
+	    // we've indicated that there is a hostname,
+	    // so even if it's empty, it has to be present.
+	    this.hostname = this.hostname || '';
+
+	    // if hostname begins with [ and ends with ]
+	    // assume that it's an IPv6 address.
+	    var ipv6Hostname = this.hostname[0] === '[' &&
+	        this.hostname[this.hostname.length - 1] === ']';
+
+	    // validate a little.
+	    if (!ipv6Hostname) {
+	      var hostparts = this.hostname.split(/\./);
+	      for (var i = 0, l = hostparts.length; i < l; i++) {
+	        var part = hostparts[i];
+	        if (!part) continue;
+	        if (!part.match(hostnamePartPattern)) {
+	          var newpart = '';
+	          for (var j = 0, k = part.length; j < k; j++) {
+	            if (part.charCodeAt(j) > 127) {
+	              // we replace non-ASCII char with a temporary placeholder
+	              // we need this to make sure size of hostname is not
+	              // broken by replacing non-ASCII by nothing
+	              newpart += 'x';
+	            } else {
+	              newpart += part[j];
+	            }
+	          }
+	          // we test again with ASCII char only
+	          if (!newpart.match(hostnamePartPattern)) {
+	            var validParts = hostparts.slice(0, i);
+	            var notHost = hostparts.slice(i + 1);
+	            var bit = part.match(hostnamePartStart);
+	            if (bit) {
+	              validParts.push(bit[1]);
+	              notHost.unshift(bit[2]);
+	            }
+	            if (notHost.length) {
+	              rest = '/' + notHost.join('.') + rest;
+	            }
+	            this.hostname = validParts.join('.');
+	            break;
+	          }
+	        }
+	      }
+	    }
+
+	    if (this.hostname.length > hostnameMaxLen) {
+	      this.hostname = '';
+	    } else {
+	      // hostnames are always lower case.
+	      this.hostname = this.hostname.toLowerCase();
+	    }
+
+	    if (!ipv6Hostname) {
+	      // IDNA Support: Returns a punycoded representation of "domain".
+	      // It only converts parts of the domain name that
+	      // have non-ASCII characters, i.e. it doesn't matter if
+	      // you call it with a domain that already is ASCII-only.
+	      this.hostname = punycode.toASCII(this.hostname);
+	    }
+
+	    var p = this.port ? ':' + this.port : '';
+	    var h = this.hostname || '';
+	    this.host = h + p;
+	    this.href += this.host;
+
+	    // strip [ and ] from the hostname
+	    // the host field still retains them, though
+	    if (ipv6Hostname) {
+	      this.hostname = this.hostname.substr(1, this.hostname.length - 2);
+	      if (rest[0] !== '/') {
+	        rest = '/' + rest;
+	      }
+	    }
+	  }
+
+	  // now rest is set to the post-host stuff.
+	  // chop off any delim chars.
+	  if (!unsafeProtocol[lowerProto]) {
+
+	    // First, make 100% sure that any "autoEscape" chars get
+	    // escaped, even if encodeURIComponent doesn't think they
+	    // need to be.
+	    for (var i = 0, l = autoEscape.length; i < l; i++) {
+	      var ae = autoEscape[i];
+	      if (rest.indexOf(ae) === -1)
+	        continue;
+	      var esc = encodeURIComponent(ae);
+	      if (esc === ae) {
+	        esc = escape(ae);
+	      }
+	      rest = rest.split(ae).join(esc);
+	    }
+	  }
+
+
+	  // chop off from the tail first.
+	  var hash = rest.indexOf('#');
+	  if (hash !== -1) {
+	    // got a fragment string.
+	    this.hash = rest.substr(hash);
+	    rest = rest.slice(0, hash);
+	  }
+	  var qm = rest.indexOf('?');
+	  if (qm !== -1) {
+	    this.search = rest.substr(qm);
+	    this.query = rest.substr(qm + 1);
+	    if (parseQueryString) {
+	      this.query = querystring.parse(this.query);
+	    }
+	    rest = rest.slice(0, qm);
+	  } else if (parseQueryString) {
+	    // no query string, but parseQueryString still requested
+	    this.search = '';
+	    this.query = {};
+	  }
+	  if (rest) this.pathname = rest;
+	  if (slashedProtocol[lowerProto] &&
+	      this.hostname && !this.pathname) {
+	    this.pathname = '/';
+	  }
+
+	  //to support http.request
+	  if (this.pathname || this.search) {
+	    var p = this.pathname || '';
+	    var s = this.search || '';
+	    this.path = p + s;
+	  }
+
+	  // finally, reconstruct the href based on what has been validated.
+	  this.href = this.format();
+	  return this;
+	};
+
+	// format a parsed object into a url string
+	function urlFormat(obj) {
+	  // ensure it's an object, and not a string url.
+	  // If it's an obj, this is a no-op.
+	  // this way, you can call url_format() on strings
+	  // to clean up potentially wonky urls.
+	  if (util.isString(obj)) obj = urlParse(obj);
+	  if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
+	  return obj.format();
+	}
+
+	Url.prototype.format = function() {
+	  var auth = this.auth || '';
+	  if (auth) {
+	    auth = encodeURIComponent(auth);
+	    auth = auth.replace(/%3A/i, ':');
+	    auth += '@';
+	  }
+
+	  var protocol = this.protocol || '',
+	      pathname = this.pathname || '',
+	      hash = this.hash || '',
+	      host = false,
+	      query = '';
+
+	  if (this.host) {
+	    host = auth + this.host;
+	  } else if (this.hostname) {
+	    host = auth + (this.hostname.indexOf(':') === -1 ?
+	        this.hostname :
+	        '[' + this.hostname + ']');
+	    if (this.port) {
+	      host += ':' + this.port;
+	    }
+	  }
+
+	  if (this.query &&
+	      util.isObject(this.query) &&
+	      Object.keys(this.query).length) {
+	    query = querystring.stringify(this.query);
+	  }
+
+	  var search = this.search || (query && ('?' + query)) || '';
+
+	  if (protocol && protocol.substr(-1) !== ':') protocol += ':';
+
+	  // only the slashedProtocols get the //.  Not mailto:, xmpp:, etc.
+	  // unless they had them to begin with.
+	  if (this.slashes ||
+	      (!protocol || slashedProtocol[protocol]) && host !== false) {
+	    host = '//' + (host || '');
+	    if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
+	  } else if (!host) {
+	    host = '';
+	  }
+
+	  if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
+	  if (search && search.charAt(0) !== '?') search = '?' + search;
+
+	  pathname = pathname.replace(/[?#]/g, function(match) {
+	    return encodeURIComponent(match);
+	  });
+	  search = search.replace('#', '%23');
+
+	  return protocol + host + pathname + search + hash;
+	};
+
+	function urlResolve(source, relative) {
+	  return urlParse(source, false, true).resolve(relative);
+	}
+
+	Url.prototype.resolve = function(relative) {
+	  return this.resolveObject(urlParse(relative, false, true)).format();
+	};
+
+	function urlResolveObject(source, relative) {
+	  if (!source) return relative;
+	  return urlParse(source, false, true).resolveObject(relative);
+	}
+
+	Url.prototype.resolveObject = function(relative) {
+	  if (util.isString(relative)) {
+	    var rel = new Url();
+	    rel.parse(relative, false, true);
+	    relative = rel;
+	  }
+
+	  var result = new Url();
+	  var tkeys = Object.keys(this);
+	  for (var tk = 0; tk < tkeys.length; tk++) {
+	    var tkey = tkeys[tk];
+	    result[tkey] = this[tkey];
+	  }
+
+	  // hash is always overridden, no matter what.
+	  // even href="" will remove it.
+	  result.hash = relative.hash;
+
+	  // if the relative url is empty, then there's nothing left to do here.
+	  if (relative.href === '') {
+	    result.href = result.format();
+	    return result;
+	  }
+
+	  // hrefs like //foo/bar always cut to the protocol.
+	  if (relative.slashes && !relative.protocol) {
+	    // take everything except the protocol from relative
+	    var rkeys = Object.keys(relative);
+	    for (var rk = 0; rk < rkeys.length; rk++) {
+	      var rkey = rkeys[rk];
+	      if (rkey !== 'protocol')
+	        result[rkey] = relative[rkey];
+	    }
+
+	    //urlParse appends trailing / to urls like http://www.example.com
+	    if (slashedProtocol[result.protocol] &&
+	        result.hostname && !result.pathname) {
+	      result.path = result.pathname = '/';
+	    }
+
+	    result.href = result.format();
+	    return result;
+	  }
+
+	  if (relative.protocol && relative.protocol !== result.protocol) {
+	    // if it's a known url protocol, then changing
+	    // the protocol does weird things
+	    // first, if it's not file:, then we MUST have a host,
+	    // and if there was a path
+	    // to begin with, then we MUST have a path.
+	    // if it is file:, then the host is dropped,
+	    // because that's known to be hostless.
+	    // anything else is assumed to be absolute.
+	    if (!slashedProtocol[relative.protocol]) {
+	      var keys = Object.keys(relative);
+	      for (var v = 0; v < keys.length; v++) {
+	        var k = keys[v];
+	        result[k] = relative[k];
+	      }
+	      result.href = result.format();
+	      return result;
+	    }
+
+	    result.protocol = relative.protocol;
+	    if (!relative.host && !hostlessProtocol[relative.protocol]) {
+	      var relPath = (relative.pathname || '').split('/');
+	      while (relPath.length && !(relative.host = relPath.shift()));
+	      if (!relative.host) relative.host = '';
+	      if (!relative.hostname) relative.hostname = '';
+	      if (relPath[0] !== '') relPath.unshift('');
+	      if (relPath.length < 2) relPath.unshift('');
+	      result.pathname = relPath.join('/');
+	    } else {
+	      result.pathname = relative.pathname;
+	    }
+	    result.search = relative.search;
+	    result.query = relative.query;
+	    result.host = relative.host || '';
+	    result.auth = relative.auth;
+	    result.hostname = relative.hostname || relative.host;
+	    result.port = relative.port;
+	    // to support http.request
+	    if (result.pathname || result.search) {
+	      var p = result.pathname || '';
+	      var s = result.search || '';
+	      result.path = p + s;
+	    }
+	    result.slashes = result.slashes || relative.slashes;
+	    result.href = result.format();
+	    return result;
+	  }
+
+	  var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
+	      isRelAbs = (
+	          relative.host ||
+	          relative.pathname && relative.pathname.charAt(0) === '/'
+	      ),
+	      mustEndAbs = (isRelAbs || isSourceAbs ||
+	                    (result.host && relative.pathname)),
+	      removeAllDots = mustEndAbs,
+	      srcPath = result.pathname && result.pathname.split('/') || [],
+	      relPath = relative.pathname && relative.pathname.split('/') || [],
+	      psychotic = result.protocol && !slashedProtocol[result.protocol];
+
+	  // if the url is a non-slashed url, then relative
+	  // links like ../.. should be able
+	  // to crawl up to the hostname, as well.  This is strange.
+	  // result.protocol has already been set by now.
+	  // Later on, put the first path part into the host field.
+	  if (psychotic) {
+	    result.hostname = '';
+	    result.port = null;
+	    if (result.host) {
+	      if (srcPath[0] === '') srcPath[0] = result.host;
+	      else srcPath.unshift(result.host);
+	    }
+	    result.host = '';
+	    if (relative.protocol) {
+	      relative.hostname = null;
+	      relative.port = null;
+	      if (relative.host) {
+	        if (relPath[0] === '') relPath[0] = relative.host;
+	        else relPath.unshift(relative.host);
+	      }
+	      relative.host = null;
+	    }
+	    mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
+	  }
+
+	  if (isRelAbs) {
+	    // it's absolute.
+	    result.host = (relative.host || relative.host === '') ?
+	                  relative.host : result.host;
+	    result.hostname = (relative.hostname || relative.hostname === '') ?
+	                      relative.hostname : result.hostname;
+	    result.search = relative.search;
+	    result.query = relative.query;
+	    srcPath = relPath;
+	    // fall through to the dot-handling below.
+	  } else if (relPath.length) {
+	    // it's relative
+	    // throw away the existing file, and take the new path instead.
+	    if (!srcPath) srcPath = [];
+	    srcPath.pop();
+	    srcPath = srcPath.concat(relPath);
+	    result.search = relative.search;
+	    result.query = relative.query;
+	  } else if (!util.isNullOrUndefined(relative.search)) {
+	    // just pull out the search.
+	    // like href='?foo'.
+	    // Put this after the other two cases because it simplifies the booleans
+	    if (psychotic) {
+	      result.hostname = result.host = srcPath.shift();
+	      //occationaly the auth can get stuck only in host
+	      //this especially happens in cases like
+	      //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
+	      var authInHost = result.host && result.host.indexOf('@') > 0 ?
+	                       result.host.split('@') : false;
+	      if (authInHost) {
+	        result.auth = authInHost.shift();
+	        result.host = result.hostname = authInHost.shift();
+	      }
+	    }
+	    result.search = relative.search;
+	    result.query = relative.query;
+	    //to support http.request
+	    if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
+	      result.path = (result.pathname ? result.pathname : '') +
+	                    (result.search ? result.search : '');
+	    }
+	    result.href = result.format();
+	    return result;
+	  }
+
+	  if (!srcPath.length) {
+	    // no path at all.  easy.
+	    // we've already handled the other stuff above.
+	    result.pathname = null;
+	    //to support http.request
+	    if (result.search) {
+	      result.path = '/' + result.search;
+	    } else {
+	      result.path = null;
+	    }
+	    result.href = result.format();
+	    return result;
+	  }
+
+	  // if a url ENDs in . or .., then it must get a trailing slash.
+	  // however, if it ends in anything else non-slashy,
+	  // then it must NOT get a trailing slash.
+	  var last = srcPath.slice(-1)[0];
+	  var hasTrailingSlash = (
+	      (result.host || relative.host || srcPath.length > 1) &&
+	      (last === '.' || last === '..') || last === '');
+
+	  // strip single dots, resolve double dots to parent dir
+	  // if the path tries to go above the root, `up` ends up > 0
+	  var up = 0;
+	  for (var i = srcPath.length; i >= 0; i--) {
+	    last = srcPath[i];
+	    if (last === '.') {
+	      srcPath.splice(i, 1);
+	    } else if (last === '..') {
+	      srcPath.splice(i, 1);
+	      up++;
+	    } else if (up) {
+	      srcPath.splice(i, 1);
+	      up--;
+	    }
+	  }
+
+	  // if the path is allowed to go above the root, restore leading ..s
+	  if (!mustEndAbs && !removeAllDots) {
+	    for (; up--; up) {
+	      srcPath.unshift('..');
+	    }
+	  }
+
+	  if (mustEndAbs && srcPath[0] !== '' &&
+	      (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
+	    srcPath.unshift('');
+	  }
+
+	  if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
+	    srcPath.push('');
+	  }
+
+	  var isAbsolute = srcPath[0] === '' ||
+	      (srcPath[0] && srcPath[0].charAt(0) === '/');
+
+	  // put the host back
+	  if (psychotic) {
+	    result.hostname = result.host = isAbsolute ? '' :
+	                                    srcPath.length ? srcPath.shift() : '';
+	    //occationaly the auth can get stuck only in host
+	    //this especially happens in cases like
+	    //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
+	    var authInHost = result.host && result.host.indexOf('@') > 0 ?
+	                     result.host.split('@') : false;
+	    if (authInHost) {
+	      result.auth = authInHost.shift();
+	      result.host = result.hostname = authInHost.shift();
+	    }
+	  }
+
+	  mustEndAbs = mustEndAbs || (result.host && srcPath.length);
+
+	  if (mustEndAbs && !isAbsolute) {
+	    srcPath.unshift('');
+	  }
+
+	  if (!srcPath.length) {
+	    result.pathname = null;
+	    result.path = null;
+	  } else {
+	    result.pathname = srcPath.join('/');
+	  }
+
+	  //to support request.http
+	  if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
+	    result.path = (result.pathname ? result.pathname : '') +
+	                  (result.search ? result.search : '');
+	  }
+	  result.auth = relative.auth || result.auth;
+	  result.slashes = result.slashes || relative.slashes;
+	  result.href = result.format();
+	  return result;
+	};
+
+	Url.prototype.parseHost = function() {
+	  var host = this.host;
+	  var port = portPattern.exec(host);
+	  if (port) {
+	    port = port[0];
+	    if (port !== ':') {
+	      this.port = port.substr(1);
+	    }
+	    host = host.substr(0, host.length - port.length);
+	  }
+	  if (host) this.hostname = host;
+	};
+
+
+/***/ }),
+/* 3 */
+/***/ (function(module, exports, __webpack_require__) {
+
+	var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module, global) {/*! https://mths.be/punycode v1.3.2 by @mathias */
+	;(function(root) {
+
+		/** Detect free variables */
+		var freeExports = typeof exports == 'object' && exports &&
+			!exports.nodeType && exports;
+		var freeModule = typeof module == 'object' && module &&
+			!module.nodeType && module;
+		var freeGlobal = typeof global == 'object' && global;
+		if (
+			freeGlobal.global === freeGlobal ||
+			freeGlobal.window === freeGlobal ||
+			freeGlobal.self === freeGlobal
+		) {
+			root = freeGlobal;
+		}
+
+		/**
+		 * The `punycode` object.
+		 * @name punycode
+		 * @type Object
+		 */
+		var punycode,
+
+		/** Highest positive signed 32-bit float value */
+		maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
+
+		/** Bootstring parameters */
+		base = 36,
+		tMin = 1,
+		tMax = 26,
+		skew = 38,
+		damp = 700,
+		initialBias = 72,
+		initialN = 128, // 0x80
+		delimiter = '-', // '\x2D'
+
+		/** Regular expressions */
+		regexPunycode = /^xn--/,
+		regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
+		regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
+
+		/** Error messages */
+		errors = {
+			'overflow': 'Overflow: input needs wider integers to process',
+			'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
+			'invalid-input': 'Invalid input'
+		},
+
+		/** Convenience shortcuts */
+		baseMinusTMin = base - tMin,
+		floor = Math.floor,
+		stringFromCharCode = String.fromCharCode,
+
+		/** Temporary variable */
+		key;
+
+		/*--------------------------------------------------------------------------*/
+
+		/**
+		 * A generic error utility function.
+		 * @private
+		 * @param {String} type The error type.
+		 * @returns {Error} Throws a `RangeError` with the applicable error message.
+		 */
+		function error(type) {
+			throw RangeError(errors[type]);
+		}
+
+		/**
+		 * A generic `Array#map` utility function.
+		 * @private
+		 * @param {Array} array The array to iterate over.
+		 * @param {Function} callback The function that gets called for every array
+		 * item.
+		 * @returns {Array} A new array of values returned by the callback function.
+		 */
+		function map(array, fn) {
+			var length = array.length;
+			var result = [];
+			while (length--) {
+				result[length] = fn(array[length]);
+			}
+			return result;
+		}
+
+		/**
+		 * A simple `Array#map`-like wrapper to work with domain name strings or email
+		 * addresses.
+		 * @private
+		 * @param {String} domain The domain name or email address.
+		 * @param {Function} callback The function that gets called for every
+		 * character.
+		 * @returns {Array} A new string of characters returned by the callback
+		 * function.
+		 */
+		function mapDomain(string, fn) {
+			var parts = string.split('@');
+			var result = '';
+			if (parts.length > 1) {
+				// In email addresses, only the domain name should be punycoded. Leave
+				// the local part (i.e. everything up to `@`) intact.
+				result = parts[0] + '@';
+				string = parts[1];
+			}
+			// Avoid `split(regex)` for IE8 compatibility. See #17.
+			string = string.replace(regexSeparators, '\x2E');
+			var labels = string.split('.');
+			var encoded = map(labels, fn).join('.');
+			return result + encoded;
+		}
+
+		/**
+		 * Creates an array containing the numeric code points of each Unicode
+		 * character in the string. While JavaScript uses UCS-2 internally,
+		 * this function will convert a pair of surrogate halves (each of which
+		 * UCS-2 exposes as separate characters) into a single code point,
+		 * matching UTF-16.
+		 * @see `punycode.ucs2.encode`
+		 * @see <https://mathiasbynens.be/notes/javascript-encoding>
+		 * @memberOf punycode.ucs2
+		 * @name decode
+		 * @param {String} string The Unicode input string (UCS-2).
+		 * @returns {Array} The new array of code points.
+		 */
+		function ucs2decode(string) {
+			var output = [],
+			    counter = 0,
+			    length = string.length,
+			    value,
+			    extra;
+			while (counter < length) {
+				value = string.charCodeAt(counter++);
+				if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
+					// high surrogate, and there is a next character
+					extra = string.charCodeAt(counter++);
+					if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+						output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+					} else {
+						// unmatched surrogate; only append this code unit, in case the next
+						// code unit is the high surrogate of a surrogate pair
+						output.push(value);
+						counter--;
+					}
+				} else {
+					output.push(value);
+				}
+			}
+			return output;
+		}
+
+		/**
+		 * Creates a string based on an array of numeric code points.
+		 * @see `punycode.ucs2.decode`
+		 * @memberOf punycode.ucs2
+		 * @name encode
+		 * @param {Array} codePoints The array of numeric code points.
+		 * @returns {String} The new Unicode string (UCS-2).
+		 */
+		function ucs2encode(array) {
+			return map(array, function(value) {
+				var output = '';
+				if (value > 0xFFFF) {
+					value -= 0x10000;
+					output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+					value = 0xDC00 | value & 0x3FF;
+				}
+				output += stringFromCharCode(value);
+				return output;
+			}).join('');
+		}
+
+		/**
+		 * Converts a basic code point into a digit/integer.
+		 * @see `digitToBasic()`
+		 * @private
+		 * @param {Number} codePoint The basic numeric code point value.
+		 * @returns {Number} The numeric value of a basic code point (for use in
+		 * representing integers) in the range `0` to `base - 1`, or `base` if
+		 * the code point does not represent a value.
+		 */
+		function basicToDigit(codePoint) {
+			if (codePoint - 48 < 10) {
+				return codePoint - 22;
+			}
+			if (codePoint - 65 < 26) {
+				return codePoint - 65;
+			}
+			if (codePoint - 97 < 26) {
+				return codePoint - 97;
+			}
+			return base;
+		}
+
+		/**
+		 * Converts a digit/integer into a basic code point.
+		 * @see `basicToDigit()`
+		 * @private
+		 * @param {Number} digit The numeric value of a basic code point.
+		 * @returns {Number} The basic code point whose value (when used for
+		 * representing integers) is `digit`, which needs to be in the range
+		 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
+		 * used; else, the lowercase form is used. The behavior is undefined
+		 * if `flag` is non-zero and `digit` has no uppercase form.
+		 */
+		function digitToBasic(digit, flag) {
+			//  0..25 map to ASCII a..z or A..Z
+			// 26..35 map to ASCII 0..9
+			return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+		}
+
+		/**
+		 * Bias adaptation function as per section 3.4 of RFC 3492.
+		 * http://tools.ietf.org/html/rfc3492#section-3.4
+		 * @private
+		 */
+		function adapt(delta, numPoints, firstTime) {
+			var k = 0;
+			delta = firstTime ? floor(delta / damp) : delta >> 1;
+			delta += floor(delta / numPoints);
+			for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
+				delta = floor(delta / baseMinusTMin);
+			}
+			return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
+		}
+
+		/**
+		 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
+		 * symbols.
+		 * @memberOf punycode
+		 * @param {String} input The Punycode string of ASCII-only symbols.
+		 * @returns {String} The resulting string of Unicode symbols.
+		 */
+		function decode(input) {
+			// Don't use UCS-2
+			var output = [],
+			    inputLength = input.length,
+			    out,
+			    i = 0,
+			    n = initialN,
+			    bias = initialBias,
+			    basic,
+			    j,
+			    index,
+			    oldi,
+			    w,
+			    k,
+			    digit,
+			    t,
+			    /** Cached calculation results */
+			    baseMinusT;
+
+			// Handle the basic code points: let `basic` be the number of input code
+			// points before the last delimiter, or `0` if there is none, then copy
+			// the first basic code points to the output.
+
+			basic = input.lastIndexOf(delimiter);
+			if (basic < 0) {
+				basic = 0;
+			}
+
+			for (j = 0; j < basic; ++j) {
+				// if it's not a basic code point
+				if (input.charCodeAt(j) >= 0x80) {
+					error('not-basic');
+				}
+				output.push(input.charCodeAt(j));
+			}
+
+			// Main decoding loop: start just after the last delimiter if any basic code
+			// points were copied; start at the beginning otherwise.
+
+			for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
+
+				// `index` is the index of the next character to be consumed.
+				// Decode a generalized variable-length integer into `delta`,
+				// which gets added to `i`. The overflow checking is easier
+				// if we increase `i` as we go, then subtract off its starting
+				// value at the end to obtain `delta`.
+				for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
+
+					if (index >= inputLength) {
+						error('invalid-input');
+					}
+
+					digit = basicToDigit(input.charCodeAt(index++));
+
+					if (digit >= base || digit > floor((maxInt - i) / w)) {
+						error('overflow');
+					}
+
+					i += digit * w;
+					t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+
+					if (digit < t) {
+						break;
+					}
+
+					baseMinusT = base - t;
+					if (w > floor(maxInt / baseMinusT)) {
+						error('overflow');
+					}
+
+					w *= baseMinusT;
+
+				}
+
+				out = output.length + 1;
+				bias = adapt(i - oldi, out, oldi == 0);
+
+				// `i` was supposed to wrap around from `out` to `0`,
+				// incrementing `n` each time, so we'll fix that now:
+				if (floor(i / out) > maxInt - n) {
+					error('overflow');
+				}
+
+				n += floor(i / out);
+				i %= out;
+
+				// Insert `n` at position `i` of the output
+				output.splice(i++, 0, n);
+
+			}
+
+			return ucs2encode(output);
+		}
+
+		/**
+		 * Converts a string of Unicode symbols (e.g. a domain name label) to a
+		 * Punycode string of ASCII-only symbols.
+		 * @memberOf punycode
+		 * @param {String} input The string of Unicode symbols.
+		 * @returns {String} The resulting Punycode string of ASCII-only symbols.
+		 */
+		function encode(input) {
+			var n,
+			    delta,
+			    handledCPCount,
+			    basicLength,
+			    bias,
+			    j,
+			    m,
+			    q,
+			    k,
+			    t,
+			    currentValue,
+			    output = [],
+			    /** `inputLength` will hold the number of code points in `input`. */
+			    inputLength,
+			    /** Cached calculation results */
+			    handledCPCountPlusOne,
+			    baseMinusT,
+			    qMinusT;
+
+			// Convert the input in UCS-2 to Unicode
+			input = ucs2decode(input);
+
+			// Cache the length
+			inputLength = input.length;
+
+			// Initialize the state
+			n = initialN;
+			delta = 0;
+			bias = initialBias;
+
+			// Handle the basic code points
+			for (j = 0; j < inputLength; ++j) {
+				currentValue = input[j];
+				if (currentValue < 0x80) {
+					output.push(stringFromCharCode(currentValue));
+				}
+			}
+
+			handledCPCount = basicLength = output.length;
+
+			// `handledCPCount` is the number of code points that have been handled;
+			// `basicLength` is the number of basic code points.
+
+			// Finish the basic string - if it is not empty - with a delimiter
+			if (basicLength) {
+				output.push(delimiter);
+			}
+
+			// Main encoding loop:
+			while (handledCPCount < inputLength) {
+
+				// All non-basic code points < n have been handled already. Find the next
+				// larger one:
+				for (m = maxInt, j = 0; j < inputLength; ++j) {
+					currentValue = input[j];
+					if (currentValue >= n && currentValue < m) {
+						m = currentValue;
+					}
+				}
+
+				// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
+				// but guard against overflow
+				handledCPCountPlusOne = handledCPCount + 1;
+				if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
+					error('overflow');
+				}
+
+				delta += (m - n) * handledCPCountPlusOne;
+				n = m;
+
+				for (j = 0; j < inputLength; ++j) {
+					currentValue = input[j];
+
+					if (currentValue < n && ++delta > maxInt) {
+						error('overflow');
+					}
+
+					if (currentValue == n) {
+						// Represent delta as a generalized variable-length integer
+						for (q = delta, k = base; /* no condition */; k += base) {
+							t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+							if (q < t) {
+								break;
+							}
+							qMinusT = q - t;
+							baseMinusT = base - t;
+							output.push(
+								stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
+							);
+							q = floor(qMinusT / baseMinusT);
+						}
+
+						output.push(stringFromCharCode(digitToBasic(q, 0)));
+						bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
+						delta = 0;
+						++handledCPCount;
+					}
+				}
+
+				++delta;
+				++n;
+
+			}
+			return output.join('');
+		}
+
+		/**
+		 * Converts a Punycode string representing a domain name or an email address
+		 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
+		 * it doesn't matter if you call it on a string that has already been
+		 * converted to Unicode.
+		 * @memberOf punycode
+		 * @param {String} input The Punycoded domain name or email address to
+		 * convert to Unicode.
+		 * @returns {String} The Unicode representation of the given Punycode
+		 * string.
+		 */
+		function toUnicode(input) {
+			return mapDomain(input, function(string) {
+				return regexPunycode.test(string)
+					? decode(string.slice(4).toLowerCase())
+					: string;
+			});
+		}
+
+		/**
+		 * Converts a Unicode string representing a domain name or an email address to
+		 * Punycode. Only the non-ASCII parts of the domain name will be converted,
+		 * i.e. it doesn't matter if you call it with a domain that's already in
+		 * ASCII.
+		 * @memberOf punycode
+		 * @param {String} input The domain name or email address to convert, as a
+		 * Unicode string.
+		 * @returns {String} The Punycode representation of the given domain name or
+		 * email address.
+		 */
+		function toASCII(input) {
+			return mapDomain(input, function(string) {
+				return regexNonASCII.test(string)
+					? 'xn--' + encode(string)
+					: string;
+			});
+		}
+
+		/*--------------------------------------------------------------------------*/
+
+		/** Define the public API */
+		punycode = {
+			/**
+			 * A string representing the current Punycode.js version number.
+			 * @memberOf punycode
+			 * @type String
+			 */
+			'version': '1.3.2',
+			/**
+			 * An object of methods to convert from JavaScript's internal character
+			 * representation (UCS-2) to Unicode code points, and back.
+			 * @see <https://mathiasbynens.be/notes/javascript-encoding>
+			 * @memberOf punycode
+			 * @type Object
+			 */
+			'ucs2': {
+				'decode': ucs2decode,
+				'encode': ucs2encode
+			},
+			'decode': decode,
+			'encode': encode,
+			'toASCII': toASCII,
+			'toUnicode': toUnicode
+		};
+
+		/** Expose `punycode` */
+		// Some AMD build optimizers, like r.js, check for specific condition patterns
+		// like the following:
+		if (
+			true
+		) {
+			!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
+				return punycode;
+			}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+		} else if (freeExports && freeModule) {
+			if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
+				freeModule.exports = punycode;
+			} else { // in Narwhal or RingoJS v0.7.0-
+				for (key in punycode) {
+					punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
+				}
+			}
+		} else { // in Rhino or a web browser
+			root.punycode = punycode;
+		}
+
+	}(this));
+
+	/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)(module), (function() { return this; }())))
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports) {
+
+	module.exports = function(module) {

+		if(!module.webpackPolyfill) {

+			module.deprecate = function() {};

+			module.paths = [];

+			// module.parent = undefined by default

+			module.children = [];

+			module.webpackPolyfill = 1;

+		}

+		return module;

+	}

+
+
+/***/ }),
+/* 5 */
+/***/ (function(module, exports) {
+
+	'use strict';
+
+	module.exports = {
+	  isString: function(arg) {
+	    return typeof(arg) === 'string';
+	  },
+	  isObject: function(arg) {
+	    return typeof(arg) === 'object' && arg !== null;
+	  },
+	  isNull: function(arg) {
+	    return arg === null;
+	  },
+	  isNullOrUndefined: function(arg) {
+	    return arg == null;
+	  }
+	};
+
+
+/***/ }),
+/* 6 */
+/***/ (function(module, exports, __webpack_require__) {
+
+	'use strict';
+
+	exports.decode = exports.parse = __webpack_require__(7);
+	exports.encode = exports.stringify = __webpack_require__(8);
+
+
+/***/ }),
+/* 7 */
+/***/ (function(module, exports) {
+
+	// Copyright Joyent, Inc. and other Node contributors.
+	//
+	// Permission is hereby granted, free of charge, to any person obtaining a
+	// copy of this software and associated documentation files (the
+	// "Software"), to deal in the Software without restriction, including
+	// without limitation the rights to use, copy, modify, merge, publish,
+	// distribute, sublicense, and/or sell copies of the Software, and to permit
+	// persons to whom the Software is furnished to do so, subject to the
+	// following conditions:
+	//
+	// The above copyright notice and this permission notice shall be included
+	// in all copies or substantial portions of the Software.
+	//
+	// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+	// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+	// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+	// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+	// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+	// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+	// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+	'use strict';
+
+	// If obj.hasOwnProperty has been overridden, then calling
+	// obj.hasOwnProperty(prop) will break.
+	// See: https://github.com/joyent/node/issues/1707
+	function hasOwnProperty(obj, prop) {
+	  return Object.prototype.hasOwnProperty.call(obj, prop);
+	}
+
+	module.exports = function(qs, sep, eq, options) {
+	  sep = sep || '&';
+	  eq = eq || '=';
+	  var obj = {};
+
+	  if (typeof qs !== 'string' || qs.length === 0) {
+	    return obj;
+	  }
+
+	  var regexp = /\+/g;
+	  qs = qs.split(sep);
+
+	  var maxKeys = 1000;
+	  if (options && typeof options.maxKeys === 'number') {
+	    maxKeys = options.maxKeys;
+	  }
+
+	  var len = qs.length;
+	  // maxKeys <= 0 means that we should not limit keys count
+	  if (maxKeys > 0 && len > maxKeys) {
+	    len = maxKeys;
+	  }
+
+	  for (var i = 0; i < len; ++i) {
+	    var x = qs[i].replace(regexp, '%20'),
+	        idx = x.indexOf(eq),
+	        kstr, vstr, k, v;
+
+	    if (idx >= 0) {
+	      kstr = x.substr(0, idx);
+	      vstr = x.substr(idx + 1);
+	    } else {
+	      kstr = x;
+	      vstr = '';
+	    }
+
+	    k = decodeURIComponent(kstr);
+	    v = decodeURIComponent(vstr);
+
+	    if (!hasOwnProperty(obj, k)) {
+	      obj[k] = v;
+	    } else if (Array.isArray(obj[k])) {
+	      obj[k].push(v);
+	    } else {
+	      obj[k] = [obj[k], v];
+	    }
+	  }
+
+	  return obj;
+	};
+
+
+/***/ }),
+/* 8 */
+/***/ (function(module, exports) {
+
+	// Copyright Joyent, Inc. and other Node contributors.
+	//
+	// Permission is hereby granted, free of charge, to any person obtaining a
+	// copy of this software and associated documentation files (the
+	// "Software"), to deal in the Software without restriction, including
+	// without limitation the rights to use, copy, modify, merge, publish,
+	// distribute, sublicense, and/or sell copies of the Software, and to permit
+	// persons to whom the Software is furnished to do so, subject to the
+	// following conditions:
+	//
+	// The above copyright notice and this permission notice shall be included
+	// in all copies or substantial portions of the Software.
+	//
+	// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+	// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+	// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+	// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+	// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+	// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+	// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+	'use strict';
+
+	var stringifyPrimitive = function(v) {
+	  switch (typeof v) {
+	    case 'string':
+	      return v;
+
+	    case 'boolean':
+	      return v ? 'true' : 'false';
+
+	    case 'number':
+	      return isFinite(v) ? v : '';
+
+	    default:
+	      return '';
+	  }
+	};
+
+	module.exports = function(obj, sep, eq, name) {
+	  sep = sep || '&';
+	  eq = eq || '=';
+	  if (obj === null) {
+	    obj = undefined;
+	  }
+
+	  if (typeof obj === 'object') {
+	    return Object.keys(obj).map(function(k) {
+	      var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
+	      if (Array.isArray(obj[k])) {
+	        return obj[k].map(function(v) {
+	          return ks + encodeURIComponent(stringifyPrimitive(v));
+	        }).join(sep);
+	      } else {
+	        return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
+	      }
+	    }).join(sep);
+
+	  }
+
+	  if (!name) return '';
+	  return encodeURIComponent(stringifyPrimitive(name)) + eq +
+	         encodeURIComponent(stringifyPrimitive(obj));
+	};
+
+
+/***/ }),
+/* 9 */
+/***/ (function(module, exports, __webpack_require__) {
+
+	/* WEBPACK VAR INJECTION */(function(global, process) {// Copyright Joyent, Inc. and other Node contributors.
+	//
+	// Permission is hereby granted, free of charge, to any person obtaining a
+	// copy of this software and associated documentation files (the
+	// "Software"), to deal in the Software without restriction, including
+	// without limitation the rights to use, copy, modify, merge, publish,
+	// distribute, sublicense, and/or sell copies of the Software, and to permit
+	// persons to whom the Software is furnished to do so, subject to the
+	// following conditions:
+	//
+	// The above copyright notice and this permission notice shall be included
+	// in all copies or substantial portions of the Software.
+	//
+	// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+	// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+	// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+	// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+	// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+	// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+	// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+	var formatRegExp = /%[sdj%]/g;
+	exports.format = function(f) {
+	  if (!isString(f)) {
+	    var objects = [];
+	    for (var i = 0; i < arguments.length; i++) {
+	      objects.push(inspect(arguments[i]));
+	    }
+	    return objects.join(' ');
+	  }
+
+	  var i = 1;
+	  var args = arguments;
+	  var len = args.length;
+	  var str = String(f).replace(formatRegExp, function(x) {
+	    if (x === '%%') return '%';
+	    if (i >= len) return x;
+	    switch (x) {
+	      case '%s': return String(args[i++]);
+	      case '%d': return Number(args[i++]);
+	      case '%j':
+	        try {
+	          return JSON.stringify(args[i++]);
+	        } catch (_) {
+	          return '[Circular]';
+	        }
+	      default:
+	        return x;
+	    }
+	  });
+	  for (var x = args[i]; i < len; x = args[++i]) {
+	    if (isNull(x) || !isObject(x)) {
+	      str += ' ' + x;
+	    } else {
+	      str += ' ' + inspect(x);
+	    }
+	  }
+	  return str;
+	};
+
+
+	// Mark that a method should not be used.
+	// Returns a modified function which warns once by default.
+	// If --no-deprecation is set, then it is a no-op.
+	exports.deprecate = function(fn, msg) {
+	  // Allow for deprecating things in the process of starting up.
+	  if (isUndefined(global.process)) {
+	    return function() {
+	      return exports.deprecate(fn, msg).apply(this, arguments);
+	    };
+	  }
+
+	  if (process.noDeprecation === true) {
+	    return fn;
+	  }
+
+	  var warned = false;
+	  function deprecated() {
+	    if (!warned) {
+	      if (process.throwDeprecation) {
+	        throw new Error(msg);
+	      } else if (process.traceDeprecation) {
+	        console.trace(msg);
+	      } else {
+	        console.error(msg);
+	      }
+	      warned = true;
+	    }
+	    return fn.apply(this, arguments);
+	  }
+
+	  return deprecated;
+	};
+
+
+	var debugs = {};
+	var debugEnviron;
+	exports.debuglog = function(set) {
+	  if (isUndefined(debugEnviron))
+	    debugEnviron = process.env.NODE_DEBUG || '';
+	  set = set.toUpperCase();
+	  if (!debugs[set]) {
+	    if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
+	      var pid = process.pid;
+	      debugs[set] = function() {
+	        var msg = exports.format.apply(exports, arguments);
+	        console.error('%s %d: %s', set, pid, msg);
+	      };
+	    } else {
+	      debugs[set] = function() {};
+	    }
+	  }
+	  return debugs[set];
+	};
+
+
+	/**
+	 * Echos the value of a value. Trys to print the value out
+	 * in the best way possible given the different types.
+	 *
+	 * @param {Object} obj The object to print out.
+	 * @param {Object} opts Optional options object that alters the output.
+	 */
+	/* legacy: obj, showHidden, depth, colors*/
+	function inspect(obj, opts) {
+	  // default options
+	  var ctx = {
+	    seen: [],
+	    stylize: stylizeNoColor
+	  };
+	  // legacy...
+	  if (arguments.length >= 3) ctx.depth = arguments[2];
+	  if (arguments.length >= 4) ctx.colors = arguments[3];
+	  if (isBoolean(opts)) {
+	    // legacy...
+	    ctx.showHidden = opts;
+	  } else if (opts) {
+	    // got an "options" object
+	    exports._extend(ctx, opts);
+	  }
+	  // set default options
+	  if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
+	  if (isUndefined(ctx.depth)) ctx.depth = 2;
+	  if (isUndefined(ctx.colors)) ctx.colors = false;
+	  if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
+	  if (ctx.colors) ctx.stylize = stylizeWithColor;
+	  return formatValue(ctx, obj, ctx.depth);
+	}
+	exports.inspect = inspect;
+
+
+	// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
+	inspect.colors = {
+	  'bold' : [1, 22],
+	  'italic' : [3, 23],
+	  'underline' : [4, 24],
+	  'inverse' : [7, 27],
+	  'white' : [37, 39],
+	  'grey' : [90, 39],
+	  'black' : [30, 39],
+	  'blue' : [34, 39],
+	  'cyan' : [36, 39],
+	  'green' : [32, 39],
+	  'magenta' : [35, 39],
+	  'red' : [31, 39],
+	  'yellow' : [33, 39]
+	};
+
+	// Don't use 'blue' not visible on cmd.exe
+	inspect.styles = {
+	  'special': 'cyan',
+	  'number': 'yellow',
+	  'boolean': 'yellow',
+	  'undefined': 'grey',
+	  'null': 'bold',
+	  'string': 'green',
+	  'date': 'magenta',
+	  // "name": intentionally not styling
+	  'regexp': 'red'
+	};
+
+
+	function stylizeWithColor(str, styleType) {
+	  var style = inspect.styles[styleType];
+
+	  if (style) {
+	    return '\u001b[' + inspect.colors[style][0] + 'm' + str +
+	           '\u001b[' + inspect.colors[style][1] + 'm';
+	  } else {
+	    return str;
+	  }
+	}
+
+
+	function stylizeNoColor(str, styleType) {
+	  return str;
+	}
+
+
+	function arrayToHash(array) {
+	  var hash = {};
+
+	  array.forEach(function(val, idx) {
+	    hash[val] = true;
+	  });
+
+	  return hash;
+	}
+
+
+	function formatValue(ctx, value, recurseTimes) {
+	  // Provide a hook for user-specified inspect functions.
+	  // Check that value is an object with an inspect function on it
+	  if (ctx.customInspect &&
+	      value &&
+	      isFunction(value.inspect) &&
+	      // Filter out the util module, it's inspect function is special
+	      value.inspect !== exports.inspect &&
+	      // Also filter out any prototype objects using the circular check.
+	      !(value.constructor && value.constructor.prototype === value)) {
+	    var ret = value.inspect(recurseTimes, ctx);
+	    if (!isString(ret)) {
+	      ret = formatValue(ctx, ret, recurseTimes);
+	    }
+	    return ret;
+	  }
+
+	  // Primitive types cannot have properties
+	  var primitive = formatPrimitive(ctx, value);
+	  if (primitive) {
+	    return primitive;
+	  }
+
+	  // Look up the keys of the object.
+	  var keys = Object.keys(value);
+	  var visibleKeys = arrayToHash(keys);
+
+	  if (ctx.showHidden) {
+	    keys = Object.getOwnPropertyNames(value);
+	  }
+
+	  // IE doesn't make error fields non-enumerable
+	  // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
+	  if (isError(value)
+	      && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
+	    return formatError(value);
+	  }
+
+	  // Some type of object without properties can be shortcutted.
+	  if (keys.length === 0) {
+	    if (isFunction(value)) {
+	      var name = value.name ? ': ' + value.name : '';
+	      return ctx.stylize('[Function' + name + ']', 'special');
+	    }
+	    if (isRegExp(value)) {
+	      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
+	    }
+	    if (isDate(value)) {
+	      return ctx.stylize(Date.prototype.toString.call(value), 'date');
+	    }
+	    if (isError(value)) {
+	      return formatError(value);
+	    }
+	  }
+
+	  var base = '', array = false, braces = ['{', '}'];
+
+	  // Make Array say that they are Array
+	  if (isArray(value)) {
+	    array = true;
+	    braces = ['[', ']'];
+	  }
+
+	  // Make functions say that they are functions
+	  if (isFunction(value)) {
+	    var n = value.name ? ': ' + value.name : '';
+	    base = ' [Function' + n + ']';
+	  }
+
+	  // Make RegExps say that they are RegExps
+	  if (isRegExp(value)) {
+	    base = ' ' + RegExp.prototype.toString.call(value);
+	  }
+
+	  // Make dates with properties first say the date
+	  if (isDate(value)) {
+	    base = ' ' + Date.prototype.toUTCString.call(value);
+	  }
+
+	  // Make error with message first say the error
+	  if (isError(value)) {
+	    base = ' ' + formatError(value);
+	  }
+
+	  if (keys.length === 0 && (!array || value.length == 0)) {
+	    return braces[0] + base + braces[1];
+	  }
+
+	  if (recurseTimes < 0) {
+	    if (isRegExp(value)) {
+	      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
+	    } else {
+	      return ctx.stylize('[Object]', 'special');
+	    }
+	  }
+
+	  ctx.seen.push(value);
+
+	  var output;
+	  if (array) {
+	    output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
+	  } else {
+	    output = keys.map(function(key) {
+	      return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
+	    });
+	  }
+
+	  ctx.seen.pop();
+
+	  return reduceToSingleString(output, base, braces);
+	}
+
+
+	function formatPrimitive(ctx, value) {
+	  if (isUndefined(value))
+	    return ctx.stylize('undefined', 'undefined');
+	  if (isString(value)) {
+	    var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
+	                                             .replace(/'/g, "\\'")
+	                                             .replace(/\\"/g, '"') + '\'';
+	    return ctx.stylize(simple, 'string');
+	  }
+	  if (isNumber(value))
+	    return ctx.stylize('' + value, 'number');
+	  if (isBoolean(value))
+	    return ctx.stylize('' + value, 'boolean');
+	  // For some reason typeof null is "object", so special case here.
+	  if (isNull(value))
+	    return ctx.stylize('null', 'null');
+	}
+
+
+	function formatError(value) {
+	  return '[' + Error.prototype.toString.call(value) + ']';
+	}
+
+
+	function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
+	  var output = [];
+	  for (var i = 0, l = value.length; i < l; ++i) {
+	    if (hasOwnProperty(value, String(i))) {
+	      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
+	          String(i), true));
+	    } else {
+	      output.push('');
+	    }
+	  }
+	  keys.forEach(function(key) {
+	    if (!key.match(/^\d+$/)) {
+	      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
+	          key, true));
+	    }
+	  });
+	  return output;
+	}
+
+
+	function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
+	  var name, str, desc;
+	  desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
+	  if (desc.get) {
+	    if (desc.set) {
+	      str = ctx.stylize('[Getter/Setter]', 'special');
+	    } else {
+	      str = ctx.stylize('[Getter]', 'special');
+	    }
+	  } else {
+	    if (desc.set) {
+	      str = ctx.stylize('[Setter]', 'special');
+	    }
+	  }
+	  if (!hasOwnProperty(visibleKeys, key)) {
+	    name = '[' + key + ']';
+	  }
+	  if (!str) {
+	    if (ctx.seen.indexOf(desc.value) < 0) {
+	      if (isNull(recurseTimes)) {
+	        str = formatValue(ctx, desc.value, null);
+	      } else {
+	        str = formatValue(ctx, desc.value, recurseTimes - 1);
+	      }
+	      if (str.indexOf('\n') > -1) {
+	        if (array) {
+	          str = str.split('\n').map(function(line) {
+	            return '  ' + line;
+	          }).join('\n').substr(2);
+	        } else {
+	          str = '\n' + str.split('\n').map(function(line) {
+	            return '   ' + line;
+	          }).join('\n');
+	        }
+	      }
+	    } else {
+	      str = ctx.stylize('[Circular]', 'special');
+	    }
+	  }
+	  if (isUndefined(name)) {
+	    if (array && key.match(/^\d+$/)) {
+	      return str;
+	    }
+	    name = JSON.stringify('' + key);
+	    if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
+	      name = name.substr(1, name.length - 2);
+	      name = ctx.stylize(name, 'name');
+	    } else {
+	      name = name.replace(/'/g, "\\'")
+	                 .replace(/\\"/g, '"')
+	                 .replace(/(^"|"$)/g, "'");
+	      name = ctx.stylize(name, 'string');
+	    }
+	  }
+
+	  return name + ': ' + str;
+	}
+
+
+	function reduceToSingleString(output, base, braces) {
+	  var numLinesEst = 0;
+	  var length = output.reduce(function(prev, cur) {
+	    numLinesEst++;
+	    if (cur.indexOf('\n') >= 0) numLinesEst++;
+	    return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
+	  }, 0);
+
+	  if (length > 60) {
+	    return braces[0] +
+	           (base === '' ? '' : base + '\n ') +
+	           ' ' +
+	           output.join(',\n  ') +
+	           ' ' +
+	           braces[1];
+	  }
+
+	  return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
+	}
+
+
+	// NOTE: These type checking functions intentionally don't use `instanceof`
+	// because it is fragile and can be easily faked with `Object.create()`.
+	function isArray(ar) {
+	  return Array.isArray(ar);
+	}
+	exports.isArray = isArray;
+
+	function isBoolean(arg) {
+	  return typeof arg === 'boolean';
+	}
+	exports.isBoolean = isBoolean;
+
+	function isNull(arg) {
+	  return arg === null;
+	}
+	exports.isNull = isNull;
+
+	function isNullOrUndefined(arg) {
+	  return arg == null;
+	}
+	exports.isNullOrUndefined = isNullOrUndefined;
+
+	function isNumber(arg) {
+	  return typeof arg === 'number';
+	}
+	exports.isNumber = isNumber;
+
+	function isString(arg) {
+	  return typeof arg === 'string';
+	}
+	exports.isString = isString;
+
+	function isSymbol(arg) {
+	  return typeof arg === 'symbol';
+	}
+	exports.isSymbol = isSymbol;
+
+	function isUndefined(arg) {
+	  return arg === void 0;
+	}
+	exports.isUndefined = isUndefined;
+
+	function isRegExp(re) {
+	  return isObject(re) && objectToString(re) === '[object RegExp]';
+	}
+	exports.isRegExp = isRegExp;
+
+	function isObject(arg) {
+	  return typeof arg === 'object' && arg !== null;
+	}
+	exports.isObject = isObject;
+
+	function isDate(d) {
+	  return isObject(d) && objectToString(d) === '[object Date]';
+	}
+	exports.isDate = isDate;
+
+	function isError(e) {
+	  return isObject(e) &&
+	      (objectToString(e) === '[object Error]' || e instanceof Error);
+	}
+	exports.isError = isError;
+
+	function isFunction(arg) {
+	  return typeof arg === 'function';
+	}
+	exports.isFunction = isFunction;
+
+	function isPrimitive(arg) {
+	  return arg === null ||
+	         typeof arg === 'boolean' ||
+	         typeof arg === 'number' ||
+	         typeof arg === 'string' ||
+	         typeof arg === 'symbol' ||  // ES6 symbol
+	         typeof arg === 'undefined';
+	}
+	exports.isPrimitive = isPrimitive;
+
+	exports.isBuffer = __webpack_require__(11);
+
+	function objectToString(o) {
+	  return Object.prototype.toString.call(o);
+	}
+
+
+	function pad(n) {
+	  return n < 10 ? '0' + n.toString(10) : n.toString(10);
+	}
+
+
+	var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
+	              'Oct', 'Nov', 'Dec'];
+
+	// 26 Feb 16:19:34
+	function timestamp() {
+	  var d = new Date();
+	  var time = [pad(d.getHours()),
+	              pad(d.getMinutes()),
+	              pad(d.getSeconds())].join(':');
+	  return [d.getDate(), months[d.getMonth()], time].join(' ');
+	}
+
+
+	// log is just a thin wrapper to console.log that prepends a timestamp
+	exports.log = function() {
+	  console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
+	};
+
+
+	/**
+	 * Inherit the prototype methods from one constructor into another.
+	 *
+	 * The Function.prototype.inherits from lang.js rewritten as a standalone
+	 * function (not on Function.prototype). NOTE: If this file is to be loaded
+	 * during bootstrapping this function needs to be rewritten using some native
+	 * functions as prototype setup using normal JavaScript does not work as
+	 * expected during bootstrapping (see mirror.js in r114903).
+	 *
+	 * @param {function} ctor Constructor function which needs to inherit the
+	 *     prototype.
+	 * @param {function} superCtor Constructor function to inherit prototype from.
+	 */
+	exports.inherits = __webpack_require__(12);
+
+	exports._extend = function(origin, add) {
+	  // Don't do anything if add isn't an object
+	  if (!add || !isObject(add)) return origin;
+
+	  var keys = Object.keys(add);
+	  var i = keys.length;
+	  while (i--) {
+	    origin[keys[i]] = add[keys[i]];
+	  }
+	  return origin;
+	};
+
+	function hasOwnProperty(obj, prop) {
+	  return Object.prototype.hasOwnProperty.call(obj, prop);
+	}
+
+	/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(10)))
+
+/***/ }),
+/* 10 */
+/***/ (function(module, exports) {
+
+	// shim for using process in browser
+	var process = module.exports = {};
+
+	// cached from whatever global is present so that test runners that stub it
+	// don't break things.  But we need to wrap it in a try catch in case it is
+	// wrapped in strict mode code which doesn't define any globals.  It's inside a
+	// function because try/catches deoptimize in certain engines.
+
+	var cachedSetTimeout;
+	var cachedClearTimeout;
+
+	function defaultSetTimout() {
+	    throw new Error('setTimeout has not been defined');
+	}
+	function defaultClearTimeout () {
+	    throw new Error('clearTimeout has not been defined');
+	}
+	(function () {
+	    try {
+	        if (typeof setTimeout === 'function') {
+	            cachedSetTimeout = setTimeout;
+	        } else {
+	            cachedSetTimeout = defaultSetTimout;
+	        }
+	    } catch (e) {
+	        cachedSetTimeout = defaultSetTimout;
+	    }
+	    try {
+	        if (typeof clearTimeout === 'function') {
+	            cachedClearTimeout = clearTimeout;
+	        } else {
+	            cachedClearTimeout = defaultClearTimeout;
+	        }
+	    } catch (e) {
+	        cachedClearTimeout = defaultClearTimeout;
+	    }
+	} ())
+	function runTimeout(fun) {
+	    if (cachedSetTimeout === setTimeout) {
+	        //normal enviroments in sane situations
+	        return setTimeout(fun, 0);
+	    }
+	    // if setTimeout wasn't available but was latter defined
+	    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
+	        cachedSetTimeout = setTimeout;
+	        return setTimeout(fun, 0);
+	    }
+	    try {
+	        // when when somebody has screwed with setTimeout but no I.E. maddness
+	        return cachedSetTimeout(fun, 0);
+	    } catch(e){
+	        try {
+	            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+	            return cachedSetTimeout.call(null, fun, 0);
+	        } catch(e){
+	            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
+	            return cachedSetTimeout.call(this, fun, 0);
+	        }
+	    }
+
+
+	}
+	function runClearTimeout(marker) {
+	    if (cachedClearTimeout === clearTimeout) {
+	        //normal enviroments in sane situations
+	        return clearTimeout(marker);
+	    }
+	    // if clearTimeout wasn't available but was latter defined
+	    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
+	        cachedClearTimeout = clearTimeout;
+	        return clearTimeout(marker);
+	    }
+	    try {
+	        // when when somebody has screwed with setTimeout but no I.E. maddness
+	        return cachedClearTimeout(marker);
+	    } catch (e){
+	        try {
+	            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
+	            return cachedClearTimeout.call(null, marker);
+	        } catch (e){
+	            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
+	            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
+	            return cachedClearTimeout.call(this, marker);
+	        }
+	    }
+
+
+
+	}
+	var queue = [];
+	var draining = false;
+	var currentQueue;
+	var queueIndex = -1;
+
+	function cleanUpNextTick() {
+	    if (!draining || !currentQueue) {
+	        return;
+	    }
+	    draining = false;
+	    if (currentQueue.length) {
+	        queue = currentQueue.concat(queue);
+	    } else {
+	        queueIndex = -1;
+	    }
+	    if (queue.length) {
+	        drainQueue();
+	    }
+	}
+
+	function drainQueue() {
+	    if (draining) {
+	        return;
+	    }
+	    var timeout = runTimeout(cleanUpNextTick);
+	    draining = true;
+
+	    var len = queue.length;
+	    while(len) {
+	        currentQueue = queue;
+	        queue = [];
+	        while (++queueIndex < len) {
+	            if (currentQueue) {
+	                currentQueue[queueIndex].run();
+	            }
+	        }
+	        queueIndex = -1;
+	        len = queue.length;
+	    }
+	    currentQueue = null;
+	    draining = false;
+	    runClearTimeout(timeout);
+	}
+
+	process.nextTick = function (fun) {
+	    var args = new Array(arguments.length - 1);
+	    if (arguments.length > 1) {
+	        for (var i = 1; i < arguments.length; i++) {
+	            args[i - 1] = arguments[i];
+	        }
+	    }
+	    queue.push(new Item(fun, args));
+	    if (queue.length === 1 && !draining) {
+	        runTimeout(drainQueue);
+	    }
+	};
+
+	// v8 likes predictible objects
+	function Item(fun, array) {
+	    this.fun = fun;
+	    this.array = array;
+	}
+	Item.prototype.run = function () {
+	    this.fun.apply(null, this.array);
+	};
+	process.title = 'browser';
+	process.browser = true;
+	process.env = {};
+	process.argv = [];
+	process.version = ''; // empty string to avoid regexp issues
+	process.versions = {};
+
+	function noop() {}
+
+	process.on = noop;
+	process.addListener = noop;
+	process.once = noop;
+	process.off = noop;
+	process.removeListener = noop;
+	process.removeAllListeners = noop;
+	process.emit = noop;
+	process.prependListener = noop;
+	process.prependOnceListener = noop;
+
+	process.listeners = function (name) { return [] }
+
+	process.binding = function (name) {
+	    throw new Error('process.binding is not supported');
+	};
+
+	process.cwd = function () { return '/' };
+	process.chdir = function (dir) {
+	    throw new Error('process.chdir is not supported');
+	};
+	process.umask = function() { return 0; };
+
+
+/***/ }),
+/* 11 */
+/***/ (function(module, exports) {
+
+	module.exports = function isBuffer(arg) {
+	  return arg && typeof arg === 'object'
+	    && typeof arg.copy === 'function'
+	    && typeof arg.fill === 'function'
+	    && typeof arg.readUInt8 === 'function';
+	}
+
+/***/ }),
+/* 12 */
+/***/ (function(module, exports) {
+
+	if (typeof Object.create === 'function') {
+	  // implementation from standard node.js 'util' module
+	  module.exports = function inherits(ctor, superCtor) {
+	    ctor.super_ = superCtor
+	    ctor.prototype = Object.create(superCtor.prototype, {
+	      constructor: {
+	        value: ctor,
+	        enumerable: false,
+	        writable: true,
+	        configurable: true
+	      }
+	    });
+	  };
+	} else {
+	  // old school shim for old browsers
+	  module.exports = function inherits(ctor, superCtor) {
+	    ctor.super_ = superCtor
+	    var TempCtor = function () {}
+	    TempCtor.prototype = superCtor.prototype
+	    ctor.prototype = new TempCtor()
+	    ctor.prototype.constructor = ctor
+	  }
+	}
+
+
+/***/ }),
+/* 13 */
+/***/ (function(module, exports, __webpack_require__) {
+
+	/*!
+	 * Copyright (c) 2018, Salesforce.com, Inc.
+	 * All rights reserved.
+	 *
+	 * Redistribution and use in source and binary forms, with or without
+	 * modification, are permitted provided that the following conditions are met:
+	 *
+	 * 1. Redistributions of source code must retain the above copyright notice,
+	 * this list of conditions and the following disclaimer.
+	 *
+	 * 2. Redistributions in binary form must reproduce the above copyright notice,
+	 * this list of conditions and the following disclaimer in the documentation
+	 * and/or other materials provided with the distribution.
+	 *
+	 * 3. Neither the name of Salesforce.com nor the names of its contributors may
+	 * be used to endorse or promote products derived from this software without
+	 * specific prior written permission.
+	 *
+	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+	 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+	 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+	 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+	 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+	 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+	 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+	 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+	 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+	 * POSSIBILITY OF SUCH DAMAGE.
+	 */
+	'use strict';
+	var psl = __webpack_require__(14);
+
+	function getPublicSuffix(domain) {
+	  return psl.get(domain);
+	}
+
+	exports.getPublicSuffix = getPublicSuffix;
+
+
+/***/ }),
+/* 14 */
+/***/ (function(module, exports, __webpack_require__) {
+
+	/*eslint no-var:0, prefer-arrow-callback: 0, object-shorthand: 0 */
+	'use strict';
+
+
+	var Punycode = __webpack_require__(15);
+
+
+	var internals = {};
+
+
+	//
+	// Read rules from file.
+	//
+	internals.rules = __webpack_require__(16).map(function (rule) {
+
+	  return {
+	    rule: rule,
+	    suffix: rule.replace(/^(\*\.|\!)/, ''),
+	    punySuffix: -1,
+	    wildcard: rule.charAt(0) === '*',
+	    exception: rule.charAt(0) === '!'
+	  };
+	});
+
+
+	//
+	// Check is given string ends with `suffix`.
+	//
+	internals.endsWith = function (str, suffix) {
+
+	  return str.indexOf(suffix, str.length - suffix.length) !== -1;
+	};
+
+
+	//
+	// Find rule for a given domain.
+	//
+	internals.findRule = function (domain) {
+
+	  var punyDomain = Punycode.toASCII(domain);
+	  return internals.rules.reduce(function (memo, rule) {
+
+	    if (rule.punySuffix === -1){
+	      rule.punySuffix = Punycode.toASCII(rule.suffix);
+	    }
+	    if (!internals.endsWith(punyDomain, '.' + rule.punySuffix) && punyDomain !== rule.punySuffix) {
+	      return memo;
+	    }
+	    // This has been commented out as it never seems to run. This is because
+	    // sub tlds always appear after their parents and we never find a shorter
+	    // match.
+	    //if (memo) {
+	    //  var memoSuffix = Punycode.toASCII(memo.suffix);
+	    //  if (memoSuffix.length >= punySuffix.length) {
+	    //    return memo;
+	    //  }
+	    //}
+	    return rule;
+	  }, null);
+	};
+
+
+	//
+	// Error codes and messages.
+	//
+	exports.errorCodes = {
+	  DOMAIN_TOO_SHORT: 'Domain name too short.',
+	  DOMAIN_TOO_LONG: 'Domain name too long. It should be no more than 255 chars.',
+	  LABEL_STARTS_WITH_DASH: 'Domain name label can not start with a dash.',
+	  LABEL_ENDS_WITH_DASH: 'Domain name label can not end with a dash.',
+	  LABEL_TOO_LONG: 'Domain name label should be at most 63 chars long.',
+	  LABEL_TOO_SHORT: 'Domain name label should be at least 1 character long.',
+	  LABEL_INVALID_CHARS: 'Domain name label can only contain alphanumeric characters or dashes.'
+	};
+
+
+	//
+	// Validate domain name and throw if not valid.
+	//
+	// From wikipedia:
+	//
+	// Hostnames are composed of series of labels concatenated with dots, as are all
+	// domain names. Each label must be between 1 and 63 characters long, and the
+	// entire hostname (including the delimiting dots) has a maximum of 255 chars.
+	//
+	// Allowed chars:
+	//
+	// * `a-z`
+	// * `0-9`
+	// * `-` but not as a starting or ending character
+	// * `.` as a separator for the textual portions of a domain name
+	//
+	// * http://en.wikipedia.org/wiki/Domain_name
+	// * http://en.wikipedia.org/wiki/Hostname
+	//
+	internals.validate = function (input) {
+
+	  // Before we can validate we need to take care of IDNs with unicode chars.
+	  var ascii = Punycode.toASCII(input);
+
+	  if (ascii.length < 1) {
+	    return 'DOMAIN_TOO_SHORT';
+	  }
+	  if (ascii.length > 255) {
+	    return 'DOMAIN_TOO_LONG';
+	  }
+
+	  // Check each part's length and allowed chars.
+	  var labels = ascii.split('.');
+	  var label;
+
+	  for (var i = 0; i < labels.length; ++i) {
+	    label = labels[i];
+	    if (!label.length) {
+	      return 'LABEL_TOO_SHORT';
+	    }
+	    if (label.length > 63) {
+	      return 'LABEL_TOO_LONG';
+	    }
+	    if (label.charAt(0) === '-') {
+	      return 'LABEL_STARTS_WITH_DASH';
+	    }
+	    if (label.charAt(label.length - 1) === '-') {
+	      return 'LABEL_ENDS_WITH_DASH';
+	    }
+	    if (!/^[a-z0-9\-]+$/.test(label)) {
+	      return 'LABEL_INVALID_CHARS';
+	    }
+	  }
+	};
+
+
+	//
+	// Public API
+	//
+
+
+	//
+	// Parse domain.
+	//
+	exports.parse = function (input) {
+
+	  if (typeof input !== 'string') {
+	    throw new TypeError('Domain name must be a string.');
+	  }
+
+	  // Force domain to lowercase.
+	  var domain = input.slice(0).toLowerCase();
+
+	  // Handle FQDN.
+	  // TODO: Simply remove trailing dot?
+	  if (domain.charAt(domain.length - 1) === '.') {
+	    domain = domain.slice(0, domain.length - 1);
+	  }
+
+	  // Validate and sanitise input.
+	  var error = internals.validate(domain);
+	  if (error) {
+	    return {
+	      input: input,
+	      error: {
+	        message: exports.errorCodes[error],
+	        code: error
+	      }
+	    };
+	  }
+
+	  var parsed = {
+	    input: input,
+	    tld: null,
+	    sld: null,
+	    domain: null,
+	    subdomain: null,
+	    listed: false
+	  };
+
+	  var domainParts = domain.split('.');
+
+	  // Non-Internet TLD
+	  if (domainParts[domainParts.length - 1] === 'local') {
+	    return parsed;
+	  }
+
+	  var handlePunycode = function () {
+
+	    if (!/xn--/.test(domain)) {
+	      return parsed;
+	    }
+	    if (parsed.domain) {
+	      parsed.domain = Punycode.toASCII(parsed.domain);
+	    }
+	    if (parsed.subdomain) {
+	      parsed.subdomain = Punycode.toASCII(parsed.subdomain);
+	    }
+	    return parsed;
+	  };
+
+	  var rule = internals.findRule(domain);
+
+	  // Unlisted tld.
+	  if (!rule) {
+	    if (domainParts.length < 2) {
+	      return parsed;
+	    }
+	    parsed.tld = domainParts.pop();
+	    parsed.sld = domainParts.pop();
+	    parsed.domain = [parsed.sld, parsed.tld].join('.');
+	    if (domainParts.length) {
+	      parsed.subdomain = domainParts.pop();
+	    }
+	    return handlePunycode();
+	  }
+
+	  // At this point we know the public suffix is listed.
+	  parsed.listed = true;
+
+	  var tldParts = rule.suffix.split('.');
+	  var privateParts = domainParts.slice(0, domainParts.length - tldParts.length);
+
+	  if (rule.exception) {
+	    privateParts.push(tldParts.shift());
+	  }
+
+	  parsed.tld = tldParts.join('.');
+
+	  if (!privateParts.length) {
+	    return handlePunycode();
+	  }
+
+	  if (rule.wildcard) {
+	    tldParts.unshift(privateParts.pop());
+	    parsed.tld = tldParts.join('.');
+	  }
+
+	  if (!privateParts.length) {
+	    return handlePunycode();
+	  }
+
+	  parsed.sld = privateParts.pop();
+	  parsed.domain = [parsed.sld,  parsed.tld].join('.');
+
+	  if (privateParts.length) {
+	    parsed.subdomain = privateParts.join('.');
+	  }
+
+	  return handlePunycode();
+	};
+
+
+	//
+	// Get domain.
+	//
+	exports.get = function (domain) {
+
+	  if (!domain) {
+	    return null;
+	  }
+	  return exports.parse(domain).domain || null;
+	};
+
+
+	//
+	// Check whether domain belongs to a known public suffix.
+	//
+	exports.isValid = function (domain) {
+
+	  var parsed = exports.parse(domain);
+	  return Boolean(parsed.domain && parsed.listed);
+	};
+
+
+/***/ }),
+/* 15 */
+/***/ (function(module, exports, __webpack_require__) {
+
+	var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module, global) {/*! https://mths.be/punycode v1.4.1 by @mathias */
+	;(function(root) {
+
+		/** Detect free variables */
+		var freeExports = typeof exports == 'object' && exports &&
+			!exports.nodeType && exports;
+		var freeModule = typeof module == 'object' && module &&
+			!module.nodeType && module;
+		var freeGlobal = typeof global == 'object' && global;
+		if (
+			freeGlobal.global === freeGlobal ||
+			freeGlobal.window === freeGlobal ||
+			freeGlobal.self === freeGlobal
+		) {
+			root = freeGlobal;
+		}
+
+		/**
+		 * The `punycode` object.
+		 * @name punycode
+		 * @type Object
+		 */
+		var punycode,
+
+		/** Highest positive signed 32-bit float value */
+		maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
+
+		/** Bootstring parameters */
+		base = 36,
+		tMin = 1,
+		tMax = 26,
+		skew = 38,
+		damp = 700,
+		initialBias = 72,
+		initialN = 128, // 0x80
+		delimiter = '-', // '\x2D'
+
+		/** Regular expressions */
+		regexPunycode = /^xn--/,
+		regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
+		regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
+
+		/** Error messages */
+		errors = {
+			'overflow': 'Overflow: input needs wider integers to process',
+			'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
+			'invalid-input': 'Invalid input'
+		},
+
+		/** Convenience shortcuts */
+		baseMinusTMin = base - tMin,
+		floor = Math.floor,
+		stringFromCharCode = String.fromCharCode,
+
+		/** Temporary variable */
+		key;
+
+		/*--------------------------------------------------------------------------*/
+
+		/**
+		 * A generic error utility function.
+		 * @private
+		 * @param {String} type The error type.
+		 * @returns {Error} Throws a `RangeError` with the applicable error message.
+		 */
+		function error(type) {
+			throw new RangeError(errors[type]);
+		}
+
+		/**
+		 * A generic `Array#map` utility function.
+		 * @private
+		 * @param {Array} array The array to iterate over.
+		 * @param {Function} callback The function that gets called for every array
+		 * item.
+		 * @returns {Array} A new array of values returned by the callback function.
+		 */
+		function map(array, fn) {
+			var length = array.length;
+			var result = [];
+			while (length--) {
+				result[length] = fn(array[length]);
+			}
+			return result;
+		}
+
+		/**
+		 * A simple `Array#map`-like wrapper to work with domain name strings or email
+		 * addresses.
+		 * @private
+		 * @param {String} domain The domain name or email address.
+		 * @param {Function} callback The function that gets called for every
+		 * character.
+		 * @returns {Array} A new string of characters returned by the callback
+		 * function.
+		 */
+		function mapDomain(string, fn) {
+			var parts = string.split('@');
+			var result = '';
+			if (parts.length > 1) {
+				// In email addresses, only the domain name should be punycoded. Leave
+				// the local part (i.e. everything up to `@`) intact.
+				result = parts[0] + '@';
+				string = parts[1];
+			}
+			// Avoid `split(regex)` for IE8 compatibility. See #17.
+			string = string.replace(regexSeparators, '\x2E');
+			var labels = string.split('.');
+			var encoded = map(labels, fn).join('.');
+			return result + encoded;
+		}
+
+		/**
+		 * Creates an array containing the numeric code points of each Unicode
+		 * character in the string. While JavaScript uses UCS-2 internally,
+		 * this function will convert a pair of surrogate halves (each of which
+		 * UCS-2 exposes as separate characters) into a single code point,
+		 * matching UTF-16.
+		 * @see `punycode.ucs2.encode`
+		 * @see <https://mathiasbynens.be/notes/javascript-encoding>
+		 * @memberOf punycode.ucs2
+		 * @name decode
+		 * @param {String} string The Unicode input string (UCS-2).
+		 * @returns {Array} The new array of code points.
+		 */
+		function ucs2decode(string) {
+			var output = [],
+			    counter = 0,
+			    length = string.length,
+			    value,
+			    extra;
+			while (counter < length) {
+				value = string.charCodeAt(counter++);
+				if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
+					// high surrogate, and there is a next character
+					extra = string.charCodeAt(counter++);
+					if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+						output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+					} else {
+						// unmatched surrogate; only append this code unit, in case the next
+						// code unit is the high surrogate of a surrogate pair
+						output.push(value);
+						counter--;
+					}
+				} else {
+					output.push(value);
+				}
+			}
+			return output;
+		}
+
+		/**
+		 * Creates a string based on an array of numeric code points.
+		 * @see `punycode.ucs2.decode`
+		 * @memberOf punycode.ucs2
+		 * @name encode
+		 * @param {Array} codePoints The array of numeric code points.
+		 * @returns {String} The new Unicode string (UCS-2).
+		 */
+		function ucs2encode(array) {
+			return map(array, function(value) {
+				var output = '';
+				if (value > 0xFFFF) {
+					value -= 0x10000;
+					output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+					value = 0xDC00 | value & 0x3FF;
+				}
+				output += stringFromCharCode(value);
+				return output;
+			}).join('');
+		}
+
+		/**
+		 * Converts a basic code point into a digit/integer.
+		 * @see `digitToBasic()`
+		 * @private
+		 * @param {Number} codePoint The basic numeric code point value.
+		 * @returns {Number} The numeric value of a basic code point (for use in
+		 * representing integers) in the range `0` to `base - 1`, or `base` if
+		 * the code point does not represent a value.
+		 */
+		function basicToDigit(codePoint) {
+			if (codePoint - 48 < 10) {
+				return codePoint - 22;
+			}
+			if (codePoint - 65 < 26) {
+				return codePoint - 65;
+			}
+			if (codePoint - 97 < 26) {
+				return codePoint - 97;
+			}
+			return base;
+		}
+
+		/**
+		 * Converts a digit/integer into a basic code point.
+		 * @see `basicToDigit()`
+		 * @private
+		 * @param {Number} digit The numeric value of a basic code point.
+		 * @returns {Number} The basic code point whose value (when used for
+		 * representing integers) is `digit`, which needs to be in the range
+		 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
+		 * used; else, the lowercase form is used. The behavior is undefined
+		 * if `flag` is non-zero and `digit` has no uppercase form.
+		 */
+		function digitToBasic(digit, flag) {
+			//  0..25 map to ASCII a..z or A..Z
+			// 26..35 map to ASCII 0..9
+			return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+		}
+
+		/**
+		 * Bias adaptation function as per section 3.4 of RFC 3492.
+		 * https://tools.ietf.org/html/rfc3492#section-3.4
+		 * @private
+		 */
+		function adapt(delta, numPoints, firstTime) {
+			var k = 0;
+			delta = firstTime ? floor(delta / damp) : delta >> 1;
+			delta += floor(delta / numPoints);
+			for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
+				delta = floor(delta / baseMinusTMin);
+			}
+			return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
+		}
+
+		/**
+		 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
+		 * symbols.
+		 * @memberOf punycode
+		 * @param {String} input The Punycode string of ASCII-only symbols.
+		 * @returns {String} The resulting string of Unicode symbols.
+		 */
+		function decode(input) {
+			// Don't use UCS-2
+			var output = [],
+			    inputLength = input.length,
+			    out,
+			    i = 0,
+			    n = initialN,
+			    bias = initialBias,
+			    basic,
+			    j,
+			    index,
+			    oldi,
+			    w,
+			    k,
+			    digit,
+			    t,
+			    /** Cached calculation results */
+			    baseMinusT;
+
+			// Handle the basic code points: let `basic` be the number of input code
+			// points before the last delimiter, or `0` if there is none, then copy
+			// the first basic code points to the output.
+
+			basic = input.lastIndexOf(delimiter);
+			if (basic < 0) {
+				basic = 0;
+			}
+
+			for (j = 0; j < basic; ++j) {
+				// if it's not a basic code point
+				if (input.charCodeAt(j) >= 0x80) {
+					error('not-basic');
+				}
+				output.push(input.charCodeAt(j));
+			}
+
+			// Main decoding loop: start just after the last delimiter if any basic code
+			// points were copied; start at the beginning otherwise.
+
+			for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
+
+				// `index` is the index of the next character to be consumed.
+				// Decode a generalized variable-length integer into `delta`,
+				// which gets added to `i`. The overflow checking is easier
+				// if we increase `i` as we go, then subtract off its starting
+				// value at the end to obtain `delta`.
+				for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
+
+					if (index >= inputLength) {
+						error('invalid-input');
+					}
+
+					digit = basicToDigit(input.charCodeAt(index++));
+
+					if (digit >= base || digit > floor((maxInt - i) / w)) {
+						error('overflow');
+					}
+
+					i += digit * w;
+					t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+
+					if (digit < t) {
+						break;
+					}
+
+					baseMinusT = base - t;
+					if (w > floor(maxInt / baseMinusT)) {
+						error('overflow');
+					}
+
+					w *= baseMinusT;
+
+				}
+
+				out = output.length + 1;
+				bias = adapt(i - oldi, out, oldi == 0);
+
+				// `i` was supposed to wrap around from `out` to `0`,
+				// incrementing `n` each time, so we'll fix that now:
+				if (floor(i / out) > maxInt - n) {
+					error('overflow');
+				}
+
+				n += floor(i / out);
+				i %= out;
+
+				// Insert `n` at position `i` of the output
+				output.splice(i++, 0, n);
+
+			}
+
+			return ucs2encode(output);
+		}
+
+		/**
+		 * Converts a string of Unicode symbols (e.g. a domain name label) to a
+		 * Punycode string of ASCII-only symbols.
+		 * @memberOf punycode
+		 * @param {String} input The string of Unicode symbols.
+		 * @returns {String} The resulting Punycode string of ASCII-only symbols.
+		 */
+		function encode(input) {
+			var n,
+			    delta,
+			    handledCPCount,
+			    basicLength,
+			    bias,
+			    j,
+			    m,
+			    q,
+			    k,
+			    t,
+			    currentValue,
+			    output = [],
+			    /** `inputLength` will hold the number of code points in `input`. */
+			    inputLength,
+			    /** Cached calculation results */
+			    handledCPCountPlusOne,
+			    baseMinusT,
+			    qMinusT;
+
+			// Convert the input in UCS-2 to Unicode
+			input = ucs2decode(input);
+
+			// Cache the length
+			inputLength = input.length;
+
+			// Initialize the state
+			n = initialN;
+			delta = 0;
+			bias = initialBias;
+
+			// Handle the basic code points
+			for (j = 0; j < inputLength; ++j) {
+				currentValue = input[j];
+				if (currentValue < 0x80) {
+					output.push(stringFromCharCode(currentValue));
+				}
+			}
+
+			handledCPCount = basicLength = output.length;
+
+			// `handledCPCount` is the number of code points that have been handled;
+			// `basicLength` is the number of basic code points.
+
+			// Finish the basic string - if it is not empty - with a delimiter
+			if (basicLength) {
+				output.push(delimiter);
+			}
+
+			// Main encoding loop:
+			while (handledCPCount < inputLength) {
+
+				// All non-basic code points < n have been handled already. Find the next
+				// larger one:
+				for (m = maxInt, j = 0; j < inputLength; ++j) {
+					currentValue = input[j];
+					if (currentValue >= n && currentValue < m) {
+						m = currentValue;
+					}
+				}
+
+				// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
+				// but guard against overflow
+				handledCPCountPlusOne = handledCPCount + 1;
+				if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
+					error('overflow');
+				}
+
+				delta += (m - n) * handledCPCountPlusOne;
+				n = m;
+
+				for (j = 0; j < inputLength; ++j) {
+					currentValue = input[j];
+
+					if (currentValue < n && ++delta > maxInt) {
+						error('overflow');
+					}
+
+					if (currentValue == n) {
+						// Represent delta as a generalized variable-length integer
+						for (q = delta, k = base; /* no condition */; k += base) {
+							t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+							if (q < t) {
+								break;
+							}
+							qMinusT = q - t;
+							baseMinusT = base - t;
+							output.push(
+								stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
+							);
+							q = floor(qMinusT / baseMinusT);
+						}
+
+						output.push(stringFromCharCode(digitToBasic(q, 0)));
+						bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
+						delta = 0;
+						++handledCPCount;
+					}
+				}
+
+				++delta;
+				++n;
+
+			}
+			return output.join('');
+		}
+
+		/**
+		 * Converts a Punycode string representing a domain name or an email address
+		 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
+		 * it doesn't matter if you call it on a string that has already been
+		 * converted to Unicode.
+		 * @memberOf punycode
+		 * @param {String} input The Punycoded domain name or email address to
+		 * convert to Unicode.
+		 * @returns {String} The Unicode representation of the given Punycode
+		 * string.
+		 */
+		function toUnicode(input) {
+			return mapDomain(input, function(string) {
+				return regexPunycode.test(string)
+					? decode(string.slice(4).toLowerCase())
+					: string;
+			});
+		}
+
+		/**
+		 * Converts a Unicode string representing a domain name or an email address to
+		 * Punycode. Only the non-ASCII parts of the domain name will be converted,
+		 * i.e. it doesn't matter if you call it with a domain that's already in
+		 * ASCII.
+		 * @memberOf punycode
+		 * @param {String} input The domain name or email address to convert, as a
+		 * Unicode string.
+		 * @returns {String} The Punycode representation of the given domain name or
+		 * email address.
+		 */
+		function toASCII(input) {
+			return mapDomain(input, function(string) {
+				return regexNonASCII.test(string)
+					? 'xn--' + encode(string)
+					: string;
+			});
+		}
+
+		/*--------------------------------------------------------------------------*/
+
+		/** Define the public API */
+		punycode = {
+			/**
+			 * A string representing the current Punycode.js version number.
+			 * @memberOf punycode
+			 * @type String
+			 */
+			'version': '1.4.1',
+			/**
+			 * An object of methods to convert from JavaScript's internal character
+			 * representation (UCS-2) to Unicode code points, and back.
+			 * @see <https://mathiasbynens.be/notes/javascript-encoding>
+			 * @memberOf punycode
+			 * @type Object
+			 */
+			'ucs2': {
+				'decode': ucs2decode,
+				'encode': ucs2encode
+			},
+			'decode': decode,
+			'encode': encode,
+			'toASCII': toASCII,
+			'toUnicode': toUnicode
+		};
+
+		/** Expose `punycode` */
+		// Some AMD build optimizers, like r.js, check for specific condition patterns
+		// like the following:
+		if (
+			true
+		) {
+			!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
+				return punycode;
+			}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+		} else if (freeExports && freeModule) {
+			if (module.exports == freeExports) {
+				// in Node.js, io.js, or RingoJS v0.8.0+
+				freeModule.exports = punycode;
+			} else {
+				// in Narwhal or RingoJS v0.7.0-
+				for (key in punycode) {
+					punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
+				}
+			}
+		} else {
+			// in Rhino or a web browser
+			root.punycode = punycode;
+		}
+
+	}(this));
+
+	/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)(module), (function() { return this; }())))
+
+/***/ }),
+/* 16 */
+/***/ (function(module, exports) {
+
+	module.exports = ["ac","com.ac","edu.ac","gov.ac","net.ac","mil.ac","org.ac","ad","nom.ad","ae","co.ae","net.ae","org.ae","sch.ae","ac.ae","gov.ae","mil.ae","aero","accident-investigation.aero","accident-prevention.aero","aerobatic.aero","aeroclub.aero","aerodrome.aero","agents.aero","aircraft.aero","airline.aero","airport.aero","air-surveillance.aero","airtraffic.aero","air-traffic-control.aero","ambulance.aero","amusement.aero","association.aero","author.aero","ballooning.aero","broker.aero","caa.aero","cargo.aero","catering.aero","certification.aero","championship.aero","charter.aero","civilaviation.aero","club.aero","conference.aero","consultant.aero","consulting.aero","control.aero","council.aero","crew.aero","design.aero","dgca.aero","educator.aero","emergency.aero","engine.aero","engineer.aero","entertainment.aero","equipment.aero","exchange.aero","express.aero","federation.aero","flight.aero","freight.aero","fuel.aero","gliding.aero","government.aero","groundhandling.aero","group.aero","hanggliding.aero","homebuilt.aero","insurance.aero","journal.aero","journalist.aero","leasing.aero","logistics.aero","magazine.aero","maintenance.aero","media.aero","microlight.aero","modelling.aero","navigation.aero","parachuting.aero","paragliding.aero","passenger-association.aero","pilot.aero","press.aero","production.aero","recreation.aero","repbody.aero","res.aero","research.aero","rotorcraft.aero","safety.aero","scientist.aero","services.aero","show.aero","skydiving.aero","software.aero","student.aero","trader.aero","trading.aero","trainer.aero","union.aero","workinggroup.aero","works.aero","af","gov.af","com.af","org.af","net.af","edu.af","ag","com.ag","org.ag","net.ag","co.ag","nom.ag","ai","off.ai","com.ai","net.ai","org.ai","al","com.al","edu.al","gov.al","mil.al","net.al","org.al","am","ao","ed.ao","gv.ao","og.ao","co.ao","pb.ao","it.ao","aq","ar","com.ar","edu.ar","gob.ar","gov.ar","int.ar","mil.ar","musica.ar","net.ar","org.ar","tur.ar","arpa","e164.arpa","in-addr.arpa","ip6.arpa","iris.arpa","uri.arpa","urn.arpa","as","gov.as","asia","at","ac.at","co.at","gv.at","or.at","au","com.au","net.au","org.au","edu.au","gov.au","asn.au","id.au","info.au","conf.au","oz.au","act.au","nsw.au","nt.au","qld.au","sa.au","tas.au","vic.au","wa.au","act.edu.au","nsw.edu.au","nt.edu.au","qld.edu.au","sa.edu.au","tas.edu.au","vic.edu.au","wa.edu.au","qld.gov.au","sa.gov.au","tas.gov.au","vic.gov.au","wa.gov.au","aw","com.aw","ax","az","com.az","net.az","int.az","gov.az","org.az","edu.az","info.az","pp.az","mil.az","name.az","pro.az","biz.az","ba","com.ba","edu.ba","gov.ba","mil.ba","net.ba","org.ba","bb","biz.bb","co.bb","com.bb","edu.bb","gov.bb","info.bb","net.bb","org.bb","store.bb","tv.bb","*.bd","be","ac.be","bf","gov.bf","bg","a.bg","b.bg","c.bg","d.bg","e.bg","f.bg","g.bg","h.bg","i.bg","j.bg","k.bg","l.bg","m.bg","n.bg","o.bg","p.bg","q.bg","r.bg","s.bg","t.bg","u.bg","v.bg","w.bg","x.bg","y.bg","z.bg","0.bg","1.bg","2.bg","3.bg","4.bg","5.bg","6.bg","7.bg","8.bg","9.bg","bh","com.bh","edu.bh","net.bh","org.bh","gov.bh","bi","co.bi","com.bi","edu.bi","or.bi","org.bi","biz","bj","asso.bj","barreau.bj","gouv.bj","bm","com.bm","edu.bm","gov.bm","net.bm","org.bm","*.bn","bo","com.bo","edu.bo","gob.bo","int.bo","org.bo","net.bo","mil.bo","tv.bo","web.bo","academia.bo","agro.bo","arte.bo","blog.bo","bolivia.bo","ciencia.bo","cooperativa.bo","democracia.bo","deporte.bo","ecologia.bo","economia.bo","empresa.bo","indigena.bo","industria.bo","info.bo","medicina.bo","movimiento.bo","musica.bo","natural.bo","nombre.bo","noticias.bo","patria.bo","politica.bo","profesional.bo","plurinacional.bo","pueblo.bo","revista.bo","salud.bo","tecnologia.bo","tksat.bo","transporte.bo","wiki.bo","br","9guacu.br","abc.br","adm.br","adv.br","agr.br","aju.br","am.br","anani.br","aparecida.br","arq.br","art.br","ato.br","b.br","barueri.br","belem.br","bhz.br","bio.br","blog.br","bmd.br","boavista.br","bsb.br","campinagrande.br","campinas.br","caxias.br","cim.br","cng.br","cnt.br","com.br","contagem.br","coop.br","cri.br","cuiaba.br","curitiba.br","def.br","ecn.br","eco.br","edu.br","emp.br","eng.br","esp.br","etc.br","eti.br","far.br","feira.br","flog.br","floripa.br","fm.br","fnd.br","fortal.br","fot.br","foz.br","fst.br","g12.br","ggf.br","goiania.br","gov.br","ac.gov.br","al.gov.br","am.gov.br","ap.gov.br","ba.gov.br","ce.gov.br","df.gov.br","es.gov.br","go.gov.br","ma.gov.br","mg.gov.br","ms.gov.br","mt.gov.br","pa.gov.br","pb.gov.br","pe.gov.br","pi.gov.br","pr.gov.br","rj.gov.br","rn.gov.br","ro.gov.br","rr.gov.br","rs.gov.br","sc.gov.br","se.gov.br","sp.gov.br","to.gov.br","gru.br","imb.br","ind.br","inf.br","jab.br","jampa.br","jdf.br","joinville.br","jor.br","jus.br","leg.br","lel.br","londrina.br","macapa.br","maceio.br","manaus.br","maringa.br","mat.br","med.br","mil.br","morena.br","mp.br","mus.br","natal.br","net.br","niteroi.br","*.nom.br","not.br","ntr.br","odo.br","org.br","osasco.br","palmas.br","poa.br","ppg.br","pro.br","psc.br","psi.br","pvh.br","qsl.br","radio.br","rec.br","recife.br","ribeirao.br","rio.br","riobranco.br","riopreto.br","salvador.br","sampa.br","santamaria.br","santoandre.br","saobernardo.br","saogonca.br","sjc.br","slg.br","slz.br","sorocaba.br","srv.br","taxi.br","teo.br","the.br","tmp.br","trd.br","tur.br","tv.br","udi.br","vet.br","vix.br","vlog.br","wiki.br","zlg.br","bs","com.bs","net.bs","org.bs","edu.bs","gov.bs","bt","com.bt","edu.bt","gov.bt","net.bt","org.bt","bv","bw","co.bw","org.bw","by","gov.by","mil.by","com.by","of.by","bz","com.bz","net.bz","org.bz","edu.bz","gov.bz","ca","ab.ca","bc.ca","mb.ca","nb.ca","nf.ca","nl.ca","ns.ca","nt.ca","nu.ca","on.ca","pe.ca","qc.ca","sk.ca","yk.ca","gc.ca","cat","cc","cd","gov.cd","cf","cg","ch","ci","org.ci","or.ci","com.ci","co.ci","edu.ci","ed.ci","ac.ci","net.ci","go.ci","asso.ci","aéroport.ci","int.ci","presse.ci","md.ci","gouv.ci","*.ck","!www.ck","cl","gov.cl","gob.cl","co.cl","mil.cl","cm","co.cm","com.cm","gov.cm","net.cm","cn","ac.cn","com.cn","edu.cn","gov.cn","net.cn","org.cn","mil.cn","公司.cn","网络.cn","網絡.cn","ah.cn","bj.cn","cq.cn","fj.cn","gd.cn","gs.cn","gz.cn","gx.cn","ha.cn","hb.cn","he.cn","hi.cn","hl.cn","hn.cn","jl.cn","js.cn","jx.cn","ln.cn","nm.cn","nx.cn","qh.cn","sc.cn","sd.cn","sh.cn","sn.cn","sx.cn","tj.cn","xj.cn","xz.cn","yn.cn","zj.cn","hk.cn","mo.cn","tw.cn","co","arts.co","com.co","edu.co","firm.co","gov.co","info.co","int.co","mil.co","net.co","nom.co","org.co","rec.co","web.co","com","coop","cr","ac.cr","co.cr","ed.cr","fi.cr","go.cr","or.cr","sa.cr","cu","com.cu","edu.cu","org.cu","net.cu","gov.cu","inf.cu","cv","cw","com.cw","edu.cw","net.cw","org.cw","cx","gov.cx","cy","ac.cy","biz.cy","com.cy","ekloges.cy","gov.cy","ltd.cy","name.cy","net.cy","org.cy","parliament.cy","press.cy","pro.cy","tm.cy","cz","de","dj","dk","dm","com.dm","net.dm","org.dm","edu.dm","gov.dm","do","art.do","com.do","edu.do","gob.do","gov.do","mil.do","net.do","org.do","sld.do","web.do","dz","com.dz","org.dz","net.dz","gov.dz","edu.dz","asso.dz","pol.dz","art.dz","ec","com.ec","info.ec","net.ec","fin.ec","k12.ec","med.ec","pro.ec","org.ec","edu.ec","gov.ec","gob.ec","mil.ec","edu","ee","edu.ee","gov.ee","riik.ee","lib.ee","med.ee","com.ee","pri.ee","aip.ee","org.ee","fie.ee","eg","com.eg","edu.eg","eun.eg","gov.eg","mil.eg","name.eg","net.eg","org.eg","sci.eg","*.er","es","com.es","nom.es","org.es","gob.es","edu.es","et","com.et","gov.et","org.et","edu.et","biz.et","name.et","info.et","net.et","eu","fi","aland.fi","*.fj","*.fk","fm","fo","fr","com.fr","asso.fr","nom.fr","prd.fr","presse.fr","tm.fr","aeroport.fr","assedic.fr","avocat.fr","avoues.fr","cci.fr","chambagri.fr","chirurgiens-dentistes.fr","experts-comptables.fr","geometre-expert.fr","gouv.fr","greta.fr","huissier-justice.fr","medecin.fr","notaires.fr","pharmacien.fr","port.fr","veterinaire.fr","ga","gb","gd","ge","com.ge","edu.ge","gov.ge","org.ge","mil.ge","net.ge","pvt.ge","gf","gg","co.gg","net.gg","org.gg","gh","com.gh","edu.gh","gov.gh","org.gh","mil.gh","gi","com.gi","ltd.gi","gov.gi","mod.gi","edu.gi","org.gi","gl","co.gl","com.gl","edu.gl","net.gl","org.gl","gm","gn","ac.gn","com.gn","edu.gn","gov.gn","org.gn","net.gn","gov","gp","com.gp","net.gp","mobi.gp","edu.gp","org.gp","asso.gp","gq","gr","com.gr","edu.gr","net.gr","org.gr","gov.gr","gs","gt","com.gt","edu.gt","gob.gt","ind.gt","mil.gt","net.gt","org.gt","gu","com.gu","edu.gu","gov.gu","guam.gu","info.gu","net.gu","org.gu","web.gu","gw","gy","co.gy","com.gy","edu.gy","gov.gy","net.gy","org.gy","hk","com.hk","edu.hk","gov.hk","idv.hk","net.hk","org.hk","公司.hk","教育.hk","敎育.hk","政府.hk","個人.hk","个人.hk","箇人.hk","網络.hk","网络.hk","组織.hk","網絡.hk","网絡.hk","组织.hk","組織.hk","組织.hk","hm","hn","com.hn","edu.hn","org.hn","net.hn","mil.hn","gob.hn","hr","iz.hr","from.hr","name.hr","com.hr","ht","com.ht","shop.ht","firm.ht","info.ht","adult.ht","net.ht","pro.ht","org.ht","med.ht","art.ht","coop.ht","pol.ht","asso.ht","edu.ht","rel.ht","gouv.ht","perso.ht","hu","co.hu","info.hu","org.hu","priv.hu","sport.hu","tm.hu","2000.hu","agrar.hu","bolt.hu","casino.hu","city.hu","erotica.hu","erotika.hu","film.hu","forum.hu","games.hu","hotel.hu","ingatlan.hu","jogasz.hu","konyvelo.hu","lakas.hu","media.hu","news.hu","reklam.hu","sex.hu","shop.hu","suli.hu","szex.hu","tozsde.hu","utazas.hu","video.hu","id","ac.id","biz.id","co.id","desa.id","go.id","mil.id","my.id","net.id","or.id","sch.id","web.id","ie","gov.ie","il","ac.il","co.il","gov.il","idf.il","k12.il","muni.il","net.il","org.il","im","ac.im","co.im","com.im","ltd.co.im","net.im","org.im","plc.co.im","tt.im","tv.im","in","co.in","firm.in","net.in","org.in","gen.in","ind.in","nic.in","ac.in","edu.in","res.in","gov.in","mil.in","info","int","eu.int","io","com.io","iq","gov.iq","edu.iq","mil.iq","com.iq","org.iq","net.iq","ir","ac.ir","co.ir","gov.ir","id.ir","net.ir","org.ir","sch.ir","ایران.ir","ايران.ir","is","net.is","com.is","edu.is","gov.is","org.is","int.is","it","gov.it","edu.it","abr.it","abruzzo.it","aosta-valley.it","aostavalley.it","bas.it","basilicata.it","cal.it","calabria.it","cam.it","campania.it","emilia-romagna.it","emiliaromagna.it","emr.it","friuli-v-giulia.it","friuli-ve-giulia.it","friuli-vegiulia.it","friuli-venezia-giulia.it","friuli-veneziagiulia.it","friuli-vgiulia.it","friuliv-giulia.it","friulive-giulia.it","friulivegiulia.it","friulivenezia-giulia.it","friuliveneziagiulia.it","friulivgiulia.it","fvg.it","laz.it","lazio.it","lig.it","liguria.it","lom.it","lombardia.it","lombardy.it","lucania.it","mar.it","marche.it","mol.it","molise.it","piedmont.it","piemonte.it","pmn.it","pug.it","puglia.it","sar.it","sardegna.it","sardinia.it","sic.it","sicilia.it","sicily.it","taa.it","tos.it","toscana.it","trentin-sud-tirol.it","trentin-süd-tirol.it","trentin-sudtirol.it","trentin-südtirol.it","trentin-sued-tirol.it","trentin-suedtirol.it","trentino-a-adige.it","trentino-aadige.it","trentino-alto-adige.it","trentino-altoadige.it","trentino-s-tirol.it","trentino-stirol.it","trentino-sud-tirol.it","trentino-süd-tirol.it","trentino-sudtirol.it","trentino-südtirol.it","trentino-sued-tirol.it","trentino-suedtirol.it","trentino.it","trentinoa-adige.it","trentinoaadige.it","trentinoalto-adige.it","trentinoaltoadige.it","trentinos-tirol.it","trentinostirol.it","trentinosud-tirol.it","trentinosüd-tirol.it","trentinosudtirol.it","trentinosüdtirol.it","trentinosued-tirol.it","trentinosuedtirol.it","trentinsud-tirol.it","trentinsüd-tirol.it","trentinsudtirol.it","trentinsüdtirol.it","trentinsued-tirol.it","trentinsuedtirol.it","tuscany.it","umb.it","umbria.it","val-d-aosta.it","val-daosta.it","vald-aosta.it","valdaosta.it","valle-aosta.it","valle-d-aosta.it","valle-daosta.it","valleaosta.it","valled-aosta.it","valledaosta.it","vallee-aoste.it","vallée-aoste.it","vallee-d-aoste.it","vallée-d-aoste.it","valleeaoste.it","valléeaoste.it","valleedaoste.it","valléedaoste.it","vao.it","vda.it","ven.it","veneto.it","ag.it","agrigento.it","al.it","alessandria.it","alto-adige.it","altoadige.it","an.it","ancona.it","andria-barletta-trani.it","andria-trani-barletta.it","andriabarlettatrani.it","andriatranibarletta.it","ao.it","aosta.it","aoste.it","ap.it","aq.it","aquila.it","ar.it","arezzo.it","ascoli-piceno.it","ascolipiceno.it","asti.it","at.it","av.it","avellino.it","ba.it","balsan-sudtirol.it","balsan-südtirol.it","balsan-suedtirol.it","balsan.it","bari.it","barletta-trani-andria.it","barlettatraniandria.it","belluno.it","benevento.it","bergamo.it","bg.it","bi.it","biella.it","bl.it","bn.it","bo.it","bologna.it","bolzano-altoadige.it","bolzano.it","bozen-sudtirol.it","bozen-südtirol.it","bozen-suedtirol.it","bozen.it","br.it","brescia.it","brindisi.it","bs.it","bt.it","bulsan-sudtirol.it","bulsan-südtirol.it","bulsan-suedtirol.it","bulsan.it","bz.it","ca.it","cagliari.it","caltanissetta.it","campidano-medio.it","campidanomedio.it","campobasso.it","carbonia-iglesias.it","carboniaiglesias.it","carrara-massa.it","carraramassa.it","caserta.it","catania.it","catanzaro.it","cb.it","ce.it","cesena-forli.it","cesena-forlì.it","cesenaforli.it","cesenaforlì.it","ch.it","chieti.it","ci.it","cl.it","cn.it","co.it","como.it","cosenza.it","cr.it","cremona.it","crotone.it","cs.it","ct.it","cuneo.it","cz.it","dell-ogliastra.it","dellogliastra.it","en.it","enna.it","fc.it","fe.it","fermo.it","ferrara.it","fg.it","fi.it","firenze.it","florence.it","fm.it","foggia.it","forli-cesena.it","forlì-cesena.it","forlicesena.it","forlìcesena.it","fr.it","frosinone.it","ge.it","genoa.it","genova.it","go.it","gorizia.it","gr.it","grosseto.it","iglesias-carbonia.it","iglesiascarbonia.it","im.it","imperia.it","is.it","isernia.it","kr.it","la-spezia.it","laquila.it","laspezia.it","latina.it","lc.it","le.it","lecce.it","lecco.it","li.it","livorno.it","lo.it","lodi.it","lt.it","lu.it","lucca.it","macerata.it","mantova.it","massa-carrara.it","massacarrara.it","matera.it","mb.it","mc.it","me.it","medio-campidano.it","mediocampidano.it","messina.it","mi.it","milan.it","milano.it","mn.it","mo.it","modena.it","monza-brianza.it","monza-e-della-brianza.it","monza.it","monzabrianza.it","monzaebrianza.it","monzaedellabrianza.it","ms.it","mt.it","na.it","naples.it","napoli.it","no.it","novara.it","nu.it","nuoro.it","og.it","ogliastra.it","olbia-tempio.it","olbiatempio.it","or.it","oristano.it","ot.it","pa.it","padova.it","padua.it","palermo.it","parma.it","pavia.it","pc.it","pd.it","pe.it","perugia.it","pesaro-urbino.it","pesarourbino.it","pescara.it","pg.it","pi.it","piacenza.it","pisa.it","pistoia.it","pn.it","po.it","pordenone.it","potenza.it","pr.it","prato.it","pt.it","pu.it","pv.it","pz.it","ra.it","ragusa.it","ravenna.it","rc.it","re.it","reggio-calabria.it","reggio-emilia.it","reggiocalabria.it","reggioemilia.it","rg.it","ri.it","rieti.it","rimini.it","rm.it","rn.it","ro.it","roma.it","rome.it","rovigo.it","sa.it","salerno.it","sassari.it","savona.it","si.it","siena.it","siracusa.it","so.it","sondrio.it","sp.it","sr.it","ss.it","suedtirol.it","südtirol.it","sv.it","ta.it","taranto.it","te.it","tempio-olbia.it","tempioolbia.it","teramo.it","terni.it","tn.it","to.it","torino.it","tp.it","tr.it","trani-andria-barletta.it","trani-barletta-andria.it","traniandriabarletta.it","tranibarlettaandria.it","trapani.it","trento.it","treviso.it","trieste.it","ts.it","turin.it","tv.it","ud.it","udine.it","urbino-pesaro.it","urbinopesaro.it","va.it","varese.it","vb.it","vc.it","ve.it","venezia.it","venice.it","verbania.it","vercelli.it","verona.it","vi.it","vibo-valentia.it","vibovalentia.it","vicenza.it","viterbo.it","vr.it","vs.it","vt.it","vv.it","je","co.je","net.je","org.je","*.jm","jo","com.jo","org.jo","net.jo","edu.jo","sch.jo","gov.jo","mil.jo","name.jo","jobs","jp","ac.jp","ad.jp","co.jp","ed.jp","go.jp","gr.jp","lg.jp","ne.jp","or.jp","aichi.jp","akita.jp","aomori.jp","chiba.jp","ehime.jp","fukui.jp","fukuoka.jp","fukushima.jp","gifu.jp","gunma.jp","hiroshima.jp","hokkaido.jp","hyogo.jp","ibaraki.jp","ishikawa.jp","iwate.jp","kagawa.jp","kagoshima.jp","kanagawa.jp","kochi.jp","kumamoto.jp","kyoto.jp","mie.jp","miyagi.jp","miyazaki.jp","nagano.jp","nagasaki.jp","nara.jp","niigata.jp","oita.jp","okayama.jp","okinawa.jp","osaka.jp","saga.jp","saitama.jp","shiga.jp","shimane.jp","shizuoka.jp","tochigi.jp","tokushima.jp","tokyo.jp","tottori.jp","toyama.jp","wakayama.jp","yamagata.jp","yamaguchi.jp","yamanashi.jp","栃木.jp","愛知.jp","愛媛.jp","兵庫.jp","熊本.jp","茨城.jp","北海道.jp","千葉.jp","和歌山.jp","長崎.jp","長野.jp","新潟.jp","青森.jp","静岡.jp","東京.jp","石川.jp","埼玉.jp","三重.jp","京都.jp","佐賀.jp","大分.jp","大阪.jp","奈良.jp","宮城.jp","宮崎.jp","富山.jp","山口.jp","山形.jp","山梨.jp","岩手.jp","岐阜.jp","岡山.jp","島根.jp","広島.jp","徳島.jp","沖縄.jp","滋賀.jp","神奈川.jp","福井.jp","福岡.jp","福島.jp","秋田.jp","群馬.jp","香川.jp","高知.jp","鳥取.jp","鹿児島.jp","*.kawasaki.jp","*.kitakyushu.jp","*.kobe.jp","*.nagoya.jp","*.sapporo.jp","*.sendai.jp","*.yokohama.jp","!city.kawasaki.jp","!city.kitakyushu.jp","!city.kobe.jp","!city.nagoya.jp","!city.sapporo.jp","!city.sendai.jp","!city.yokohama.jp","aisai.aichi.jp","ama.aichi.jp","anjo.aichi.jp","asuke.aichi.jp","chiryu.aichi.jp","chita.aichi.jp","fuso.aichi.jp","gamagori.aichi.jp","handa.aichi.jp","hazu.aichi.jp","hekinan.aichi.jp","higashiura.aichi.jp","ichinomiya.aichi.jp","inazawa.aichi.jp","inuyama.aichi.jp","isshiki.aichi.jp","iwakura.aichi.jp","kanie.aichi.jp","kariya.aichi.jp","kasugai.aichi.jp","kira.aichi.jp","kiyosu.aichi.jp","komaki.aichi.jp","konan.aichi.jp","kota.aichi.jp","mihama.aichi.jp","miyoshi.aichi.jp","nishio.aichi.jp","nisshin.aichi.jp","obu.aichi.jp","oguchi.aichi.jp","oharu.aichi.jp","okazaki.aichi.jp","owariasahi.aichi.jp","seto.aichi.jp","shikatsu.aichi.jp","shinshiro.aichi.jp","shitara.aichi.jp","tahara.aichi.jp","takahama.aichi.jp","tobishima.aichi.jp","toei.aichi.jp","togo.aichi.jp","tokai.aichi.jp","tokoname.aichi.jp","toyoake.aichi.jp","toyohashi.aichi.jp","toyokawa.aichi.jp","toyone.aichi.jp","toyota.aichi.jp","tsushima.aichi.jp","yatomi.aichi.jp","akita.akita.jp","daisen.akita.jp","fujisato.akita.jp","gojome.akita.jp","hachirogata.akita.jp","happou.akita.jp","higashinaruse.akita.jp","honjo.akita.jp","honjyo.akita.jp","ikawa.akita.jp","kamikoani.akita.jp","kamioka.akita.jp","katagami.akita.jp","kazuno.akita.jp","kitaakita.akita.jp","kosaka.akita.jp","kyowa.akita.jp","misato.akita.jp","mitane.akita.jp","moriyoshi.akita.jp","nikaho.akita.jp","noshiro.akita.jp","odate.akita.jp","oga.akita.jp","ogata.akita.jp","semboku.akita.jp","yokote.akita.jp","yurihonjo.akita.jp","aomori.aomori.jp","gonohe.aomori.jp","hachinohe.aomori.jp","hashikami.aomori.jp","hiranai.aomori.jp","hirosaki.aomori.jp","itayanagi.aomori.jp","kuroishi.aomori.jp","misawa.aomori.jp","mutsu.aomori.jp","nakadomari.aomori.jp","noheji.aomori.jp","oirase.aomori.jp","owani.aomori.jp","rokunohe.aomori.jp","sannohe.aomori.jp","shichinohe.aomori.jp","shingo.aomori.jp","takko.aomori.jp","towada.aomori.jp","tsugaru.aomori.jp","tsuruta.aomori.jp","abiko.chiba.jp","asahi.chiba.jp","chonan.chiba.jp","chosei.chiba.jp","choshi.chiba.jp","chuo.chiba.jp","funabashi.chiba.jp","futtsu.chiba.jp","hanamigawa.chiba.jp","ichihara.chiba.jp","ichikawa.chiba.jp","ichinomiya.chiba.jp","inzai.chiba.jp","isumi.chiba.jp","kamagaya.chiba.jp","kamogawa.chiba.jp","kashiwa.chiba.jp","katori.chiba.jp","katsuura.chiba.jp","kimitsu.chiba.jp","kisarazu.chiba.jp","kozaki.chiba.jp","kujukuri.chiba.jp","kyonan.chiba.jp","matsudo.chiba.jp","midori.chiba.jp","mihama.chiba.jp","minamiboso.chiba.jp","mobara.chiba.jp","mutsuzawa.chiba.jp","nagara.chiba.jp","nagareyama.chiba.jp","narashino.chiba.jp","narita.chiba.jp","noda.chiba.jp","oamishirasato.chiba.jp","omigawa.chiba.jp","onjuku.chiba.jp","otaki.chiba.jp","sakae.chiba.jp","sakura.chiba.jp","shimofusa.chiba.jp","shirako.chiba.jp","shiroi.chiba.jp","shisui.chiba.jp","sodegaura.chiba.jp","sosa.chiba.jp","tako.chiba.jp","tateyama.chiba.jp","togane.chiba.jp","tohnosho.chiba.jp","tomisato.chiba.jp","urayasu.chiba.jp","yachimata.chiba.jp","yachiyo.chiba.jp","yokaichiba.chiba.jp","yokoshibahikari.chiba.jp","yotsukaido.chiba.jp","ainan.ehime.jp","honai.ehime.jp","ikata.ehime.jp","imabari.ehime.jp","iyo.ehime.jp","kamijima.ehime.jp","kihoku.ehime.jp","kumakogen.ehime.jp","masaki.ehime.jp","matsuno.ehime.jp","matsuyama.ehime.jp","namikata.ehime.jp","niihama.ehime.jp","ozu.ehime.jp","saijo.ehime.jp","seiyo.ehime.jp","shikokuchuo.ehime.jp","tobe.ehime.jp","toon.ehime.jp","uchiko.ehime.jp","uwajima.ehime.jp","yawatahama.ehime.jp","echizen.fukui.jp","eiheiji.fukui.jp","fukui.fukui.jp","ikeda.fukui.jp","katsuyama.fukui.jp","mihama.fukui.jp","minamiechizen.fukui.jp","obama.fukui.jp","ohi.fukui.jp","ono.fukui.jp","sabae.fukui.jp","sakai.fukui.jp","takahama.fukui.jp","tsuruga.fukui.jp","wakasa.fukui.jp","ashiya.fukuoka.jp","buzen.fukuoka.jp","chikugo.fukuoka.jp","chikuho.fukuoka.jp","chikujo.fukuoka.jp","chikushino.fukuoka.jp","chikuzen.fukuoka.jp","chuo.fukuoka.jp","dazaifu.fukuoka.jp","fukuchi.fukuoka.jp","hakata.fukuoka.jp","higashi.fukuoka.jp","hirokawa.fukuoka.jp","hisayama.fukuoka.jp","iizuka.fukuoka.jp","inatsuki.fukuoka.jp","kaho.fukuoka.jp","kasuga.fukuoka.jp","kasuya.fukuoka.jp","kawara.fukuoka.jp","keisen.fukuoka.jp","koga.fukuoka.jp","kurate.fukuoka.jp","kurogi.fukuoka.jp","kurume.fukuoka.jp","minami.fukuoka.jp","miyako.fukuoka.jp","miyama.fukuoka.jp","miyawaka.fukuoka.jp","mizumaki.fukuoka.jp","munakata.fukuoka.jp","nakagawa.fukuoka.jp","nakama.fukuoka.jp","nishi.fukuoka.jp","nogata.fukuoka.jp","ogori.fukuoka.jp","okagaki.fukuoka.jp","okawa.fukuoka.jp","oki.fukuoka.jp","omuta.fukuoka.jp","onga.fukuoka.jp","onojo.fukuoka.jp","oto.fukuoka.jp","saigawa.fukuoka.jp","sasaguri.fukuoka.jp","shingu.fukuoka.jp","shinyoshitomi.fukuoka.jp","shonai.fukuoka.jp","soeda.fukuoka.jp","sue.fukuoka.jp","tachiarai.fukuoka.jp","tagawa.fukuoka.jp","takata.fukuoka.jp","toho.fukuoka.jp","toyotsu.fukuoka.jp","tsuiki.fukuoka.jp","ukiha.fukuoka.jp","umi.fukuoka.jp","usui.fukuoka.jp","yamada.fukuoka.jp","yame.fukuoka.jp","yanagawa.fukuoka.jp","yukuhashi.fukuoka.jp","aizubange.fukushima.jp","aizumisato.fukushima.jp","aizuwakamatsu.fukushima.jp","asakawa.fukushima.jp","bandai.fukushima.jp","date.fukushima.jp","fukushima.fukushima.jp","furudono.fukushima.jp","futaba.fukushima.jp","hanawa.fukushima.jp","higashi.fukushima.jp","hirata.fukushima.jp","hirono.fukushima.jp","iitate.fukushima.jp","inawashiro.fukushima.jp","ishikawa.fukushima.jp","iwaki.fukushima.jp","izumizaki.fukushima.jp","kagamiishi.fukushima.jp","kaneyama.fukushima.jp","kawamata.fukushima.jp","kitakata.fukushima.jp","kitashiobara.fukushima.jp","koori.fukushima.jp","koriyama.fukushima.jp","kunimi.fukushima.jp","miharu.fukushima.jp","mishima.fukushima.jp","namie.fukushima.jp","nango.fukushima.jp","nishiaizu.fukushima.jp","nishigo.fukushima.jp","okuma.fukushima.jp","omotego.fukushima.jp","ono.fukushima.jp","otama.fukushima.jp","samegawa.fukushima.jp","shimogo.fukushima.jp","shirakawa.fukushima.jp","showa.fukushima.jp","soma.fukushima.jp","sukagawa.fukushima.jp","taishin.fukushima.jp","tamakawa.fukushima.jp","tanagura.fukushima.jp","tenei.fukushima.jp","yabuki.fukushima.jp","yamato.fukushima.jp","yamatsuri.fukushima.jp","yanaizu.fukushima.jp","yugawa.fukushima.jp","anpachi.gifu.jp","ena.gifu.jp","gifu.gifu.jp","ginan.gifu.jp","godo.gifu.jp","gujo.gifu.jp","hashima.gifu.jp","hichiso.gifu.jp","hida.gifu.jp","higashishirakawa.gifu.jp","ibigawa.gifu.jp","ikeda.gifu.jp","kakamigahara.gifu.jp","kani.gifu.jp","kasahara.gifu.jp","kasamatsu.gifu.jp","kawaue.gifu.jp","kitagata.gifu.jp","mino.gifu.jp","minokamo.gifu.jp","mitake.gifu.jp","mizunami.gifu.jp","motosu.gifu.jp","nakatsugawa.gifu.jp","ogaki.gifu.jp","sakahogi.gifu.jp","seki.gifu.jp","sekigahara.gifu.jp","shirakawa.gifu.jp","tajimi.gifu.jp","takayama.gifu.jp","tarui.gifu.jp","toki.gifu.jp","tomika.gifu.jp","wanouchi.gifu.jp","yamagata.gifu.jp","yaotsu.gifu.jp","yoro.gifu.jp","annaka.gunma.jp","chiyoda.gunma.jp","fujioka.gunma.jp","higashiagatsuma.gunma.jp","isesaki.gunma.jp","itakura.gunma.jp","kanna.gunma.jp","kanra.gunma.jp","katashina.gunma.jp","kawaba.gunma.jp","kiryu.gunma.jp","kusatsu.gunma.jp","maebashi.gunma.jp","meiwa.gunma.jp","midori.gunma.jp","minakami.gunma.jp","naganohara.gunma.jp","nakanojo.gunma.jp","nanmoku.gunma.jp","numata.gunma.jp","oizumi.gunma.jp","ora.gunma.jp","ota.gunma.jp","shibukawa.gunma.jp","shimonita.gunma.jp","shinto.gunma.jp","showa.gunma.jp","takasaki.gunma.jp","takayama.gunma.jp","tamamura.gunma.jp","tatebayashi.gunma.jp","tomioka.gunma.jp","tsukiyono.gunma.jp","tsumagoi.gunma.jp","ueno.gunma.jp","yoshioka.gunma.jp","asaminami.hiroshima.jp","daiwa.hiroshima.jp","etajima.hiroshima.jp","fuchu.hiroshima.jp","fukuyama.hiroshima.jp","hatsukaichi.hiroshima.jp","higashihiroshima.hiroshima.jp","hongo.hiroshima.jp","jinsekikogen.hiroshima.jp","kaita.hiroshima.jp","kui.hiroshima.jp","kumano.hiroshima.jp","kure.hiroshima.jp","mihara.hiroshima.jp","miyoshi.hiroshima.jp","naka.hiroshima.jp","onomichi.hiroshima.jp","osakikamijima.hiroshima.jp","otake.hiroshima.jp","saka.hiroshima.jp","sera.hiroshima.jp","seranishi.hiroshima.jp","shinichi.hiroshima.jp","shobara.hiroshima.jp","takehara.hiroshima.jp","abashiri.hokkaido.jp","abira.hokkaido.jp","aibetsu.hokkaido.jp","akabira.hokkaido.jp","akkeshi.hokkaido.jp","asahikawa.hokkaido.jp","ashibetsu.hokkaido.jp","ashoro.hokkaido.jp","assabu.hokkaido.jp","atsuma.hokkaido.jp","bibai.hokkaido.jp","biei.hokkaido.jp","bifuka.hokkaido.jp","bihoro.hokkaido.jp","biratori.hokkaido.jp","chippubetsu.hokkaido.jp","chitose.hokkaido.jp","date.hokkaido.jp","ebetsu.hokkaido.jp","embetsu.hokkaido.jp","eniwa.hokkaido.jp","erimo.hokkaido.jp","esan.hokkaido.jp","esashi.hokkaido.jp","fukagawa.hokkaido.jp","fukushima.hokkaido.jp","furano.hokkaido.jp","furubira.hokkaido.jp","haboro.hokkaido.jp","hakodate.hokkaido.jp","hamatonbetsu.hokkaido.jp","hidaka.hokkaido.jp","higashikagura.hokkaido.jp","higashikawa.hokkaido.jp","hiroo.hokkaido.jp","hokuryu.hokkaido.jp","hokuto.hokkaido.jp","honbetsu.hokkaido.jp","horokanai.hokkaido.jp","horonobe.hokkaido.jp","ikeda.hokkaido.jp","imakane.hokkaido.jp","ishikari.hokkaido.jp","iwamizawa.hokkaido.jp","iwanai.hokkaido.jp","kamifurano.hokkaido.jp","kamikawa.hokkaido.jp","kamishihoro.hokkaido.jp","kamisunagawa.hokkaido.jp","kamoenai.hokkaido.jp","kayabe.hokkaido.jp","kembuchi.hokkaido.jp","kikonai.hokkaido.jp","kimobetsu.hokkaido.jp","kitahiroshima.hokkaido.jp","kitami.hokkaido.jp","kiyosato.hokkaido.jp","koshimizu.hokkaido.jp","kunneppu.hokkaido.jp","kuriyama.hokkaido.jp","kuromatsunai.hokkaido.jp","kushiro.hokkaido.jp","kutchan.hokkaido.jp","kyowa.hokkaido.jp","mashike.hokkaido.jp","matsumae.hokkaido.jp","mikasa.hokkaido.jp","minamifurano.hokkaido.jp","mombetsu.hokkaido.jp","moseushi.hokkaido.jp","mukawa.hokkaido.jp","muroran.hokkaido.jp","naie.hokkaido.jp","nakagawa.hokkaido.jp","nakasatsunai.hokkaido.jp","nakatombetsu.hokkaido.jp","nanae.hokkaido.jp","nanporo.hokkaido.jp","nayoro.hokkaido.jp","nemuro.hokkaido.jp","niikappu.hokkaido.jp","niki.hokkaido.jp","nishiokoppe.hokkaido.jp","noboribetsu.hokkaido.jp","numata.hokkaido.jp","obihiro.hokkaido.jp","obira.hokkaido.jp","oketo.hokkaido.jp","okoppe.hokkaido.jp","otaru.hokkaido.jp","otobe.hokkaido.jp","otofuke.hokkaido.jp","otoineppu.hokkaido.jp","oumu.hokkaido.jp","ozora.hokkaido.jp","pippu.hokkaido.jp","rankoshi.hokkaido.jp","rebun.hokkaido.jp","rikubetsu.hokkaido.jp","rishiri.hokkaido.jp","rishirifuji.hokkaido.jp","saroma.hokkaido.jp","sarufutsu.hokkaido.jp","shakotan.hokkaido.jp","shari.hokkaido.jp","shibecha.hokkaido.jp","shibetsu.hokkaido.jp","shikabe.hokkaido.jp","shikaoi.hokkaido.jp","shimamaki.hokkaido.jp","shimizu.hokkaido.jp","shimokawa.hokkaido.jp","shinshinotsu.hokkaido.jp","shintoku.hokkaido.jp","shiranuka.hokkaido.jp","shiraoi.hokkaido.jp","shiriuchi.hokkaido.jp","sobetsu.hokkaido.jp","sunagawa.hokkaido.jp","taiki.hokkaido.jp","takasu.hokkaido.jp","takikawa.hokkaido.jp","takinoue.hokkaido.jp","teshikaga.hokkaido.jp","tobetsu.hokkaido.jp","tohma.hokkaido.jp","tomakomai.hokkaido.jp","tomari.hokkaido.jp","toya.hokkaido.jp","toyako.hokkaido.jp","toyotomi.hokkaido.jp","toyoura.hokkaido.jp","tsubetsu.hokkaido.jp","tsukigata.hokkaido.jp","urakawa.hokkaido.jp","urausu.hokkaido.jp","uryu.hokkaido.jp","utashinai.hokkaido.jp","wakkanai.hokkaido.jp","wassamu.hokkaido.jp","yakumo.hokkaido.jp","yoichi.hokkaido.jp","aioi.hyogo.jp","akashi.hyogo.jp","ako.hyogo.jp","amagasaki.hyogo.jp","aogaki.hyogo.jp","asago.hyogo.jp","ashiya.hyogo.jp","awaji.hyogo.jp","fukusaki.hyogo.jp","goshiki.hyogo.jp","harima.hyogo.jp","himeji.hyogo.jp","ichikawa.hyogo.jp","inagawa.hyogo.jp","itami.hyogo.jp","kakogawa.hyogo.jp","kamigori.hyogo.jp","kamikawa.hyogo.jp","kasai.hyogo.jp","kasuga.hyogo.jp","kawanishi.hyogo.jp","miki.hyogo.jp","minamiawaji.hyogo.jp","nishinomiya.hyogo.jp","nishiwaki.hyogo.jp","ono.hyogo.jp","sanda.hyogo.jp","sannan.hyogo.jp","sasayama.hyogo.jp","sayo.hyogo.jp","shingu.hyogo.jp","shinonsen.hyogo.jp","shiso.hyogo.jp","sumoto.hyogo.jp","taishi.hyogo.jp","taka.hyogo.jp","takarazuka.hyogo.jp","takasago.hyogo.jp","takino.hyogo.jp","tamba.hyogo.jp","tatsuno.hyogo.jp","toyooka.hyogo.jp","yabu.hyogo.jp","yashiro.hyogo.jp","yoka.hyogo.jp","yokawa.hyogo.jp","ami.ibaraki.jp","asahi.ibaraki.jp","bando.ibaraki.jp","chikusei.ibaraki.jp","daigo.ibaraki.jp","fujishiro.ibaraki.jp","hitachi.ibaraki.jp","hitachinaka.ibaraki.jp","hitachiomiya.ibaraki.jp","hitachiota.ibaraki.jp","ibaraki.ibaraki.jp","ina.ibaraki.jp","inashiki.ibaraki.jp","itako.ibaraki.jp","iwama.ibaraki.jp","joso.ibaraki.jp","kamisu.ibaraki.jp","kasama.ibaraki.jp","kashima.ibaraki.jp","kasumigaura.ibaraki.jp","koga.ibaraki.jp","miho.ibaraki.jp","mito.ibaraki.jp","moriya.ibaraki.jp","naka.ibaraki.jp","namegata.ibaraki.jp","oarai.ibaraki.jp","ogawa.ibaraki.jp","omitama.ibaraki.jp","ryugasaki.ibaraki.jp","sakai.ibaraki.jp","sakuragawa.ibaraki.jp","shimodate.ibaraki.jp","shimotsuma.ibaraki.jp","shirosato.ibaraki.jp","sowa.ibaraki.jp","suifu.ibaraki.jp","takahagi.ibaraki.jp","tamatsukuri.ibaraki.jp","tokai.ibaraki.jp","tomobe.ibaraki.jp","tone.ibaraki.jp","toride.ibaraki.jp","tsuchiura.ibaraki.jp","tsukuba.ibaraki.jp","uchihara.ibaraki.jp","ushiku.ibaraki.jp","yachiyo.ibaraki.jp","yamagata.ibaraki.jp","yawara.ibaraki.jp","yuki.ibaraki.jp","anamizu.ishikawa.jp","hakui.ishikawa.jp","hakusan.ishikawa.jp","kaga.ishikawa.jp","kahoku.ishikawa.jp","kanazawa.ishikawa.jp","kawakita.ishikawa.jp","komatsu.ishikawa.jp","nakanoto.ishikawa.jp","nanao.ishikawa.jp","nomi.ishikawa.jp","nonoichi.ishikawa.jp","noto.ishikawa.jp","shika.ishikawa.jp","suzu.ishikawa.jp","tsubata.ishikawa.jp","tsurugi.ishikawa.jp","uchinada.ishikawa.jp","wajima.ishikawa.jp","fudai.iwate.jp","fujisawa.iwate.jp","hanamaki.iwate.jp","hiraizumi.iwate.jp","hirono.iwate.jp","ichinohe.iwate.jp","ichinoseki.iwate.jp","iwaizumi.iwate.jp","iwate.iwate.jp","joboji.iwate.jp","kamaishi.iwate.jp","kanegasaki.iwate.jp","karumai.iwate.jp","kawai.iwate.jp","kitakami.iwate.jp","kuji.iwate.jp","kunohe.iwate.jp","kuzumaki.iwate.jp","miyako.iwate.jp","mizusawa.iwate.jp","morioka.iwate.jp","ninohe.iwate.jp","noda.iwate.jp","ofunato.iwate.jp","oshu.iwate.jp","otsuchi.iwate.jp","rikuzentakata.iwate.jp","shiwa.iwate.jp","shizukuishi.iwate.jp","sumita.iwate.jp","tanohata.iwate.jp","tono.iwate.jp","yahaba.iwate.jp","yamada.iwate.jp","ayagawa.kagawa.jp","higashikagawa.kagawa.jp","kanonji.kagawa.jp","kotohira.kagawa.jp","manno.kagawa.jp","marugame.kagawa.jp","mitoyo.kagawa.jp","naoshima.kagawa.jp","sanuki.kagawa.jp","tadotsu.kagawa.jp","takamatsu.kagawa.jp","tonosho.kagawa.jp","uchinomi.kagawa.jp","utazu.kagawa.jp","zentsuji.kagawa.jp","akune.kagoshima.jp","amami.kagoshima.jp","hioki.kagoshima.jp","isa.kagoshima.jp","isen.kagoshima.jp","izumi.kagoshima.jp","kagoshima.kagoshima.jp","kanoya.kagoshima.jp","kawanabe.kagoshima.jp","kinko.kagoshima.jp","kouyama.kagoshima.jp","makurazaki.kagoshima.jp","matsumoto.kagoshima.jp","minamitane.kagoshima.jp","nakatane.kagoshima.jp","nishinoomote.kagoshima.jp","satsumasendai.kagoshima.jp","soo.kagoshima.jp","tarumizu.kagoshima.jp","yusui.kagoshima.jp","aikawa.kanagawa.jp","atsugi.kanagawa.jp","ayase.kanagawa.jp","chigasaki.kanagawa.jp","ebina.kanagawa.jp","fujisawa.kanagawa.jp","hadano.kanagawa.jp","hakone.kanagawa.jp","hiratsuka.kanagawa.jp","isehara.kanagawa.jp","kaisei.kanagawa.jp","kamakura.kanagawa.jp","kiyokawa.kanagawa.jp","matsuda.kanagawa.jp","minamiashigara.kanagawa.jp","miura.kanagawa.jp","nakai.kanagawa.jp","ninomiya.kanagawa.jp","odawara.kanagawa.jp","oi.kanagawa.jp","oiso.kanagawa.jp","sagamihara.kanagawa.jp","samukawa.kanagawa.jp","tsukui.kanagawa.jp","yamakita.kanagawa.jp","yamato.kanagawa.jp","yokosuka.kanagawa.jp","yugawara.kanagawa.jp","zama.kanagawa.jp","zushi.kanagawa.jp","aki.kochi.jp","geisei.kochi.jp","hidaka.kochi.jp","higashitsuno.kochi.jp","ino.kochi.jp","kagami.kochi.jp","kami.kochi.jp","kitagawa.kochi.jp","kochi.kochi.jp","mihara.kochi.jp","motoyama.kochi.jp","muroto.kochi.jp","nahari.kochi.jp","nakamura.kochi.jp","nankoku.kochi.jp","nishitosa.kochi.jp","niyodogawa.kochi.jp","ochi.kochi.jp","okawa.kochi.jp","otoyo.kochi.jp","otsuki.kochi.jp","sakawa.kochi.jp","sukumo.kochi.jp","susaki.kochi.jp","tosa.kochi.jp","tosashimizu.kochi.jp","toyo.kochi.jp","tsuno.kochi.jp","umaji.kochi.jp","yasuda.kochi.jp","yusuhara.kochi.jp","amakusa.kumamoto.jp","arao.kumamoto.jp","aso.kumamoto.jp","choyo.kumamoto.jp","gyokuto.kumamoto.jp","kamiamakusa.kumamoto.jp","kikuchi.kumamoto.jp","kumamoto.kumamoto.jp","mashiki.kumamoto.jp","mifune.kumamoto.jp","minamata.kumamoto.jp","minamioguni.kumamoto.jp","nagasu.kumamoto.jp","nishihara.kumamoto.jp","oguni.kumamoto.jp","ozu.kumamoto.jp","sumoto.kumamoto.jp","takamori.kumamoto.jp","uki.kumamoto.jp","uto.kumamoto.jp","yamaga.kumamoto.jp","yamato.kumamoto.jp","yatsushiro.kumamoto.jp","ayabe.kyoto.jp","fukuchiyama.kyoto.jp","higashiyama.kyoto.jp","ide.kyoto.jp","ine.kyoto.jp","joyo.kyoto.jp","kameoka.kyoto.jp","kamo.kyoto.jp","kita.kyoto.jp","kizu.kyoto.jp","kumiyama.kyoto.jp","kyotamba.kyoto.jp","kyotanabe.kyoto.jp","kyotango.kyoto.jp","maizuru.kyoto.jp","minami.kyoto.jp","minamiyamashiro.kyoto.jp","miyazu.kyoto.jp","muko.kyoto.jp","nagaokakyo.kyoto.jp","nakagyo.kyoto.jp","nantan.kyoto.jp","oyamazaki.kyoto.jp","sakyo.kyoto.jp","seika.kyoto.jp","tanabe.kyoto.jp","uji.kyoto.jp","ujitawara.kyoto.jp","wazuka.kyoto.jp","yamashina.kyoto.jp","yawata.kyoto.jp","asahi.mie.jp","inabe.mie.jp","ise.mie.jp","kameyama.mie.jp","kawagoe.mie.jp","kiho.mie.jp","kisosaki.mie.jp","kiwa.mie.jp","komono.mie.jp","kumano.mie.jp","kuwana.mie.jp","matsusaka.mie.jp","meiwa.mie.jp","mihama.mie.jp","minamiise.mie.jp","misugi.mie.jp","miyama.mie.jp","nabari.mie.jp","shima.mie.jp","suzuka.mie.jp","tado.mie.jp","taiki.mie.jp","taki.mie.jp","tamaki.mie.jp","toba.mie.jp","tsu.mie.jp","udono.mie.jp","ureshino.mie.jp","watarai.mie.jp","yokkaichi.mie.jp","furukawa.miyagi.jp","higashimatsushima.miyagi.jp","ishinomaki.miyagi.jp","iwanuma.miyagi.jp","kakuda.miyagi.jp","kami.miyagi.jp","kawasaki.miyagi.jp","marumori.miyagi.jp","matsushima.miyagi.jp","minamisanriku.miyagi.jp","misato.miyagi.jp","murata.miyagi.jp","natori.miyagi.jp","ogawara.miyagi.jp","ohira.miyagi.jp","onagawa.miyagi.jp","osaki.miyagi.jp","rifu.miyagi.jp","semine.miyagi.jp","shibata.miyagi.jp","shichikashuku.miyagi.jp","shikama.miyagi.jp","shiogama.miyagi.jp","shiroishi.miyagi.jp","tagajo.miyagi.jp","taiwa.miyagi.jp","tome.miyagi.jp","tomiya.miyagi.jp","wakuya.miyagi.jp","watari.miyagi.jp","yamamoto.miyagi.jp","zao.miyagi.jp","aya.miyazaki.jp","ebino.miyazaki.jp","gokase.miyazaki.jp","hyuga.miyazaki.jp","kadogawa.miyazaki.jp","kawaminami.miyazaki.jp","kijo.miyazaki.jp","kitagawa.miyazaki.jp","kitakata.miyazaki.jp","kitaura.miyazaki.jp","kobayashi.miyazaki.jp","kunitomi.miyazaki.jp","kushima.miyazaki.jp","mimata.miyazaki.jp","miyakonojo.miyazaki.jp","miyazaki.miyazaki.jp","morotsuka.miyazaki.jp","nichinan.miyazaki.jp","nishimera.miyazaki.jp","nobeoka.miyazaki.jp","saito.miyazaki.jp","shiiba.miyazaki.jp","shintomi.miyazaki.jp","takaharu.miyazaki.jp","takanabe.miyazaki.jp","takazaki.miyazaki.jp","tsuno.miyazaki.jp","achi.nagano.jp","agematsu.nagano.jp","anan.nagano.jp","aoki.nagano.jp","asahi.nagano.jp","azumino.nagano.jp","chikuhoku.nagano.jp","chikuma.nagano.jp","chino.nagano.jp","fujimi.nagano.jp","hakuba.nagano.jp","hara.nagano.jp","hiraya.nagano.jp","iida.nagano.jp","iijima.nagano.jp","iiyama.nagano.jp","iizuna.nagano.jp","ikeda.nagano.jp","ikusaka.nagano.jp","ina.nagano.jp","karuizawa.nagano.jp","kawakami.nagano.jp","kiso.nagano.jp","kisofukushima.nagano.jp","kitaaiki.nagano.jp","komagane.nagano.jp","komoro.nagano.jp","matsukawa.nagano.jp","matsumoto.nagano.jp","miasa.nagano.jp","minamiaiki.nagano.jp","minamimaki.nagano.jp","minamiminowa.nagano.jp","minowa.nagano.jp","miyada.nagano.jp","miyota.nagano.jp","mochizuki.nagano.jp","nagano.nagano.jp","nagawa.nagano.jp","nagiso.nagano.jp","nakagawa.nagano.jp","nakano.nagano.jp","nozawaonsen.nagano.jp","obuse.nagano.jp","ogawa.nagano.jp","okaya.nagano.jp","omachi.nagano.jp","omi.nagano.jp","ookuwa.nagano.jp","ooshika.nagano.jp","otaki.nagano.jp","otari.nagano.jp","sakae.nagano.jp","sakaki.nagano.jp","saku.nagano.jp","sakuho.nagano.jp","shimosuwa.nagano.jp","shinanomachi.nagano.jp","shiojiri.nagano.jp","suwa.nagano.jp","suzaka.nagano.jp","takagi.nagano.jp","takamori.nagano.jp","takayama.nagano.jp","tateshina.nagano.jp","tatsuno.nagano.jp","togakushi.nagano.jp","togura.nagano.jp","tomi.nagano.jp","ueda.nagano.jp","wada.nagano.jp","yamagata.nagano.jp","yamanouchi.nagano.jp","yasaka.nagano.jp","yasuoka.nagano.jp","chijiwa.nagasaki.jp","futsu.nagasaki.jp","goto.nagasaki.jp","hasami.nagasaki.jp","hirado.nagasaki.jp","iki.nagasaki.jp","isahaya.nagasaki.jp","kawatana.nagasaki.jp","kuchinotsu.nagasaki.jp","matsuura.nagasaki.jp","nagasaki.nagasaki.jp","obama.nagasaki.jp","omura.nagasaki.jp","oseto.nagasaki.jp","saikai.nagasaki.jp","sasebo.nagasaki.jp","seihi.nagasaki.jp","shimabara.nagasaki.jp","shinkamigoto.nagasaki.jp","togitsu.nagasaki.jp","tsushima.nagasaki.jp","unzen.nagasaki.jp","ando.nara.jp","gose.nara.jp","heguri.nara.jp","higashiyoshino.nara.jp","ikaruga.nara.jp","ikoma.nara.jp","kamikitayama.nara.jp","kanmaki.nara.jp","kashiba.nara.jp","kashihara.nara.jp","katsuragi.nara.jp","kawai.nara.jp","kawakami.nara.jp","kawanishi.nara.jp","koryo.nara.jp","kurotaki.nara.jp","mitsue.nara.jp","miyake.nara.jp","nara.nara.jp","nosegawa.nara.jp","oji.nara.jp","ouda.nara.jp","oyodo.nara.jp","sakurai.nara.jp","sango.nara.jp","shimoichi.nara.jp","shimokitayama.nara.jp","shinjo.nara.jp","soni.nara.jp","takatori.nara.jp","tawaramoto.nara.jp","tenkawa.nara.jp","tenri.nara.jp","uda.nara.jp","yamatokoriyama.nara.jp","yamatotakada.nara.jp","yamazoe.nara.jp","yoshino.nara.jp","aga.niigata.jp","agano.niigata.jp","gosen.niigata.jp","itoigawa.niigata.jp","izumozaki.niigata.jp","joetsu.niigata.jp","kamo.niigata.jp","kariwa.niigata.jp","kashiwazaki.niigata.jp","minamiuonuma.niigata.jp","mitsuke.niigata.jp","muika.niigata.jp","murakami.niigata.jp","myoko.niigata.jp","nagaoka.niigata.jp","niigata.niigata.jp","ojiya.niigata.jp","omi.niigata.jp","sado.niigata.jp","sanjo.niigata.jp","seiro.niigata.jp","seirou.niigata.jp","sekikawa.niigata.jp","shibata.niigata.jp","tagami.niigata.jp","tainai.niigata.jp","tochio.niigata.jp","tokamachi.niigata.jp","tsubame.niigata.jp","tsunan.niigata.jp","uonuma.niigata.jp","yahiko.niigata.jp","yoita.niigata.jp","yuzawa.niigata.jp","beppu.oita.jp","bungoono.oita.jp","bungotakada.oita.jp","hasama.oita.jp","hiji.oita.jp","himeshima.oita.jp","hita.oita.jp","kamitsue.oita.jp","kokonoe.oita.jp","kuju.oita.jp","kunisaki.oita.jp","kusu.oita.jp","oita.oita.jp","saiki.oita.jp","taketa.oita.jp","tsukumi.oita.jp","usa.oita.jp","usuki.oita.jp","yufu.oita.jp","akaiwa.okayama.jp","asakuchi.okayama.jp","bizen.okayama.jp","hayashima.okayama.jp","ibara.okayama.jp","kagamino.okayama.jp","kasaoka.okayama.jp","kibichuo.okayama.jp","kumenan.okayama.jp","kurashiki.okayama.jp","maniwa.okayama.jp","misaki.okayama.jp","nagi.okayama.jp","niimi.okayama.jp","nishiawakura.okayama.jp","okayama.okayama.jp","satosho.okayama.jp","setouchi.okayama.jp","shinjo.okayama.jp","shoo.okayama.jp","soja.okayama.jp","takahashi.okayama.jp","tamano.okayama.jp","tsuyama.okayama.jp","wake.okayama.jp","yakage.okayama.jp","aguni.okinawa.jp","ginowan.okinawa.jp","ginoza.okinawa.jp","gushikami.okinawa.jp","haebaru.okinawa.jp","higashi.okinawa.jp","hirara.okinawa.jp","iheya.okinawa.jp","ishigaki.okinawa.jp","ishikawa.okinawa.jp","itoman.okinawa.jp","izena.okinawa.jp","kadena.okinawa.jp","kin.okinawa.jp","kitadaito.okinawa.jp","kitanakagusuku.okinawa.jp","kumejima.okinawa.jp","kunigami.okinawa.jp","minamidaito.okinawa.jp","motobu.okinawa.jp","nago.okinawa.jp","naha.okinawa.jp","nakagusuku.okinawa.jp","nakijin.okinawa.jp","nanjo.okinawa.jp","nishihara.okinawa.jp","ogimi.okinawa.jp","okinawa.okinawa.jp","onna.okinawa.jp","shimoji.okinawa.jp","taketomi.okinawa.jp","tarama.okinawa.jp","tokashiki.okinawa.jp","tomigusuku.okinawa.jp","tonaki.okinawa.jp","urasoe.okinawa.jp","uruma.okinawa.jp","yaese.okinawa.jp","yomitan.okinawa.jp","yonabaru.okinawa.jp","yonaguni.okinawa.jp","zamami.okinawa.jp","abeno.osaka.jp","chihayaakasaka.osaka.jp","chuo.osaka.jp","daito.osaka.jp","fujiidera.osaka.jp","habikino.osaka.jp","hannan.osaka.jp","higashiosaka.osaka.jp","higashisumiyoshi.osaka.jp","higashiyodogawa.osaka.jp","hirakata.osaka.jp","ibaraki.osaka.jp","ikeda.osaka.jp","izumi.osaka.jp","izumiotsu.osaka.jp","izumisano.osaka.jp","kadoma.osaka.jp","kaizuka.osaka.jp","kanan.osaka.jp","kashiwara.osaka.jp","katano.osaka.jp","kawachinagano.osaka.jp","kishiwada.osaka.jp","kita.osaka.jp","kumatori.osaka.jp","matsubara.osaka.jp","minato.osaka.jp","minoh.osaka.jp","misaki.osaka.jp","moriguchi.osaka.jp","neyagawa.osaka.jp","nishi.osaka.jp","nose.osaka.jp","osakasayama.osaka.jp","sakai.osaka.jp","sayama.osaka.jp","sennan.osaka.jp","settsu.osaka.jp","shijonawate.osaka.jp","shimamoto.osaka.jp","suita.osaka.jp","tadaoka.osaka.jp","taishi.osaka.jp","tajiri.osaka.jp","takaishi.osaka.jp","takatsuki.osaka.jp","tondabayashi.osaka.jp","toyonaka.osaka.jp","toyono.osaka.jp","yao.osaka.jp","ariake.saga.jp","arita.saga.jp","fukudomi.saga.jp","genkai.saga.jp","hamatama.saga.jp","hizen.saga.jp","imari.saga.jp","kamimine.saga.jp","kanzaki.saga.jp","karatsu.saga.jp","kashima.saga.jp","kitagata.saga.jp","kitahata.saga.jp","kiyama.saga.jp","kouhoku.saga.jp","kyuragi.saga.jp","nishiarita.saga.jp","ogi.saga.jp","omachi.saga.jp","ouchi.saga.jp","saga.saga.jp","shiroishi.saga.jp","taku.saga.jp","tara.saga.jp","tosu.saga.jp","yoshinogari.saga.jp","arakawa.saitama.jp","asaka.saitama.jp","chichibu.saitama.jp","fujimi.saitama.jp","fujimino.saitama.jp","fukaya.saitama.jp","hanno.saitama.jp","hanyu.saitama.jp","hasuda.saitama.jp","hatogaya.saitama.jp","hatoyama.saitama.jp","hidaka.saitama.jp","higashichichibu.saitama.jp","higashimatsuyama.saitama.jp","honjo.saitama.jp","ina.saitama.jp","iruma.saitama.jp","iwatsuki.saitama.jp","kamiizumi.saitama.jp","kamikawa.saitama.jp","kamisato.saitama.jp","kasukabe.saitama.jp","kawagoe.saitama.jp","kawaguchi.saitama.jp","kawajima.saitama.jp","kazo.saitama.jp","kitamoto.saitama.jp","koshigaya.saitama.jp","kounosu.saitama.jp","kuki.saitama.jp","kumagaya.saitama.jp","matsubushi.saitama.jp","minano.saitama.jp","misato.saitama.jp","miyashiro.saitama.jp","miyoshi.saitama.jp","moroyama.saitama.jp","nagatoro.saitama.jp","namegawa.saitama.jp","niiza.saitama.jp","ogano.saitama.jp","ogawa.saitama.jp","ogose.saitama.jp","okegawa.saitama.jp","omiya.saitama.jp","otaki.saitama.jp","ranzan.saitama.jp","ryokami.saitama.jp","saitama.saitama.jp","sakado.saitama.jp","satte.saitama.jp","sayama.saitama.jp","shiki.saitama.jp","shiraoka.saitama.jp","soka.saitama.jp","sugito.saitama.jp","toda.saitama.jp","tokigawa.saitama.jp","tokorozawa.saitama.jp","tsurugashima.saitama.jp","urawa.saitama.jp","warabi.saitama.jp","yashio.saitama.jp","yokoze.saitama.jp","yono.saitama.jp","yorii.saitama.jp","yoshida.saitama.jp","yoshikawa.saitama.jp","yoshimi.saitama.jp","aisho.shiga.jp","gamo.shiga.jp","higashiomi.shiga.jp","hikone.shiga.jp","koka.shiga.jp","konan.shiga.jp","kosei.shiga.jp","koto.shiga.jp","kusatsu.shiga.jp","maibara.shiga.jp","moriyama.shiga.jp","nagahama.shiga.jp","nishiazai.shiga.jp","notogawa.shiga.jp","omihachiman.shiga.jp","otsu.shiga.jp","ritto.shiga.jp","ryuoh.shiga.jp","takashima.shiga.jp","takatsuki.shiga.jp","torahime.shiga.jp","toyosato.shiga.jp","yasu.shiga.jp","akagi.shimane.jp","ama.shimane.jp","gotsu.shimane.jp","hamada.shimane.jp","higashiizumo.shimane.jp","hikawa.shimane.jp","hikimi.shimane.jp","izumo.shimane.jp","kakinoki.shimane.jp","masuda.shimane.jp","matsue.shimane.jp","misato.shimane.jp","nishinoshima.shimane.jp","ohda.shimane.jp","okinoshima.shimane.jp","okuizumo.shimane.jp","shimane.shimane.jp","tamayu.shimane.jp","tsuwano.shimane.jp","unnan.shimane.jp","yakumo.shimane.jp","yasugi.shimane.jp","yatsuka.shimane.jp","arai.shizuoka.jp","atami.shizuoka.jp","fuji.shizuoka.jp","fujieda.shizuoka.jp","fujikawa.shizuoka.jp","fujinomiya.shizuoka.jp","fukuroi.shizuoka.jp","gotemba.shizuoka.jp","haibara.shizuoka.jp","hamamatsu.shizuoka.jp","higashiizu.shizuoka.jp","ito.shizuoka.jp","iwata.shizuoka.jp","izu.shizuoka.jp","izunokuni.shizuoka.jp","kakegawa.shizuoka.jp","kannami.shizuoka.jp","kawanehon.shizuoka.jp","kawazu.shizuoka.jp","kikugawa.shizuoka.jp","kosai.shizuoka.jp","makinohara.shizuoka.jp","matsuzaki.shizuoka.jp","minamiizu.shizuoka.jp","mishima.shizuoka.jp","morimachi.shizuoka.jp","nishiizu.shizuoka.jp","numazu.shizuoka.jp","omaezaki.shizuoka.jp","shimada.shizuoka.jp","shimizu.shizuoka.jp","shimoda.shizuoka.jp","shizuoka.shizuoka.jp","susono.shizuoka.jp","yaizu.shizuoka.jp","yoshida.shizuoka.jp","ashikaga.tochigi.jp","bato.tochigi.jp","haga.tochigi.jp","ichikai.tochigi.jp","iwafune.tochigi.jp","kaminokawa.tochigi.jp","kanuma.tochigi.jp","karasuyama.tochigi.jp","kuroiso.tochigi.jp","mashiko.tochigi.jp","mibu.tochigi.jp","moka.tochigi.jp","motegi.tochigi.jp","nasu.tochigi.jp","nasushiobara.tochigi.jp","nikko.tochigi.jp","nishikata.tochigi.jp","nogi.tochigi.jp","ohira.tochigi.jp","ohtawara.tochigi.jp","oyama.tochigi.jp","sakura.tochigi.jp","sano.tochigi.jp","shimotsuke.tochigi.jp","shioya.tochigi.jp","takanezawa.tochigi.jp","tochigi.tochigi.jp","tsuga.tochigi.jp","ujiie.tochigi.jp","utsunomiya.tochigi.jp","yaita.tochigi.jp","aizumi.tokushima.jp","anan.tokushima.jp","ichiba.tokushima.jp","itano.tokushima.jp","kainan.tokushima.jp","komatsushima.tokushima.jp","matsushige.tokushima.jp","mima.tokushima.jp","minami.tokushima.jp","miyoshi.tokushima.jp","mugi.tokushima.jp","nakagawa.tokushima.jp","naruto.tokushima.jp","sanagochi.tokushima.jp","shishikui.tokushima.jp","tokushima.tokushima.jp","wajiki.tokushima.jp","adachi.tokyo.jp","akiruno.tokyo.jp","akishima.tokyo.jp","aogashima.tokyo.jp","arakawa.tokyo.jp","bunkyo.tokyo.jp","chiyoda.tokyo.jp","chofu.tokyo.jp","chuo.tokyo.jp","edogawa.tokyo.jp","fuchu.tokyo.jp","fussa.tokyo.jp","hachijo.tokyo.jp","hachioji.tokyo.jp","hamura.tokyo.jp","higashikurume.tokyo.jp","higashimurayama.tokyo.jp","higashiyamato.tokyo.jp","hino.tokyo.jp","hinode.tokyo.jp","hinohara.tokyo.jp","inagi.tokyo.jp","itabashi.tokyo.jp","katsushika.tokyo.jp","kita.tokyo.jp","kiyose.tokyo.jp","kodaira.tokyo.jp","koganei.tokyo.jp","kokubunji.tokyo.jp","komae.tokyo.jp","koto.tokyo.jp","kouzushima.tokyo.jp","kunitachi.tokyo.jp","machida.tokyo.jp","meguro.tokyo.jp","minato.tokyo.jp","mitaka.tokyo.jp","mizuho.tokyo.jp","musashimurayama.tokyo.jp","musashino.tokyo.jp","nakano.tokyo.jp","nerima.tokyo.jp","ogasawara.tokyo.jp","okutama.tokyo.jp","ome.tokyo.jp","oshima.tokyo.jp","ota.tokyo.jp","setagaya.tokyo.jp","shibuya.tokyo.jp","shinagawa.tokyo.jp","shinjuku.tokyo.jp","suginami.tokyo.jp","sumida.tokyo.jp","tachikawa.tokyo.jp","taito.tokyo.jp","tama.tokyo.jp","toshima.tokyo.jp","chizu.tottori.jp","hino.tottori.jp","kawahara.tottori.jp","koge.tottori.jp","kotoura.tottori.jp","misasa.tottori.jp","nanbu.tottori.jp","nichinan.tottori.jp","sakaiminato.tottori.jp","tottori.tottori.jp","wakasa.tottori.jp","yazu.tottori.jp","yonago.tottori.jp","asahi.toyama.jp","fuchu.toyama.jp","fukumitsu.toyama.jp","funahashi.toyama.jp","himi.toyama.jp","imizu.toyama.jp","inami.toyama.jp","johana.toyama.jp","kamiichi.toyama.jp","kurobe.toyama.jp","nakaniikawa.toyama.jp","namerikawa.toyama.jp","nanto.toyama.jp","nyuzen.toyama.jp","oyabe.toyama.jp","taira.toyama.jp","takaoka.toyama.jp","tateyama.toyama.jp","toga.toyama.jp","tonami.toyama.jp","toyama.toyama.jp","unazuki.toyama.jp","uozu.toyama.jp","yamada.toyama.jp","arida.wakayama.jp","aridagawa.wakayama.jp","gobo.wakayama.jp","hashimoto.wakayama.jp","hidaka.wakayama.jp","hirogawa.wakayama.jp","inami.wakayama.jp","iwade.wakayama.jp","kainan.wakayama.jp","kamitonda.wakayama.jp","katsuragi.wakayama.jp","kimino.wakayama.jp","kinokawa.wakayama.jp","kitayama.wakayama.jp","koya.wakayama.jp","koza.wakayama.jp","kozagawa.wakayama.jp","kudoyama.wakayama.jp","kushimoto.wakayama.jp","mihama.wakayama.jp","misato.wakayama.jp","nachikatsuura.wakayama.jp","shingu.wakayama.jp","shirahama.wakayama.jp","taiji.wakayama.jp","tanabe.wakayama.jp","wakayama.wakayama.jp","yuasa.wakayama.jp","yura.wakayama.jp","asahi.yamagata.jp","funagata.yamagata.jp","higashine.yamagata.jp","iide.yamagata.jp","kahoku.yamagata.jp","kaminoyama.yamagata.jp","kaneyama.yamagata.jp","kawanishi.yamagata.jp","mamurogawa.yamagata.jp","mikawa.yamagata.jp","murayama.yamagata.jp","nagai.yamagata.jp","nakayama.yamagata.jp","nanyo.yamagata.jp","nishikawa.yamagata.jp","obanazawa.yamagata.jp","oe.yamagata.jp","oguni.yamagata.jp","ohkura.yamagata.jp","oishida.yamagata.jp","sagae.yamagata.jp","sakata.yamagata.jp","sakegawa.yamagata.jp","shinjo.yamagata.jp","shirataka.yamagata.jp","shonai.yamagata.jp","takahata.yamagata.jp","tendo.yamagata.jp","tozawa.yamagata.jp","tsuruoka.yamagata.jp","yamagata.yamagata.jp","yamanobe.yamagata.jp","yonezawa.yamagata.jp","yuza.yamagata.jp","abu.yamaguchi.jp","hagi.yamaguchi.jp","hikari.yamaguchi.jp","hofu.yamaguchi.jp","iwakuni.yamaguchi.jp","kudamatsu.yamaguchi.jp","mitou.yamaguchi.jp","nagato.yamaguchi.jp","oshima.yamaguchi.jp","shimonoseki.yamaguchi.jp","shunan.yamaguchi.jp","tabuse.yamaguchi.jp","tokuyama.yamaguchi.jp","toyota.yamaguchi.jp","ube.yamaguchi.jp","yuu.yamaguchi.jp","chuo.yamanashi.jp","doshi.yamanashi.jp","fuefuki.yamanashi.jp","fujikawa.yamanashi.jp","fujikawaguchiko.yamanashi.jp","fujiyoshida.yamanashi.jp","hayakawa.yamanashi.jp","hokuto.yamanashi.jp","ichikawamisato.yamanashi.jp","kai.yamanashi.jp","kofu.yamanashi.jp","koshu.yamanashi.jp","kosuge.yamanashi.jp","minami-alps.yamanashi.jp","minobu.yamanashi.jp","nakamichi.yamanashi.jp","nanbu.yamanashi.jp","narusawa.yamanashi.jp","nirasaki.yamanashi.jp","nishikatsura.yamanashi.jp","oshino.yamanashi.jp","otsuki.yamanashi.jp","showa.yamanashi.jp","tabayama.yamanashi.jp","tsuru.yamanashi.jp","uenohara.yamanashi.jp","yamanakako.yamanashi.jp","yamanashi.yamanashi.jp","ke","ac.ke","co.ke","go.ke","info.ke","me.ke","mobi.ke","ne.ke","or.ke","sc.ke","kg","org.kg","net.kg","com.kg","edu.kg","gov.kg","mil.kg","*.kh","ki","edu.ki","biz.ki","net.ki","org.ki","gov.ki","info.ki","com.ki","km","org.km","nom.km","gov.km","prd.km","tm.km","edu.km","mil.km","ass.km","com.km","coop.km","asso.km","presse.km","medecin.km","notaires.km","pharmaciens.km","veterinaire.km","gouv.km","kn","net.kn","org.kn","edu.kn","gov.kn","kp","com.kp","edu.kp","gov.kp","org.kp","rep.kp","tra.kp","kr","ac.kr","co.kr","es.kr","go.kr","hs.kr","kg.kr","mil.kr","ms.kr","ne.kr","or.kr","pe.kr","re.kr","sc.kr","busan.kr","chungbuk.kr","chungnam.kr","daegu.kr","daejeon.kr","gangwon.kr","gwangju.kr","gyeongbuk.kr","gyeonggi.kr","gyeongnam.kr","incheon.kr","jeju.kr","jeonbuk.kr","jeonnam.kr","seoul.kr","ulsan.kr","kw","com.kw","edu.kw","emb.kw","gov.kw","ind.kw","net.kw","org.kw","ky","edu.ky","gov.ky","com.ky","org.ky","net.ky","kz","org.kz","edu.kz","net.kz","gov.kz","mil.kz","com.kz","la","int.la","net.la","info.la","edu.la","gov.la","per.la","com.la","org.la","lb","com.lb","edu.lb","gov.lb","net.lb","org.lb","lc","com.lc","net.lc","co.lc","org.lc","edu.lc","gov.lc","li","lk","gov.lk","sch.lk","net.lk","int.lk","com.lk","org.lk","edu.lk","ngo.lk","soc.lk","web.lk","ltd.lk","assn.lk","grp.lk","hotel.lk","ac.lk","lr","com.lr","edu.lr","gov.lr","org.lr","net.lr","ls","co.ls","org.ls","lt","gov.lt","lu","lv","com.lv","edu.lv","gov.lv","org.lv","mil.lv","id.lv","net.lv","asn.lv","conf.lv","ly","com.ly","net.ly","gov.ly","plc.ly","edu.ly","sch.ly","med.ly","org.ly","id.ly","ma","co.ma","net.ma","gov.ma","org.ma","ac.ma","press.ma","mc","tm.mc","asso.mc","md","me","co.me","net.me","org.me","edu.me","ac.me","gov.me","its.me","priv.me","mg","org.mg","nom.mg","gov.mg","prd.mg","tm.mg","edu.mg","mil.mg","com.mg","co.mg","mh","mil","mk","com.mk","org.mk","net.mk","edu.mk","gov.mk","inf.mk","name.mk","ml","com.ml","edu.ml","gouv.ml","gov.ml","net.ml","org.ml","presse.ml","*.mm","mn","gov.mn","edu.mn","org.mn","mo","com.mo","net.mo","org.mo","edu.mo","gov.mo","mobi","mp","mq","mr","gov.mr","ms","com.ms","edu.ms","gov.ms","net.ms","org.ms","mt","com.mt","edu.mt","net.mt","org.mt","mu","com.mu","net.mu","org.mu","gov.mu","ac.mu","co.mu","or.mu","museum","academy.museum","agriculture.museum","air.museum","airguard.museum","alabama.museum","alaska.museum","amber.museum","ambulance.museum","american.museum","americana.museum","americanantiques.museum","americanart.museum","amsterdam.museum","and.museum","annefrank.museum","anthro.museum","anthropology.museum","antiques.museum","aquarium.museum","arboretum.museum","archaeological.museum","archaeology.museum","architecture.museum","art.museum","artanddesign.museum","artcenter.museum","artdeco.museum","arteducation.museum","artgallery.museum","arts.museum","artsandcrafts.museum","asmatart.museum","assassination.museum","assisi.museum","association.museum","astronomy.museum","atlanta.museum","austin.museum","australia.museum","automotive.museum","aviation.museum","axis.museum","badajoz.museum","baghdad.museum","bahn.museum","bale.museum","baltimore.museum","barcelona.museum","baseball.museum","basel.museum","baths.museum","bauern.museum","beauxarts.museum","beeldengeluid.museum","bellevue.museum","bergbau.museum","berkeley.museum","berlin.museum","bern.museum","bible.museum","bilbao.museum","bill.museum","birdart.museum","birthplace.museum","bonn.museum","boston.museum","botanical.museum","botanicalgarden.museum","botanicgarden.museum","botany.museum","brandywinevalley.museum","brasil.museum","bristol.museum","british.museum","britishcolumbia.museum","broadcast.museum","brunel.museum","brussel.museum","brussels.museum","bruxelles.museum","building.museum","burghof.museum","bus.museum","bushey.museum","cadaques.museum","california.museum","cambridge.museum","can.museum","canada.museum","capebreton.museum","carrier.museum","cartoonart.museum","casadelamoneda.museum","castle.museum","castres.museum","celtic.museum","center.museum","chattanooga.museum","cheltenham.museum","chesapeakebay.museum","chicago.museum","children.museum","childrens.museum","childrensgarden.museum","chiropractic.museum","chocolate.museum","christiansburg.museum","cincinnati.museum","cinema.museum","circus.museum","civilisation.museum","civilization.museum","civilwar.museum","clinton.museum","clock.museum","coal.museum","coastaldefence.museum","cody.museum","coldwar.museum","collection.museum","colonialwilliamsburg.museum","coloradoplateau.museum","columbia.museum","columbus.museum","communication.museum","communications.museum","community.museum","computer.museum","computerhistory.museum","comunicações.museum","contemporary.museum","contemporaryart.museum","convent.museum","copenhagen.museum","corporation.museum","correios-e-telecomunicações.museum","corvette.museum","costume.museum","countryestate.museum","county.museum","crafts.museum","cranbrook.museum","creation.museum","cultural.museum","culturalcenter.museum","culture.museum","cyber.museum","cymru.museum","dali.museum","dallas.museum","database.museum","ddr.museum","decorativearts.museum","delaware.museum","delmenhorst.museum","denmark.museum","depot.museum","design.museum","detroit.museum","dinosaur.museum","discovery.museum","dolls.museum","donostia.museum","durham.museum","eastafrica.museum","eastcoast.museum","education.museum","educational.museum","egyptian.museum","eisenbahn.museum","elburg.museum","elvendrell.museum","embroidery.museum","encyclopedic.museum","england.museum","entomology.museum","environment.museum","environmentalconservation.museum","epilepsy.museum","essex.museum","estate.museum","ethnology.museum","exeter.museum","exhibition.museum","family.museum","farm.museum","farmequipment.museum","farmers.museum","farmstead.museum","field.museum","figueres.museum","filatelia.museum","film.museum","fineart.museum","finearts.museum","finland.museum","flanders.museum","florida.museum","force.museum","fortmissoula.museum","fortworth.museum","foundation.museum","francaise.museum","frankfurt.museum","franziskaner.museum","freemasonry.museum","freiburg.museum","fribourg.museum","frog.museum","fundacio.museum","furniture.museum","gallery.museum","garden.museum","gateway.museum","geelvinck.museum","gemological.museum","geology.museum","georgia.museum","giessen.museum","glas.museum","glass.museum","gorge.museum","grandrapids.museum","graz.museum","guernsey.museum","halloffame.museum","hamburg.museum","handson.museum","harvestcelebration.museum","hawaii.museum","health.museum","heimatunduhren.museum","hellas.museum","helsinki.museum","hembygdsforbund.museum","heritage.museum","histoire.museum","historical.museum","historicalsociety.museum","historichouses.museum","historisch.museum","historisches.museum","history.museum","historyofscience.museum","horology.museum","house.museum","humanities.museum","illustration.museum","imageandsound.museum","indian.museum","indiana.museum","indianapolis.museum","indianmarket.museum","intelligence.museum","interactive.museum","iraq.museum","iron.museum","isleofman.museum","jamison.museum","jefferson.museum","jerusalem.museum","jewelry.museum","jewish.museum","jewishart.museum","jfk.museum","journalism.museum","judaica.museum","judygarland.museum","juedisches.museum","juif.museum","karate.museum","karikatur.museum","kids.museum","koebenhavn.museum","koeln.museum","kunst.museum","kunstsammlung.museum","kunstunddesign.museum","labor.museum","labour.museum","lajolla.museum","lancashire.museum","landes.museum","lans.museum","läns.museum","larsson.museum","lewismiller.museum","lincoln.museum","linz.museum","living.museum","livinghistory.museum","localhistory.museum","london.museum","losangeles.museum","louvre.museum","loyalist.museum","lucerne.museum","luxembourg.museum","luzern.museum","mad.museum","madrid.museum","mallorca.museum","manchester.museum","mansion.museum","mansions.museum","manx.museum","marburg.museum","maritime.museum","maritimo.museum","maryland.museum","marylhurst.museum","media.museum","medical.museum","medizinhistorisches.museum","meeres.museum","memorial.museum","mesaverde.museum","michigan.museum","midatlantic.museum","military.museum","mill.museum","miners.museum","mining.museum","minnesota.museum","missile.museum","missoula.museum","modern.museum","moma.museum","money.museum","monmouth.museum","monticello.museum","montreal.museum","moscow.museum","motorcycle.museum","muenchen.museum","muenster.museum","mulhouse.museum","muncie.museum","museet.museum","museumcenter.museum","museumvereniging.museum","music.museum","national.museum","nationalfirearms.museum","nationalheritage.museum","nativeamerican.museum","naturalhistory.museum","naturalhistorymuseum.museum","naturalsciences.museum","nature.museum","naturhistorisches.museum","natuurwetenschappen.museum","naumburg.museum","naval.museum","nebraska.museum","neues.museum","newhampshire.museum","newjersey.museum","newmexico.museum","newport.museum","newspaper.museum","newyork.museum","niepce.museum","norfolk.museum","north.museum","nrw.museum","nuernberg.museum","nuremberg.museum","nyc.museum","nyny.museum","oceanographic.museum","oceanographique.museum","omaha.museum","online.museum","ontario.museum","openair.museum","oregon.museum","oregontrail.museum","otago.museum","oxford.museum","pacific.museum","paderborn.museum","palace.museum","paleo.museum","palmsprings.museum","panama.museum","paris.museum","pasadena.museum","pharmacy.museum","philadelphia.museum","philadelphiaarea.museum","philately.museum","phoenix.museum","photography.museum","pilots.museum","pittsburgh.museum","planetarium.museum","plantation.museum","plants.museum","plaza.museum","portal.museum","portland.museum","portlligat.museum","posts-and-telecommunications.museum","preservation.museum","presidio.museum","press.museum","project.museum","public.museum","pubol.museum","quebec.museum","railroad.museum","railway.museum","research.museum","resistance.museum","riodejaneiro.museum","rochester.museum","rockart.museum","roma.museum","russia.museum","saintlouis.museum","salem.museum","salvadordali.museum","salzburg.museum","sandiego.museum","sanfrancisco.museum","santabarbara.museum","santacruz.museum","santafe.museum","saskatchewan.museum","satx.museum","savannahga.museum","schlesisches.museum","schoenbrunn.museum","schokoladen.museum","school.museum","schweiz.museum","science.museum","scienceandhistory.museum","scienceandindustry.museum","sciencecenter.museum","sciencecenters.museum","science-fiction.museum","sciencehistory.museum","sciences.museum","sciencesnaturelles.museum","scotland.museum","seaport.museum","settlement.museum","settlers.museum","shell.museum","sherbrooke.museum","sibenik.museum","silk.museum","ski.museum","skole.museum","society.museum","sologne.museum","soundandvision.museum","southcarolina.museum","southwest.museum","space.museum","spy.museum","square.museum","stadt.museum","stalbans.museum","starnberg.museum","state.museum","stateofdelaware.museum","station.museum","steam.museum","steiermark.museum","stjohn.museum","stockholm.museum","stpetersburg.museum","stuttgart.museum","suisse.museum","surgeonshall.museum","surrey.museum","svizzera.museum","sweden.museum","sydney.museum","tank.museum","tcm.museum","technology.museum","telekommunikation.museum","television.museum","texas.museum","textile.museum","theater.museum","time.museum","timekeeping.museum","topology.museum","torino.museum","touch.museum","town.museum","transport.museum","tree.museum","trolley.museum","trust.museum","trustee.museum","uhren.museum","ulm.museum","undersea.museum","university.museum","usa.museum","usantiques.museum","usarts.museum","uscountryestate.museum","usculture.museum","usdecorativearts.museum","usgarden.museum","ushistory.museum","ushuaia.museum","uslivinghistory.museum","utah.museum","uvic.museum","valley.museum","vantaa.museum","versailles.museum","viking.museum","village.museum","virginia.museum","virtual.museum","virtuel.museum","vlaanderen.museum","volkenkunde.museum","wales.museum","wallonie.museum","war.museum","washingtondc.museum","watchandclock.museum","watch-and-clock.museum","western.museum","westfalen.museum","whaling.museum","wildlife.museum","williamsburg.museum","windmill.museum","workshop.museum","york.museum","yorkshire.museum","yosemite.museum","youth.museum","zoological.museum","zoology.museum","ירושלים.museum","иком.museum","mv","aero.mv","biz.mv","com.mv","coop.mv","edu.mv","gov.mv","info.mv","int.mv","mil.mv","museum.mv","name.mv","net.mv","org.mv","pro.mv","mw","ac.mw","biz.mw","co.mw","com.mw","coop.mw","edu.mw","gov.mw","int.mw","museum.mw","net.mw","org.mw","mx","com.mx","org.mx","gob.mx","edu.mx","net.mx","my","com.my","net.my","org.my","gov.my","edu.my","mil.my","name.my","mz","ac.mz","adv.mz","co.mz","edu.mz","gov.mz","mil.mz","net.mz","org.mz","na","info.na","pro.na","name.na","school.na","or.na","dr.na","us.na","mx.na","ca.na","in.na","cc.na","tv.na","ws.na","mobi.na","co.na","com.na","org.na","name","nc","asso.nc","nom.nc","ne","net","nf","com.nf","net.nf","per.nf","rec.nf","web.nf","arts.nf","firm.nf","info.nf","other.nf","store.nf","ng","com.ng","edu.ng","gov.ng","i.ng","mil.ng","mobi.ng","name.ng","net.ng","org.ng","sch.ng","ni","ac.ni","biz.ni","co.ni","com.ni","edu.ni","gob.ni","in.ni","info.ni","int.ni","mil.ni","net.ni","nom.ni","org.ni","web.ni","nl","bv.nl","no","fhs.no","vgs.no","fylkesbibl.no","folkebibl.no","museum.no","idrett.no","priv.no","mil.no","stat.no","dep.no","kommune.no","herad.no","aa.no","ah.no","bu.no","fm.no","hl.no","hm.no","jan-mayen.no","mr.no","nl.no","nt.no","of.no","ol.no","oslo.no","rl.no","sf.no","st.no","svalbard.no","tm.no","tr.no","va.no","vf.no","gs.aa.no","gs.ah.no","gs.bu.no","gs.fm.no","gs.hl.no","gs.hm.no","gs.jan-mayen.no","gs.mr.no","gs.nl.no","gs.nt.no","gs.of.no","gs.ol.no","gs.oslo.no","gs.rl.no","gs.sf.no","gs.st.no","gs.svalbard.no","gs.tm.no","gs.tr.no","gs.va.no","gs.vf.no","akrehamn.no","åkrehamn.no","algard.no","ålgård.no","arna.no","brumunddal.no","bryne.no","bronnoysund.no","brønnøysund.no","drobak.no","drøbak.no","egersund.no","fetsund.no","floro.no","florø.no","fredrikstad.no","hokksund.no","honefoss.no","hønefoss.no","jessheim.no","jorpeland.no","jørpeland.no","kirkenes.no","kopervik.no","krokstadelva.no","langevag.no","langevåg.no","leirvik.no","mjondalen.no","mjøndalen.no","mo-i-rana.no","mosjoen.no","mosjøen.no","nesoddtangen.no","orkanger.no","osoyro.no","osøyro.no","raholt.no","råholt.no","sandnessjoen.no","sandnessjøen.no","skedsmokorset.no","slattum.no","spjelkavik.no","stathelle.no","stavern.no","stjordalshalsen.no","stjørdalshalsen.no","tananger.no","tranby.no","vossevangen.no","afjord.no","åfjord.no","agdenes.no","al.no","ål.no","alesund.no","ålesund.no","alstahaug.no","alta.no","áltá.no","alaheadju.no","álaheadju.no","alvdal.no","amli.no","åmli.no","amot.no","åmot.no","andebu.no","andoy.no","andøy.no","andasuolo.no","ardal.no","årdal.no","aremark.no","arendal.no","ås.no","aseral.no","åseral.no","asker.no","askim.no","askvoll.no","askoy.no","askøy.no","asnes.no","åsnes.no","audnedaln.no","aukra.no","aure.no","aurland.no","aurskog-holand.no","aurskog-høland.no","austevoll.no","austrheim.no","averoy.no","averøy.no","balestrand.no","ballangen.no","balat.no","bálát.no","balsfjord.no","bahccavuotna.no","báhccavuotna.no","bamble.no","bardu.no","beardu.no","beiarn.no","bajddar.no","bájddar.no","baidar.no","báidár.no","berg.no","bergen.no","berlevag.no","berlevåg.no","bearalvahki.no","bearalváhki.no","bindal.no","birkenes.no","bjarkoy.no","bjarkøy.no","bjerkreim.no","bjugn.no","bodo.no","bodø.no","badaddja.no","bådåddjå.no","budejju.no","bokn.no","bremanger.no","bronnoy.no","brønnøy.no","bygland.no","bykle.no","barum.no","bærum.no","bo.telemark.no","bø.telemark.no","bo.nordland.no","bø.nordland.no","bievat.no","bievát.no","bomlo.no","bømlo.no","batsfjord.no","båtsfjord.no","bahcavuotna.no","báhcavuotna.no","dovre.no","drammen.no","drangedal.no","dyroy.no","dyrøy.no","donna.no","dønna.no","eid.no","eidfjord.no","eidsberg.no","eidskog.no","eidsvoll.no","eigersund.no","elverum.no","enebakk.no","engerdal.no","etne.no","etnedal.no","evenes.no","evenassi.no","evenášši.no","evje-og-hornnes.no","farsund.no","fauske.no","fuossko.no","fuoisku.no","fedje.no","fet.no","finnoy.no","finnøy.no","fitjar.no","fjaler.no","fjell.no","flakstad.no","flatanger.no","flekkefjord.no","flesberg.no","flora.no","fla.no","flå.no","folldal.no","forsand.no","fosnes.no","frei.no","frogn.no","froland.no","frosta.no","frana.no","fræna.no","froya.no","frøya.no","fusa.no","fyresdal.no","forde.no","førde.no","gamvik.no","gangaviika.no","gáŋgaviika.no","gaular.no","gausdal.no","gildeskal.no","gildeskål.no","giske.no","gjemnes.no","gjerdrum.no","gjerstad.no","gjesdal.no","gjovik.no","gjøvik.no","gloppen.no","gol.no","gran.no","grane.no","granvin.no","gratangen.no","grimstad.no","grong.no","kraanghke.no","kråanghke.no","grue.no","gulen.no","hadsel.no","halden.no","halsa.no","hamar.no","hamaroy.no","habmer.no","hábmer.no","hapmir.no","hápmir.no","hammerfest.no","hammarfeasta.no","hámmárfeasta.no","haram.no","hareid.no","harstad.no","hasvik.no","aknoluokta.no","ákŋoluokta.no","hattfjelldal.no","aarborte.no","haugesund.no","hemne.no","hemnes.no","hemsedal.no","heroy.more-og-romsdal.no","herøy.møre-og-romsdal.no","heroy.nordland.no","herøy.nordland.no","hitra.no","hjartdal.no","hjelmeland.no","hobol.no","hobøl.no","hof.no","hol.no","hole.no","holmestrand.no","holtalen.no","holtålen.no","hornindal.no","horten.no","hurdal.no","hurum.no","hvaler.no","hyllestad.no","hagebostad.no","hægebostad.no","hoyanger.no","høyanger.no","hoylandet.no","høylandet.no","ha.no","hå.no","ibestad.no","inderoy.no","inderøy.no","iveland.no","jevnaker.no","jondal.no","jolster.no","jølster.no","karasjok.no","karasjohka.no","kárášjohka.no","karlsoy.no","galsa.no","gálsá.no","karmoy.no","karmøy.no","kautokeino.no","guovdageaidnu.no","klepp.no","klabu.no","klæbu.no","kongsberg.no","kongsvinger.no","kragero.no","kragerø.no","kristiansand.no","kristiansund.no","krodsherad.no","krødsherad.no","kvalsund.no","rahkkeravju.no","ráhkkerávju.no","kvam.no","kvinesdal.no","kvinnherad.no","kviteseid.no","kvitsoy.no","kvitsøy.no","kvafjord.no","kvæfjord.no","giehtavuoatna.no","kvanangen.no","kvænangen.no","navuotna.no","návuotna.no","kafjord.no","kåfjord.no","gaivuotna.no","gáivuotna.no","larvik.no","lavangen.no","lavagis.no","loabat.no","loabát.no","lebesby.no","davvesiida.no","leikanger.no","leirfjord.no","leka.no","leksvik.no","lenvik.no","leangaviika.no","leaŋgaviika.no","lesja.no","levanger.no","lier.no","lierne.no","lillehammer.no","lillesand.no","lindesnes.no","lindas.no","lindås.no","lom.no","loppa.no","lahppi.no","láhppi.no","lund.no","lunner.no","luroy.no","lurøy.no","luster.no","lyngdal.no","lyngen.no","ivgu.no","lardal.no","lerdal.no","lærdal.no","lodingen.no","lødingen.no","lorenskog.no","lørenskog.no","loten.no","løten.no","malvik.no","masoy.no","måsøy.no","muosat.no","muosát.no","mandal.no","marker.no","marnardal.no","masfjorden.no","meland.no","meldal.no","melhus.no","meloy.no","meløy.no","meraker.no","meråker.no","moareke.no","moåreke.no","midsund.no","midtre-gauldal.no","modalen.no","modum.no","molde.no","moskenes.no","moss.no","mosvik.no","malselv.no","målselv.no","malatvuopmi.no","málatvuopmi.no","namdalseid.no","aejrie.no","namsos.no","namsskogan.no","naamesjevuemie.no","nååmesjevuemie.no","laakesvuemie.no","nannestad.no","narvik.no","narviika.no","naustdal.no","nedre-eiker.no","nes.akershus.no","nes.buskerud.no","nesna.no","nesodden.no","nesseby.no","unjarga.no","unjárga.no","nesset.no","nissedal.no","nittedal.no","nord-aurdal.no","nord-fron.no","nord-odal.no","norddal.no","nordkapp.no","davvenjarga.no","davvenjárga.no","nordre-land.no","nordreisa.no","raisa.no","ráisa.no","nore-og-uvdal.no","notodden.no","naroy.no","nærøy.no","notteroy.no","nøtterøy.no","odda.no","oksnes.no","øksnes.no","oppdal.no","oppegard.no","oppegård.no","orkdal.no","orland.no","ørland.no","orskog.no","ørskog.no","orsta.no","ørsta.no","os.hedmark.no","os.hordaland.no","osen.no","osteroy.no","osterøy.no","ostre-toten.no","østre-toten.no","overhalla.no","ovre-eiker.no","øvre-eiker.no","oyer.no","øyer.no","oygarden.no","øygarden.no","oystre-slidre.no","øystre-slidre.no","porsanger.no","porsangu.no","porsáŋgu.no","porsgrunn.no","radoy.no","radøy.no","rakkestad.no","rana.no","ruovat.no","randaberg.no","rauma.no","rendalen.no","rennebu.no","rennesoy.no","rennesøy.no","rindal.no","ringebu.no","ringerike.no","ringsaker.no","rissa.no","risor.no","risør.no","roan.no","rollag.no","rygge.no","ralingen.no","rælingen.no","rodoy.no","rødøy.no","romskog.no","rømskog.no","roros.no","røros.no","rost.no","røst.no","royken.no","røyken.no","royrvik.no","røyrvik.no","rade.no","råde.no","salangen.no","siellak.no","saltdal.no","salat.no","sálát.no","sálat.no","samnanger.no","sande.more-og-romsdal.no","sande.møre-og-romsdal.no","sande.vestfold.no","sandefjord.no","sandnes.no","sandoy.no","sandøy.no","sarpsborg.no","sauda.no","sauherad.no","sel.no","selbu.no","selje.no","seljord.no","sigdal.no","siljan.no","sirdal.no","skaun.no","skedsmo.no","ski.no","skien.no","skiptvet.no","skjervoy.no","skjervøy.no","skierva.no","skiervá.no","skjak.no","skjåk.no","skodje.no","skanland.no","skånland.no","skanit.no","skánit.no","smola.no","smøla.no","snillfjord.no","snasa.no","snåsa.no","snoasa.no","snaase.no","snåase.no","sogndal.no","sokndal.no","sola.no","solund.no","songdalen.no","sortland.no","spydeberg.no","stange.no","stavanger.no","steigen.no","steinkjer.no","stjordal.no","stjørdal.no","stokke.no","stor-elvdal.no","stord.no","stordal.no","storfjord.no","omasvuotna.no","strand.no","stranda.no","stryn.no","sula.no","suldal.no","sund.no","sunndal.no","surnadal.no","sveio.no","svelvik.no","sykkylven.no","sogne.no","søgne.no","somna.no","sømna.no","sondre-land.no","søndre-land.no","sor-aurdal.no","sør-aurdal.no","sor-fron.no","sør-fron.no","sor-odal.no","sør-odal.no","sor-varanger.no","sør-varanger.no","matta-varjjat.no","mátta-várjjat.no","sorfold.no","sørfold.no","sorreisa.no","sørreisa.no","sorum.no","sørum.no","tana.no","deatnu.no","time.no","tingvoll.no","tinn.no","tjeldsund.no","dielddanuorri.no","tjome.no","tjøme.no","tokke.no","tolga.no","torsken.no","tranoy.no","tranøy.no","tromso.no","tromsø.no","tromsa.no","romsa.no","trondheim.no","troandin.no","trysil.no","trana.no","træna.no","trogstad.no","trøgstad.no","tvedestrand.no","tydal.no","tynset.no","tysfjord.no","divtasvuodna.no","divttasvuotna.no","tysnes.no","tysvar.no","tysvær.no","tonsberg.no","tønsberg.no","ullensaker.no","ullensvang.no","ulvik.no","utsira.no","vadso.no","vadsø.no","cahcesuolo.no","čáhcesuolo.no","vaksdal.no","valle.no","vang.no","vanylven.no","vardo.no","vardø.no","varggat.no","várggát.no","vefsn.no","vaapste.no","vega.no","vegarshei.no","vegårshei.no","vennesla.no","verdal.no","verran.no","vestby.no","vestnes.no","vestre-slidre.no","vestre-toten.no","vestvagoy.no","vestvågøy.no","vevelstad.no","vik.no","vikna.no","vindafjord.no","volda.no","voss.no","varoy.no","værøy.no","vagan.no","vågan.no","voagat.no","vagsoy.no","vågsøy.no","vaga.no","vågå.no","valer.ostfold.no","våler.østfold.no","valer.hedmark.no","våler.hedmark.no","*.np","nr","biz.nr","info.nr","gov.nr","edu.nr","org.nr","net.nr","com.nr","nu","nz","ac.nz","co.nz","cri.nz","geek.nz","gen.nz","govt.nz","health.nz","iwi.nz","kiwi.nz","maori.nz","mil.nz","māori.nz","net.nz","org.nz","parliament.nz","school.nz","om","co.om","com.om","edu.om","gov.om","med.om","museum.om","net.om","org.om","pro.om","onion","org","pa","ac.pa","gob.pa","com.pa","org.pa","sld.pa","edu.pa","net.pa","ing.pa","abo.pa","med.pa","nom.pa","pe","edu.pe","gob.pe","nom.pe","mil.pe","org.pe","com.pe","net.pe","pf","com.pf","org.pf","edu.pf","*.pg","ph","com.ph","net.ph","org.ph","gov.ph","edu.ph","ngo.ph","mil.ph","i.ph","pk","com.pk","net.pk","edu.pk","org.pk","fam.pk","biz.pk","web.pk","gov.pk","gob.pk","gok.pk","gon.pk","gop.pk","gos.pk","info.pk","pl","com.pl","net.pl","org.pl","aid.pl","agro.pl","atm.pl","auto.pl","biz.pl","edu.pl","gmina.pl","gsm.pl","info.pl","mail.pl","miasta.pl","media.pl","mil.pl","nieruchomosci.pl","nom.pl","pc.pl","powiat.pl","priv.pl","realestate.pl","rel.pl","sex.pl","shop.pl","sklep.pl","sos.pl","szkola.pl","targi.pl","tm.pl","tourism.pl","travel.pl","turystyka.pl","gov.pl","ap.gov.pl","ic.gov.pl","is.gov.pl","us.gov.pl","kmpsp.gov.pl","kppsp.gov.pl","kwpsp.gov.pl","psp.gov.pl","wskr.gov.pl","kwp.gov.pl","mw.gov.pl","ug.gov.pl","um.gov.pl","umig.gov.pl","ugim.gov.pl","upow.gov.pl","uw.gov.pl","starostwo.gov.pl","pa.gov.pl","po.gov.pl","psse.gov.pl","pup.gov.pl","rzgw.gov.pl","sa.gov.pl","so.gov.pl","sr.gov.pl","wsa.gov.pl","sko.gov.pl","uzs.gov.pl","wiih.gov.pl","winb.gov.pl","pinb.gov.pl","wios.gov.pl","witd.gov.pl","wzmiuw.gov.pl","piw.gov.pl","wiw.gov.pl","griw.gov.pl","wif.gov.pl","oum.gov.pl","sdn.gov.pl","zp.gov.pl","uppo.gov.pl","mup.gov.pl","wuoz.gov.pl","konsulat.gov.pl","oirm.gov.pl","augustow.pl","babia-gora.pl","bedzin.pl","beskidy.pl","bialowieza.pl","bialystok.pl","bielawa.pl","bieszczady.pl","boleslawiec.pl","bydgoszcz.pl","bytom.pl","cieszyn.pl","czeladz.pl","czest.pl","dlugoleka.pl","elblag.pl","elk.pl","glogow.pl","gniezno.pl","gorlice.pl","grajewo.pl","ilawa.pl","jaworzno.pl","jelenia-gora.pl","jgora.pl","kalisz.pl","kazimierz-dolny.pl","karpacz.pl","kartuzy.pl","kaszuby.pl","katowice.pl","kepno.pl","ketrzyn.pl","klodzko.pl","kobierzyce.pl","kolobrzeg.pl","konin.pl","konskowola.pl","kutno.pl","lapy.pl","lebork.pl","legnica.pl","lezajsk.pl","limanowa.pl","lomza.pl","lowicz.pl","lubin.pl","lukow.pl","malbork.pl","malopolska.pl","mazowsze.pl","mazury.pl","mielec.pl","mielno.pl","mragowo.pl","naklo.pl","nowaruda.pl","nysa.pl","olawa.pl","olecko.pl","olkusz.pl","olsztyn.pl","opoczno.pl","opole.pl","ostroda.pl","ostroleka.pl","ostrowiec.pl","ostrowwlkp.pl","pila.pl","pisz.pl","podhale.pl","podlasie.pl","polkowice.pl","pomorze.pl","pomorskie.pl","prochowice.pl","pruszkow.pl","przeworsk.pl","pulawy.pl","radom.pl","rawa-maz.pl","rybnik.pl","rzeszow.pl","sanok.pl","sejny.pl","slask.pl","slupsk.pl","sosnowiec.pl","stalowa-wola.pl","skoczow.pl","starachowice.pl","stargard.pl","suwalki.pl","swidnica.pl","swiebodzin.pl","swinoujscie.pl","szczecin.pl","szczytno.pl","tarnobrzeg.pl","tgory.pl","turek.pl","tychy.pl","ustka.pl","walbrzych.pl","warmia.pl","warszawa.pl","waw.pl","wegrow.pl","wielun.pl","wlocl.pl","wloclawek.pl","wodzislaw.pl","wolomin.pl","wroclaw.pl","zachpomor.pl","zagan.pl","zarow.pl","zgora.pl","zgorzelec.pl","pm","pn","gov.pn","co.pn","org.pn","edu.pn","net.pn","post","pr","com.pr","net.pr","org.pr","gov.pr","edu.pr","isla.pr","pro.pr","biz.pr","info.pr","name.pr","est.pr","prof.pr","ac.pr","pro","aaa.pro","aca.pro","acct.pro","avocat.pro","bar.pro","cpa.pro","eng.pro","jur.pro","law.pro","med.pro","recht.pro","ps","edu.ps","gov.ps","sec.ps","plo.ps","com.ps","org.ps","net.ps","pt","net.pt","gov.pt","org.pt","edu.pt","int.pt","publ.pt","com.pt","nome.pt","pw","co.pw","ne.pw","or.pw","ed.pw","go.pw","belau.pw","py","com.py","coop.py","edu.py","gov.py","mil.py","net.py","org.py","qa","com.qa","edu.qa","gov.qa","mil.qa","name.qa","net.qa","org.qa","sch.qa","re","asso.re","com.re","nom.re","ro","arts.ro","com.ro","firm.ro","info.ro","nom.ro","nt.ro","org.ro","rec.ro","store.ro","tm.ro","www.ro","rs","ac.rs","co.rs","edu.rs","gov.rs","in.rs","org.rs","ru","ac.ru","edu.ru","gov.ru","int.ru","mil.ru","test.ru","rw","gov.rw","net.rw","edu.rw","ac.rw","com.rw","co.rw","int.rw","mil.rw","gouv.rw","sa","com.sa","net.sa","org.sa","gov.sa","med.sa","pub.sa","edu.sa","sch.sa","sb","com.sb","edu.sb","gov.sb","net.sb","org.sb","sc","com.sc","gov.sc","net.sc","org.sc","edu.sc","sd","com.sd","net.sd","org.sd","edu.sd","med.sd","tv.sd","gov.sd","info.sd","se","a.se","ac.se","b.se","bd.se","brand.se","c.se","d.se","e.se","f.se","fh.se","fhsk.se","fhv.se","g.se","h.se","i.se","k.se","komforb.se","kommunalforbund.se","komvux.se","l.se","lanbib.se","m.se","n.se","naturbruksgymn.se","o.se","org.se","p.se","parti.se","pp.se","press.se","r.se","s.se","t.se","tm.se","u.se","w.se","x.se","y.se","z.se","sg","com.sg","net.sg","org.sg","gov.sg","edu.sg","per.sg","sh","com.sh","net.sh","gov.sh","org.sh","mil.sh","si","sj","sk","sl","com.sl","net.sl","edu.sl","gov.sl","org.sl","sm","sn","art.sn","com.sn","edu.sn","gouv.sn","org.sn","perso.sn","univ.sn","so","com.so","net.so","org.so","sr","st","co.st","com.st","consulado.st","edu.st","embaixada.st","gov.st","mil.st","net.st","org.st","principe.st","saotome.st","store.st","su","sv","com.sv","edu.sv","gob.sv","org.sv","red.sv","sx","gov.sx","sy","edu.sy","gov.sy","net.sy","mil.sy","com.sy","org.sy","sz","co.sz","ac.sz","org.sz","tc","td","tel","tf","tg","th","ac.th","co.th","go.th","in.th","mi.th","net.th","or.th","tj","ac.tj","biz.tj","co.tj","com.tj","edu.tj","go.tj","gov.tj","int.tj","mil.tj","name.tj","net.tj","nic.tj","org.tj","test.tj","web.tj","tk","tl","gov.tl","tm","com.tm","co.tm","org.tm","net.tm","nom.tm","gov.tm","mil.tm","edu.tm","tn","com.tn","ens.tn","fin.tn","gov.tn","ind.tn","intl.tn","nat.tn","net.tn","org.tn","info.tn","perso.tn","tourism.tn","edunet.tn","rnrt.tn","rns.tn","rnu.tn","mincom.tn","agrinet.tn","defense.tn","turen.tn","to","com.to","gov.to","net.to","org.to","edu.to","mil.to","tr","com.tr","info.tr","biz.tr","net.tr","org.tr","web.tr","gen.tr","tv.tr","av.tr","dr.tr","bbs.tr","name.tr","tel.tr","gov.tr","bel.tr","pol.tr","mil.tr","k12.tr","edu.tr","kep.tr","nc.tr","gov.nc.tr","tt","co.tt","com.tt","org.tt","net.tt","biz.tt","info.tt","pro.tt","int.tt","coop.tt","jobs.tt","mobi.tt","travel.tt","museum.tt","aero.tt","name.tt","gov.tt","edu.tt","tv","tw","edu.tw","gov.tw","mil.tw","com.tw","net.tw","org.tw","idv.tw","game.tw","ebiz.tw","club.tw","網路.tw","組織.tw","商業.tw","tz","ac.tz","co.tz","go.tz","hotel.tz","info.tz","me.tz","mil.tz","mobi.tz","ne.tz","or.tz","sc.tz","tv.tz","ua","com.ua","edu.ua","gov.ua","in.ua","net.ua","org.ua","cherkassy.ua","cherkasy.ua","chernigov.ua","chernihiv.ua","chernivtsi.ua","chernovtsy.ua","ck.ua","cn.ua","cr.ua","crimea.ua","cv.ua","dn.ua","dnepropetrovsk.ua","dnipropetrovsk.ua","dominic.ua","donetsk.ua","dp.ua","if.ua","ivano-frankivsk.ua","kh.ua","kharkiv.ua","kharkov.ua","kherson.ua","khmelnitskiy.ua","khmelnytskyi.ua","kiev.ua","kirovograd.ua","km.ua","kr.ua","krym.ua","ks.ua","kv.ua","kyiv.ua","lg.ua","lt.ua","lugansk.ua","lutsk.ua","lv.ua","lviv.ua","mk.ua","mykolaiv.ua","nikolaev.ua","od.ua","odesa.ua","odessa.ua","pl.ua","poltava.ua","rivne.ua","rovno.ua","rv.ua","sb.ua","sebastopol.ua","sevastopol.ua","sm.ua","sumy.ua","te.ua","ternopil.ua","uz.ua","uzhgorod.ua","vinnica.ua","vinnytsia.ua","vn.ua","volyn.ua","yalta.ua","zaporizhzhe.ua","zaporizhzhia.ua","zhitomir.ua","zhytomyr.ua","zp.ua","zt.ua","ug","co.ug","or.ug","ac.ug","sc.ug","go.ug","ne.ug","com.ug","org.ug","uk","ac.uk","co.uk","gov.uk","ltd.uk","me.uk","net.uk","nhs.uk","org.uk","plc.uk","police.uk","*.sch.uk","us","dni.us","fed.us","isa.us","kids.us","nsn.us","ak.us","al.us","ar.us","as.us","az.us","ca.us","co.us","ct.us","dc.us","de.us","fl.us","ga.us","gu.us","hi.us","ia.us","id.us","il.us","in.us","ks.us","ky.us","la.us","ma.us","md.us","me.us","mi.us","mn.us","mo.us","ms.us","mt.us","nc.us","nd.us","ne.us","nh.us","nj.us","nm.us","nv.us","ny.us","oh.us","ok.us","or.us","pa.us","pr.us","ri.us","sc.us","sd.us","tn.us","tx.us","ut.us","vi.us","vt.us","va.us","wa.us","wi.us","wv.us","wy.us","k12.ak.us","k12.al.us","k12.ar.us","k12.as.us","k12.az.us","k12.ca.us","k12.co.us","k12.ct.us","k12.dc.us","k12.de.us","k12.fl.us","k12.ga.us","k12.gu.us","k12.ia.us","k12.id.us","k12.il.us","k12.in.us","k12.ks.us","k12.ky.us","k12.la.us","k12.ma.us","k12.md.us","k12.me.us","k12.mi.us","k12.mn.us","k12.mo.us","k12.ms.us","k12.mt.us","k12.nc.us","k12.ne.us","k12.nh.us","k12.nj.us","k12.nm.us","k12.nv.us","k12.ny.us","k12.oh.us","k12.ok.us","k12.or.us","k12.pa.us","k12.pr.us","k12.ri.us","k12.sc.us","k12.tn.us","k12.tx.us","k12.ut.us","k12.vi.us","k12.vt.us","k12.va.us","k12.wa.us","k12.wi.us","k12.wy.us","cc.ak.us","cc.al.us","cc.ar.us","cc.as.us","cc.az.us","cc.ca.us","cc.co.us","cc.ct.us","cc.dc.us","cc.de.us","cc.fl.us","cc.ga.us","cc.gu.us","cc.hi.us","cc.ia.us","cc.id.us","cc.il.us","cc.in.us","cc.ks.us","cc.ky.us","cc.la.us","cc.ma.us","cc.md.us","cc.me.us","cc.mi.us","cc.mn.us","cc.mo.us","cc.ms.us","cc.mt.us","cc.nc.us","cc.nd.us","cc.ne.us","cc.nh.us","cc.nj.us","cc.nm.us","cc.nv.us","cc.ny.us","cc.oh.us","cc.ok.us","cc.or.us","cc.pa.us","cc.pr.us","cc.ri.us","cc.sc.us","cc.sd.us","cc.tn.us","cc.tx.us","cc.ut.us","cc.vi.us","cc.vt.us","cc.va.us","cc.wa.us","cc.wi.us","cc.wv.us","cc.wy.us","lib.ak.us","lib.al.us","lib.ar.us","lib.as.us","lib.az.us","lib.ca.us","lib.co.us","lib.ct.us","lib.dc.us","lib.fl.us","lib.ga.us","lib.gu.us","lib.hi.us","lib.ia.us","lib.id.us","lib.il.us","lib.in.us","lib.ks.us","lib.ky.us","lib.la.us","lib.ma.us","lib.md.us","lib.me.us","lib.mi.us","lib.mn.us","lib.mo.us","lib.ms.us","lib.mt.us","lib.nc.us","lib.nd.us","lib.ne.us","lib.nh.us","lib.nj.us","lib.nm.us","lib.nv.us","lib.ny.us","lib.oh.us","lib.ok.us","lib.or.us","lib.pa.us","lib.pr.us","lib.ri.us","lib.sc.us","lib.sd.us","lib.tn.us","lib.tx.us","lib.ut.us","lib.vi.us","lib.vt.us","lib.va.us","lib.wa.us","lib.wi.us","lib.wy.us","pvt.k12.ma.us","chtr.k12.ma.us","paroch.k12.ma.us","ann-arbor.mi.us","cog.mi.us","dst.mi.us","eaton.mi.us","gen.mi.us","mus.mi.us","tec.mi.us","washtenaw.mi.us","uy","com.uy","edu.uy","gub.uy","mil.uy","net.uy","org.uy","uz","co.uz","com.uz","net.uz","org.uz","va","vc","com.vc","net.vc","org.vc","gov.vc","mil.vc","edu.vc","ve","arts.ve","co.ve","com.ve","e12.ve","edu.ve","firm.ve","gob.ve","gov.ve","info.ve","int.ve","mil.ve","net.ve","org.ve","rec.ve","store.ve","tec.ve","web.ve","vg","vi","co.vi","com.vi","k12.vi","net.vi","org.vi","vn","com.vn","net.vn","org.vn","edu.vn","gov.vn","int.vn","ac.vn","biz.vn","info.vn","name.vn","pro.vn","health.vn","vu","com.vu","edu.vu","net.vu","org.vu","wf","ws","com.ws","net.ws","org.ws","gov.ws","edu.ws","yt","امارات","հայ","বাংলা","бг","бел","中国","中國","الجزائر","مصر","ею","გე","ελ","香港","公司.香港","教育.香港","政府.香港","個人.香港","網絡.香港","組織.香港","ಭಾರತ","ଭାରତ","ভাৰত","भारतम्","भारोत","ڀارت","ഭാരതം","भारत","بارت","بھارت","భారత్","ભારત","ਭਾਰਤ","ভারত","இந்தியா","ایران","ايران","عراق","الاردن","한국","қаз","ලංකා","இலங்கை","المغرب","мкд","мон","澳門","澳门","مليسيا","عمان","پاکستان","پاكستان","فلسطين","срб","пр.срб","орг.срб","обр.срб","од.срб","упр.срб","ак.срб","рф","قطر","السعودية","السعودیة","السعودیۃ","السعوديه","سودان","新加坡","சிங்கப்பூர்","سورية","سوريا","ไทย","ศึกษา.ไทย","ธุรกิจ.ไทย","รัฐบาล.ไทย","ทหาร.ไทย","เน็ต.ไทย","องค์กร.ไทย","تونس","台灣","台湾","臺灣","укр","اليمن","xxx","*.ye","ac.za","agric.za","alt.za","co.za","edu.za","gov.za","grondar.za","law.za","mil.za","net.za","ngo.za","nis.za","nom.za","org.za","school.za","tm.za","web.za","zm","ac.zm","biz.zm","co.zm","com.zm","edu.zm","gov.zm","info.zm","mil.zm","net.zm","org.zm","sch.zm","zw","ac.zw","co.zw","gov.zw","mil.zw","org.zw","aaa","aarp","abarth","abb","abbott","abbvie","abc","able","abogado","abudhabi","academy","accenture","accountant","accountants","aco","active","actor","adac","ads","adult","aeg","aetna","afamilycompany","afl","africa","agakhan","agency","aig","aigo","airbus","airforce","airtel","akdn","alfaromeo","alibaba","alipay","allfinanz","allstate","ally","alsace","alstom","americanexpress","americanfamily","amex","amfam","amica","amsterdam","analytics","android","anquan","anz","aol","apartments","app","apple","aquarelle","arab","aramco","archi","army","art","arte","asda","associates","athleta","attorney","auction","audi","audible","audio","auspost","author","auto","autos","avianca","aws","axa","azure","baby","baidu","banamex","bananarepublic","band","bank","bar","barcelona","barclaycard","barclays","barefoot","bargains","baseball","basketball","bauhaus","bayern","bbc","bbt","bbva","bcg","bcn","beats","beauty","beer","bentley","berlin","best","bestbuy","bet","bharti","bible","bid","bike","bing","bingo","bio","black","blackfriday","blanco","blockbuster","blog","bloomberg","blue","bms","bmw","bnl","bnpparibas","boats","boehringer","bofa","bom","bond","boo","book","booking","bosch","bostik","boston","bot","boutique","box","bradesco","bridgestone","broadway","broker","brother","brussels","budapest","bugatti","build","builders","business","buy","buzz","bzh","cab","cafe","cal","call","calvinklein","cam","camera","camp","cancerresearch","canon","capetown","capital","capitalone","car","caravan","cards","care","career","careers","cars","cartier","casa","case","caseih","cash","casino","catering","catholic","cba","cbn","cbre","cbs","ceb","center","ceo","cern","cfa","cfd","chanel","channel","charity","chase","chat","cheap","chintai","christmas","chrome","chrysler","church","cipriani","circle","cisco","citadel","citi","citic","city","cityeats","claims","cleaning","click","clinic","clinique","clothing","cloud","club","clubmed","coach","codes","coffee","college","cologne","comcast","commbank","community","company","compare","computer","comsec","condos","construction","consulting","contact","contractors","cooking","cookingchannel","cool","corsica","country","coupon","coupons","courses","credit","creditcard","creditunion","cricket","crown","crs","cruise","cruises","csc","cuisinella","cymru","cyou","dabur","dad","dance","data","date","dating","datsun","day","dclk","dds","deal","dealer","deals","degree","delivery","dell","deloitte","delta","democrat","dental","dentist","desi","design","dev","dhl","diamonds","diet","digital","direct","directory","discount","discover","dish","diy","dnp","docs","doctor","dodge","dog","doha","domains","dot","download","drive","dtv","dubai","duck","dunlop","duns","dupont","durban","dvag","dvr","earth","eat","eco","edeka","education","email","emerck","energy","engineer","engineering","enterprises","epost","epson","equipment","ericsson","erni","esq","estate","esurance","etisalat","eurovision","eus","events","everbank","exchange","expert","exposed","express","extraspace","fage","fail","fairwinds","faith","family","fan","fans","farm","farmers","fashion","fast","fedex","feedback","ferrari","ferrero","fiat","fidelity","fido","film","final","finance","financial","fire","firestone","firmdale","fish","fishing","fit","fitness","flickr","flights","flir","florist","flowers","fly","foo","food","foodnetwork","football","ford","forex","forsale","forum","foundation","fox","free","fresenius","frl","frogans","frontdoor","frontier","ftr","fujitsu","fujixerox","fun","fund","furniture","futbol","fyi","gal","gallery","gallo","gallup","game","games","gap","garden","gbiz","gdn","gea","gent","genting","george","ggee","gift","gifts","gives","giving","glade","glass","gle","global","globo","gmail","gmbh","gmo","gmx","godaddy","gold","goldpoint","golf","goo","goodhands","goodyear","goog","google","gop","got","grainger","graphics","gratis","green","gripe","grocery","group","guardian","gucci","guge","guide","guitars","guru","hair","hamburg","hangout","haus","hbo","hdfc","hdfcbank","health","healthcare","help","helsinki","here","hermes","hgtv","hiphop","hisamitsu","hitachi","hiv","hkt","hockey","holdings","holiday","homedepot","homegoods","homes","homesense","honda","honeywell","horse","hospital","host","hosting","hot","hoteles","hotels","hotmail","house","how","hsbc","hughes","hyatt","hyundai","ibm","icbc","ice","icu","ieee","ifm","ikano","imamat","imdb","immo","immobilien","inc","industries","infiniti","ing","ink","institute","insurance","insure","intel","international","intuit","investments","ipiranga","irish","iselect","ismaili","ist","istanbul","itau","itv","iveco","jaguar","java","jcb","jcp","jeep","jetzt","jewelry","jio","jlc","jll","jmp","jnj","joburg","jot","joy","jpmorgan","jprs","juegos","juniper","kaufen","kddi","kerryhotels","kerrylogistics","kerryproperties","kfh","kia","kim","kinder","kindle","kitchen","kiwi","koeln","komatsu","kosher","kpmg","kpn","krd","kred","kuokgroup","kyoto","lacaixa","ladbrokes","lamborghini","lamer","lancaster","lancia","lancome","land","landrover","lanxess","lasalle","lat","latino","latrobe","law","lawyer","lds","lease","leclerc","lefrak","legal","lego","lexus","lgbt","liaison","lidl","life","lifeinsurance","lifestyle","lighting","like","lilly","limited","limo","lincoln","linde","link","lipsy","live","living","lixil","llc","loan","loans","locker","locus","loft","lol","london","lotte","lotto","love","lpl","lplfinancial","ltd","ltda","lundbeck","lupin","luxe","luxury","macys","madrid","maif","maison","makeup","man","management","mango","map","market","marketing","markets","marriott","marshalls","maserati","mattel","mba","mckinsey","med","media","meet","melbourne","meme","memorial","men","menu","merckmsd","metlife","miami","microsoft","mini","mint","mit","mitsubishi","mlb","mls","mma","mobile","mobily","moda","moe","moi","mom","monash","money","monster","mopar","mormon","mortgage","moscow","moto","motorcycles","mov","movie","movistar","msd","mtn","mtr","mutual","nab","nadex","nagoya","nationwide","natura","navy","nba","nec","netbank","netflix","network","neustar","new","newholland","news","next","nextdirect","nexus","nfl","ngo","nhk","nico","nike","nikon","ninja","nissan","nissay","nokia","northwesternmutual","norton","now","nowruz","nowtv","nra","nrw","ntt","nyc","obi","observer","off","office","okinawa","olayan","olayangroup","oldnavy","ollo","omega","one","ong","onl","online","onyourside","ooo","open","oracle","orange","organic","origins","osaka","otsuka","ott","ovh","page","panasonic","panerai","paris","pars","partners","parts","party","passagens","pay","pccw","pet","pfizer","pharmacy","phd","philips","phone","photo","photography","photos","physio","piaget","pics","pictet","pictures","pid","pin","ping","pink","pioneer","pizza","place","play","playstation","plumbing","plus","pnc","pohl","poker","politie","porn","pramerica","praxi","press","prime","prod","productions","prof","progressive","promo","properties","property","protection","pru","prudential","pub","pwc","qpon","quebec","quest","qvc","racing","radio","raid","read","realestate","realtor","realty","recipes","red","redstone","redumbrella","rehab","reise","reisen","reit","reliance","ren","rent","rentals","repair","report","republican","rest","restaurant","review","reviews","rexroth","rich","richardli","ricoh","rightathome","ril","rio","rip","rmit","rocher","rocks","rodeo","rogers","room","rsvp","rugby","ruhr","run","rwe","ryukyu","saarland","safe","safety","sakura","sale","salon","samsclub","samsung","sandvik","sandvikcoromant","sanofi","sap","sarl","sas","save","saxo","sbi","sbs","sca","scb","schaeffler","schmidt","scholarships","school","schule","schwarz","science","scjohnson","scor","scot","search","seat","secure","security","seek","select","sener","services","ses","seven","sew","sex","sexy","sfr","shangrila","sharp","shaw","shell","shia","shiksha","shoes","shop","shopping","shouji","show","showtime","shriram","silk","sina","singles","site","ski","skin","sky","skype","sling","smart","smile","sncf","soccer","social","softbank","software","sohu","solar","solutions","song","sony","soy","space","spiegel","sport","spot","spreadbetting","srl","srt","stada","staples","star","starhub","statebank","statefarm","statoil","stc","stcgroup","stockholm","storage","store","stream","studio","study","style","sucks","supplies","supply","support","surf","surgery","suzuki","swatch","swiftcover","swiss","sydney","symantec","systems","tab","taipei","talk","taobao","target","tatamotors","tatar","tattoo","tax","taxi","tci","tdk","team","tech","technology","telecity","telefonica","temasek","tennis","teva","thd","theater","theatre","tiaa","tickets","tienda","tiffany","tips","tires","tirol","tjmaxx","tjx","tkmaxx","tmall","today","tokyo","tools","top","toray","toshiba","total","tours","town","toyota","toys","trade","trading","training","travel","travelchannel","travelers","travelersinsurance","trust","trv","tube","tui","tunes","tushu","tvs","ubank","ubs","uconnect","unicom","university","uno","uol","ups","vacations","vana","vanguard","vegas","ventures","verisign","versicherung","vet","viajes","video","vig","viking","villas","vin","vip","virgin","visa","vision","vista","vistaprint","viva","vivo","vlaanderen","vodka","volkswagen","volvo","vote","voting","voto","voyage","vuelos","wales","walmart","walter","wang","wanggou","warman","watch","watches","weather","weatherchannel","webcam","weber","website","wed","wedding","weibo","weir","whoswho","wien","wiki","williamhill","win","windows","wine","winners","wme","wolterskluwer","woodside","work","works","world","wow","wtc","wtf","xbox","xerox","xfinity","xihuan","xin","कॉम","セール","佛山","慈善","集团","在线","大众汽车","点看","คอม","八卦","موقع","公益","公司","香格里拉","网站","移动","我爱你","москва","католик","онлайн","сайт","联通","קום","时尚","微博","淡马锡","ファッション","орг","नेट","ストア","삼성","商标","商店","商城","дети","ポイント","新闻","工行","家電","كوم","中文网","中信","娱乐","谷歌","電訊盈科","购物","クラウド","通販","网店","संगठन","餐厅","网络","ком","诺基亚","食品","飞利浦","手表","手机","ارامكو","العليان","اتصالات","بازار","موبايلي","ابوظبي","كاثوليك","همراه","닷컴","政府","شبكة","بيتك","عرب","机构","组织机构","健康","招聘","рус","珠宝","大拿","みんな","グーグル","世界","書籍","网址","닷넷","コム","天主教","游戏","vermögensberater","vermögensberatung","企业","信息","嘉里大酒店","嘉里","广东","政务","xyz","yachts","yahoo","yamaxun","yandex","yodobashi","yoga","yokohama","you","youtube","yun","zappos","zara","zero","zip","zippo","zone","zuerich","cc.ua","inf.ua","ltd.ua","beep.pl","*.compute.estate","*.alces.network","alwaysdata.net","cloudfront.net","*.compute.amazonaws.com","*.compute-1.amazonaws.com","*.compute.amazonaws.com.cn","us-east-1.amazonaws.com","cn-north-1.eb.amazonaws.com.cn","elasticbeanstalk.com","ap-northeast-1.elasticbeanstalk.com","ap-northeast-2.elasticbeanstalk.com","ap-northeast-3.elasticbeanstalk.com","ap-south-1.elasticbeanstalk.com","ap-southeast-1.elasticbeanstalk.com","ap-southeast-2.elasticbeanstalk.com","ca-central-1.elasticbeanstalk.com","eu-central-1.elasticbeanstalk.com","eu-west-1.elasticbeanstalk.com","eu-west-2.elasticbeanstalk.com","eu-west-3.elasticbeanstalk.com","sa-east-1.elasticbeanstalk.com","us-east-1.elasticbeanstalk.com","us-east-2.elasticbeanstalk.com","us-gov-west-1.elasticbeanstalk.com","us-west-1.elasticbeanstalk.com","us-west-2.elasticbeanstalk.com","*.elb.amazonaws.com","*.elb.amazonaws.com.cn","s3.amazonaws.com","s3-ap-northeast-1.amazonaws.com","s3-ap-northeast-2.amazonaws.com","s3-ap-south-1.amazonaws.com","s3-ap-southeast-1.amazonaws.com","s3-ap-southeast-2.amazonaws.com","s3-ca-central-1.amazonaws.com","s3-eu-central-1.amazonaws.com","s3-eu-west-1.amazonaws.com","s3-eu-west-2.amazonaws.com","s3-eu-west-3.amazonaws.com","s3-external-1.amazonaws.com","s3-fips-us-gov-west-1.amazonaws.com","s3-sa-east-1.amazonaws.com","s3-us-gov-west-1.amazonaws.com","s3-us-east-2.amazonaws.com","s3-us-west-1.amazonaws.com","s3-us-west-2.amazonaws.com","s3.ap-northeast-2.amazonaws.com","s3.ap-south-1.amazonaws.com","s3.cn-north-1.amazonaws.com.cn","s3.ca-central-1.amazonaws.com","s3.eu-central-1.amazonaws.com","s3.eu-west-2.amazonaws.com","s3.eu-west-3.amazonaws.com","s3.us-east-2.amazonaws.com","s3.dualstack.ap-northeast-1.amazonaws.com","s3.dualstack.ap-northeast-2.amazonaws.com","s3.dualstack.ap-south-1.amazonaws.com","s3.dualstack.ap-southeast-1.amazonaws.com","s3.dualstack.ap-southeast-2.amazonaws.com","s3.dualstack.ca-central-1.amazonaws.com","s3.dualstack.eu-central-1.amazonaws.com","s3.dualstack.eu-west-1.amazonaws.com","s3.dualstack.eu-west-2.amazonaws.com","s3.dualstack.eu-west-3.amazonaws.com","s3.dualstack.sa-east-1.amazonaws.com","s3.dualstack.us-east-1.amazonaws.com","s3.dualstack.us-east-2.amazonaws.com","s3-website-us-east-1.amazonaws.com","s3-website-us-west-1.amazonaws.com","s3-website-us-west-2.amazonaws.com","s3-website-ap-northeast-1.amazonaws.com","s3-website-ap-southeast-1.amazonaws.com","s3-website-ap-southeast-2.amazonaws.com","s3-website-eu-west-1.amazonaws.com","s3-website-sa-east-1.amazonaws.com","s3-website.ap-northeast-2.amazonaws.com","s3-website.ap-south-1.amazonaws.com","s3-website.ca-central-1.amazonaws.com","s3-website.eu-central-1.amazonaws.com","s3-website.eu-west-2.amazonaws.com","s3-website.eu-west-3.amazonaws.com","s3-website.us-east-2.amazonaws.com","t3l3p0rt.net","tele.amune.org","on-aptible.com","user.party.eus","pimienta.org","poivron.org","potager.org","sweetpepper.org","myasustor.com","myfritz.net","*.awdev.ca","*.advisor.ws","backplaneapp.io","betainabox.com","bnr.la","blackbaudcdn.net","boomla.net","boxfuse.io","square7.ch","bplaced.com","bplaced.de","square7.de","bplaced.net","square7.net","browsersafetymark.io","mycd.eu","ae.org","ar.com","br.com","cn.com","com.de","com.se","de.com","eu.com","gb.com","gb.net","hu.com","hu.net","jp.net","jpn.com","kr.com","mex.com","no.com","qc.com","ru.com","sa.com","se.net","uk.com","uk.net","us.com","uy.com","za.bz","za.com","africa.com","gr.com","in.net","us.org","co.com","c.la","certmgr.org","xenapponazure.com","virtueeldomein.nl","cleverapps.io","c66.me","cloud66.ws","jdevcloud.com","wpdevcloud.com","cloudaccess.host","freesite.host","cloudaccess.net","cloudcontrolled.com","cloudcontrolapp.com","co.ca","*.otap.co","co.cz","c.cdn77.org","cdn77-ssl.net","r.cdn77.net","rsc.cdn77.org","ssl.origin.cdn77-secure.org","cloudns.asia","cloudns.biz","cloudns.club","cloudns.cc","cloudns.eu","cloudns.in","cloudns.info","cloudns.org","cloudns.pro","cloudns.pw","cloudns.us","cloudeity.net","cnpy.gdn","co.nl","co.no","webhosting.be","hosting-cluster.nl","dyn.cosidns.de","dynamisches-dns.de","dnsupdater.de","internet-dns.de","l-o-g-i-n.de","dynamic-dns.info","feste-ip.net","knx-server.net","static-access.net","realm.cz","*.cryptonomic.net","cupcake.is","cyon.link","cyon.site","daplie.me","localhost.daplie.me","dattolocal.com","dattorelay.com","dattoweb.com","mydatto.com","dattolocal.net","mydatto.net","biz.dk","co.dk","firm.dk","reg.dk","store.dk","debian.net","dedyn.io","dnshome.de","drayddns.com","dreamhosters.com","mydrobo.com","drud.io","drud.us","duckdns.org","dy.fi","tunk.org","dyndns-at-home.com","dyndns-at-work.com","dyndns-blog.com","dyndns-free.com","dyndns-home.com","dyndns-ip.com","dyndns-mail.com","dyndns-office.com","dyndns-pics.com","dyndns-remote.com","dyndns-server.com","dyndns-web.com","dyndns-wiki.com","dyndns-work.com","dyndns.biz","dyndns.info","dyndns.org","dyndns.tv","at-band-camp.net","ath.cx","barrel-of-knowledge.info","barrell-of-knowledge.info","better-than.tv","blogdns.com","blogdns.net","blogdns.org","blogsite.org","boldlygoingnowhere.org","broke-it.net","buyshouses.net","cechire.com","dnsalias.com","dnsalias.net","dnsalias.org","dnsdojo.com","dnsdojo.net","dnsdojo.org","does-it.net","doesntexist.com","doesntexist.org","dontexist.com","dontexist.net","dontexist.org","doomdns.com","doomdns.org","dvrdns.org","dyn-o-saur.com","dynalias.com","dynalias.net","dynalias.org","dynathome.net","dyndns.ws","endofinternet.net","endofinternet.org","endoftheinternet.org","est-a-la-maison.com","est-a-la-masion.com","est-le-patron.com","est-mon-blogueur.com","for-better.biz","for-more.biz","for-our.info","for-some.biz","for-the.biz","forgot.her.name","forgot.his.name","from-ak.com","from-al.com","from-ar.com","from-az.net","from-ca.com","from-co.net","from-ct.com","from-dc.com","from-de.com","from-fl.com","from-ga.com","from-hi.com","from-ia.com","from-id.com","from-il.com","from-in.com","from-ks.com","from-ky.com","from-la.net","from-ma.com","from-md.com","from-me.org","from-mi.com","from-mn.com","from-mo.com","from-ms.com","from-mt.com","from-nc.com","from-nd.com","from-ne.com","from-nh.com","from-nj.com","from-nm.com","from-nv.com","from-ny.net","from-oh.com","from-ok.com","from-or.com","from-pa.com","from-pr.com","from-ri.com","from-sc.com","from-sd.com","from-tn.com","from-tx.com","from-ut.com","from-va.com","from-vt.com","from-wa.com","from-wi.com","from-wv.com","from-wy.com","ftpaccess.cc","fuettertdasnetz.de","game-host.org","game-server.cc","getmyip.com","gets-it.net","go.dyndns.org","gotdns.com","gotdns.org","groks-the.info","groks-this.info","ham-radio-op.net","here-for-more.info","hobby-site.com","hobby-site.org","home.dyndns.org","homedns.org","homeftp.net","homeftp.org","homeip.net","homelinux.com","homelinux.net","homelinux.org","homeunix.com","homeunix.net","homeunix.org","iamallama.com","in-the-band.net","is-a-anarchist.com","is-a-blogger.com","is-a-bookkeeper.com","is-a-bruinsfan.org","is-a-bulls-fan.com","is-a-candidate.org","is-a-caterer.com","is-a-celticsfan.org","is-a-chef.com","is-a-chef.net","is-a-chef.org","is-a-conservative.com","is-a-cpa.com","is-a-cubicle-slave.com","is-a-democrat.com","is-a-designer.com","is-a-doctor.com","is-a-financialadvisor.com","is-a-geek.com","is-a-geek.net","is-a-geek.org","is-a-green.com","is-a-guru.com","is-a-hard-worker.com","is-a-hunter.com","is-a-knight.org","is-a-landscaper.com","is-a-lawyer.com","is-a-liberal.com","is-a-libertarian.com","is-a-linux-user.org","is-a-llama.com","is-a-musician.com","is-a-nascarfan.com","is-a-nurse.com","is-a-painter.com","is-a-patsfan.org","is-a-personaltrainer.com","is-a-photographer.com","is-a-player.com","is-a-republican.com","is-a-rockstar.com","is-a-socialist.com","is-a-soxfan.org","is-a-student.com","is-a-teacher.com","is-a-techie.com","is-a-therapist.com","is-an-accountant.com","is-an-actor.com","is-an-actress.com","is-an-anarchist.com","is-an-artist.com","is-an-engineer.com","is-an-entertainer.com","is-by.us","is-certified.com","is-found.org","is-gone.com","is-into-anime.com","is-into-cars.com","is-into-cartoons.com","is-into-games.com","is-leet.com","is-lost.org","is-not-certified.com","is-saved.org","is-slick.com","is-uberleet.com","is-very-bad.org","is-very-evil.org","is-very-good.org","is-very-nice.org","is-very-sweet.org","is-with-theband.com","isa-geek.com","isa-geek.net","isa-geek.org","isa-hockeynut.com","issmarterthanyou.com","isteingeek.de","istmein.de","kicks-ass.net","kicks-ass.org","knowsitall.info","land-4-sale.us","lebtimnetz.de","leitungsen.de","likes-pie.com","likescandy.com","merseine.nu","mine.nu","misconfused.org","mypets.ws","myphotos.cc","neat-url.com","office-on-the.net","on-the-web.tv","podzone.net","podzone.org","readmyblog.org","saves-the-whales.com","scrapper-site.net","scrapping.cc","selfip.biz","selfip.com","selfip.info","selfip.net","selfip.org","sells-for-less.com","sells-for-u.com","sells-it.net","sellsyourhome.org","servebbs.com","servebbs.net","servebbs.org","serveftp.net","serveftp.org","servegame.org","shacknet.nu","simple-url.com","space-to-rent.com","stuff-4-sale.org","stuff-4-sale.us","teaches-yoga.com","thruhere.net","traeumtgerade.de","webhop.biz","webhop.info","webhop.net","webhop.org","worse-than.tv","writesthisblog.com","ddnss.de","dyn.ddnss.de","dyndns.ddnss.de","dyndns1.de","dyn-ip24.de","home-webserver.de","dyn.home-webserver.de","myhome-server.de","ddnss.org","definima.net","definima.io","bci.dnstrace.pro","ddnsfree.com","ddnsgeek.com","giize.com","gleeze.com","kozow.com","loseyourip.com","ooguy.com","theworkpc.com","casacam.net","dynu.net","accesscam.org","camdvr.org","freeddns.org","mywire.org","webredirect.org","myddns.rocks","blogsite.xyz","dynv6.net","e4.cz","mytuleap.com","enonic.io","customer.enonic.io","eu.org","al.eu.org","asso.eu.org","at.eu.org","au.eu.org","be.eu.org","bg.eu.org","ca.eu.org","cd.eu.org","ch.eu.org","cn.eu.org","cy.eu.org","cz.eu.org","de.eu.org","dk.eu.org","edu.eu.org","ee.eu.org","es.eu.org","fi.eu.org","fr.eu.org","gr.eu.org","hr.eu.org","hu.eu.org","ie.eu.org","il.eu.org","in.eu.org","int.eu.org","is.eu.org","it.eu.org","jp.eu.org","kr.eu.org","lt.eu.org","lu.eu.org","lv.eu.org","mc.eu.org","me.eu.org","mk.eu.org","mt.eu.org","my.eu.org","net.eu.org","ng.eu.org","nl.eu.org","no.eu.org","nz.eu.org","paris.eu.org","pl.eu.org","pt.eu.org","q-a.eu.org","ro.eu.org","ru.eu.org","se.eu.org","si.eu.org","sk.eu.org","tr.eu.org","uk.eu.org","us.eu.org","eu-1.evennode.com","eu-2.evennode.com","eu-3.evennode.com","eu-4.evennode.com","us-1.evennode.com","us-2.evennode.com","us-3.evennode.com","us-4.evennode.com","twmail.cc","twmail.net","twmail.org","mymailer.com.tw","url.tw","apps.fbsbx.com","ru.net","adygeya.ru","bashkiria.ru","bir.ru","cbg.ru","com.ru","dagestan.ru","grozny.ru","kalmykia.ru","kustanai.ru","marine.ru","mordovia.ru","msk.ru","mytis.ru","nalchik.ru","nov.ru","pyatigorsk.ru","spb.ru","vladikavkaz.ru","vladimir.ru","abkhazia.su","adygeya.su","aktyubinsk.su","arkhangelsk.su","armenia.su","ashgabad.su","azerbaijan.su","balashov.su","bashkiria.su","bryansk.su","bukhara.su","chimkent.su","dagestan.su","east-kazakhstan.su","exnet.su","georgia.su","grozny.su","ivanovo.su","jambyl.su","kalmykia.su","kaluga.su","karacol.su","karaganda.su","karelia.su","khakassia.su","krasnodar.su","kurgan.su","kustanai.su","lenug.su","mangyshlak.su","mordovia.su","msk.su","murmansk.su","nalchik.su","navoi.su","north-kazakhstan.su","nov.su","obninsk.su","penza.su","pokrovsk.su","sochi.su","spb.su","tashkent.su","termez.su","togliatti.su","troitsk.su","tselinograd.su","tula.su","tuva.su","vladikavkaz.su","vladimir.su","vologda.su","channelsdvr.net","fastlylb.net","map.fastlylb.net","freetls.fastly.net","map.fastly.net","a.prod.fastly.net","global.prod.fastly.net","a.ssl.fastly.net","b.ssl.fastly.net","global.ssl.fastly.net","fastpanel.direct","fastvps-server.com","fhapp.xyz","fedorainfracloud.org","fedorapeople.org","cloud.fedoraproject.org","app.os.fedoraproject.org","app.os.stg.fedoraproject.org","filegear.me","firebaseapp.com","flynnhub.com","flynnhosting.net","freebox-os.com","freeboxos.com","fbx-os.fr","fbxos.fr","freebox-os.fr","freeboxos.fr","freedesktop.org","*.futurecms.at","*.ex.futurecms.at","*.in.futurecms.at","futurehosting.at","futuremailing.at","*.ex.ortsinfo.at","*.kunden.ortsinfo.at","*.statics.cloud","service.gov.uk","github.io","githubusercontent.com","gitlab.io","homeoffice.gov.uk","ro.im","shop.ro","goip.de","*.0emm.com","appspot.com","blogspot.ae","blogspot.al","blogspot.am","blogspot.ba","blogspot.be","blogspot.bg","blogspot.bj","blogspot.ca","blogspot.cf","blogspot.ch","blogspot.cl","blogspot.co.at","blogspot.co.id","blogspot.co.il","blogspot.co.ke","blogspot.co.nz","blogspot.co.uk","blogspot.co.za","blogspot.com","blogspot.com.ar","blogspot.com.au","blogspot.com.br","blogspot.com.by","blogspot.com.co","blogspot.com.cy","blogspot.com.ee","blogspot.com.eg","blogspot.com.es","blogspot.com.mt","blogspot.com.ng","blogspot.com.tr","blogspot.com.uy","blogspot.cv","blogspot.cz","blogspot.de","blogspot.dk","blogspot.fi","blogspot.fr","blogspot.gr","blogspot.hk","blogspot.hr","blogspot.hu","blogspot.ie","blogspot.in","blogspot.is","blogspot.it","blogspot.jp","blogspot.kr","blogspot.li","blogspot.lt","blogspot.lu","blogspot.md","blogspot.mk","blogspot.mr","blogspot.mx","blogspot.my","blogspot.nl","blogspot.no","blogspot.pe","blogspot.pt","blogspot.qa","blogspot.re","blogspot.ro","blogspot.rs","blogspot.ru","blogspot.se","blogspot.sg","blogspot.si","blogspot.sk","blogspot.sn","blogspot.td","blogspot.tw","blogspot.ug","blogspot.vn","cloudfunctions.net","cloud.goog","codespot.com","googleapis.com","googlecode.com","pagespeedmobilizer.com","publishproxy.com","withgoogle.com","withyoutube.com","hashbang.sh","hasura.app","hasura-app.io","hepforge.org","herokuapp.com","herokussl.com","myravendb.com","ravendb.community","ravendb.me","development.run","ravendb.run","moonscale.net","iki.fi","biz.at","info.at","info.cx","ac.leg.br","al.leg.br","am.leg.br","ap.leg.br","ba.leg.br","ce.leg.br","df.leg.br","es.leg.br","go.leg.br","ma.leg.br","mg.leg.br","ms.leg.br","mt.leg.br","pa.leg.br","pb.leg.br","pe.leg.br","pi.leg.br","pr.leg.br","rj.leg.br","rn.leg.br","ro.leg.br","rr.leg.br","rs.leg.br","sc.leg.br","se.leg.br","sp.leg.br","to.leg.br","pixolino.com","ipifony.net","mein-iserv.de","test-iserv.de","myjino.ru","*.hosting.myjino.ru","*.landing.myjino.ru","*.spectrum.myjino.ru","*.vps.myjino.ru","*.triton.zone","*.cns.joyent.com","js.org","keymachine.de","knightpoint.systems","co.krd","edu.krd","git-repos.de","lcube-server.de","svn-repos.de","app.lmpm.com","linkitools.space","linkyard.cloud","linkyard-cloud.ch","we.bs","uklugs.org","glug.org.uk","lug.org.uk","lugs.org.uk","barsy.bg","barsy.co.uk","barsyonline.co.uk","barsycenter.com","barsyonline.com","barsy.club","barsy.de","barsy.eu","barsy.in","barsy.info","barsy.io","barsy.me","barsy.menu","barsy.mobi","barsy.net","barsy.online","barsy.org","barsy.pro","barsy.pub","barsy.shop","barsy.site","barsy.support","barsy.uk","*.magentosite.cloud","mayfirst.info","mayfirst.org","hb.cldmail.ru","miniserver.com","memset.net","cloud.metacentrum.cz","custom.metacentrum.cz","flt.cloud.muni.cz","usr.cloud.muni.cz","meteorapp.com","eu.meteorapp.com","co.pl","azurecontainer.io","azurewebsites.net","azure-mobile.net","cloudapp.net","mozilla-iot.org","bmoattachments.org","net.ru","org.ru","pp.ru","bitballoon.com","netlify.com","4u.com","ngrok.io","nh-serv.co.uk","nfshost.com","dnsking.ch","mypi.co","n4t.co","001www.com","ddnslive.com","myiphost.com","forumz.info","16-b.it","32-b.it","64-b.it","soundcast.me","tcp4.me","dnsup.net","hicam.net","now-dns.net","ownip.net","vpndns.net","dynserv.org","now-dns.org","x443.pw","now-dns.top","ntdll.top","freeddns.us","crafting.xyz","zapto.xyz","nsupdate.info","nerdpol.ovh","blogsyte.com","brasilia.me","cable-modem.org","ciscofreak.com","collegefan.org","couchpotatofries.org","damnserver.com","ddns.me","ditchyourip.com","dnsfor.me","dnsiskinky.com","dvrcam.info","dynns.com","eating-organic.net","fantasyleague.cc","geekgalaxy.com","golffan.us","health-carereform.com","homesecuritymac.com","homesecuritypc.com","hopto.me","ilovecollege.info","loginto.me","mlbfan.org","mmafan.biz","myactivedirectory.com","mydissent.net","myeffect.net","mymediapc.net","mypsx.net","mysecuritycamera.com","mysecuritycamera.net","mysecuritycamera.org","net-freaks.com","nflfan.org","nhlfan.net","no-ip.ca","no-ip.co.uk","no-ip.net","noip.us","onthewifi.com","pgafan.net","point2this.com","pointto.us","privatizehealthinsurance.net","quicksytes.com","read-books.org","securitytactics.com","serveexchange.com","servehumour.com","servep2p.com","servesarcasm.com","stufftoread.com","ufcfan.org","unusualperson.com","workisboring.com","3utilities.com","bounceme.net","ddns.net","ddnsking.com","gotdns.ch","hopto.org","myftp.biz","myftp.org","myvnc.com","no-ip.biz","no-ip.info","no-ip.org","noip.me","redirectme.net","servebeer.com","serveblog.net","servecounterstrike.com","serveftp.com","servegame.com","servehalflife.com","servehttp.com","serveirc.com","serveminecraft.net","servemp3.com","servepics.com","servequake.com","sytes.net","webhop.me","zapto.org","stage.nodeart.io","nodum.co","nodum.io","pcloud.host","nyc.mn","nom.ae","nom.af","nom.ai","nom.al","nym.by","nym.bz","nom.cl","nom.gd","nom.ge","nom.gl","nym.gr","nom.gt","nym.gy","nom.hn","nym.ie","nom.im","nom.ke","nym.kz","nym.la","nym.lc","nom.li","nym.li","nym.lt","nym.lu","nym.me","nom.mk","nym.mn","nym.mx","nom.nu","nym.nz","nym.pe","nym.pt","nom.pw","nom.qa","nym.ro","nom.rs","nom.si","nym.sk","nom.st","nym.su","nym.sx","nom.tj","nym.tw","nom.ug","nom.uy","nom.vc","nom.vg","cya.gg","cloudycluster.net","nid.io","opencraft.hosting","operaunite.com","outsystemscloud.com","ownprovider.com","own.pm","ox.rs","oy.lc","pgfog.com","pagefrontapp.com","art.pl","gliwice.pl","krakow.pl","poznan.pl","wroc.pl","zakopane.pl","pantheonsite.io","gotpantheon.com","mypep.link","on-web.fr","*.platform.sh","*.platformsh.site","xen.prgmr.com","priv.at","protonet.io","chirurgiens-dentistes-en-france.fr","byen.site","ras.ru","qa2.com","dev-myqnapcloud.com","alpha-myqnapcloud.com","myqnapcloud.com","*.quipelements.com","vapor.cloud","vaporcloud.io","rackmaze.com","rackmaze.net","rhcloud.com","resindevice.io","devices.resinstaging.io","hzc.io","wellbeingzone.eu","ptplus.fit","wellbeingzone.co.uk","sandcats.io","logoip.de","logoip.com","schokokeks.net","scrysec.com","firewall-gateway.com","firewall-gateway.de","my-gateway.de","my-router.de","spdns.de","spdns.eu","firewall-gateway.net","my-firewall.org","myfirewall.org","spdns.org","*.s5y.io","*.sensiosite.cloud","biz.ua","co.ua","pp.ua","shiftedit.io","myshopblocks.com","1kapp.com","appchizi.com","applinzi.com","sinaapp.com","vipsinaapp.com","bounty-full.com","alpha.bounty-full.com","beta.bounty-full.com","static.land","dev.static.land","sites.static.land","apps.lair.io","*.stolos.io","spacekit.io","customer.speedpartner.de","storj.farm","utwente.io","temp-dns.com","diskstation.me","dscloud.biz","dscloud.me","dscloud.mobi","dsmynas.com","dsmynas.net","dsmynas.org","familyds.com","familyds.net","familyds.org","i234.me","myds.me","synology.me","vpnplus.to","taifun-dns.de","gda.pl","gdansk.pl","gdynia.pl","med.pl","sopot.pl","gwiddle.co.uk","cust.dev.thingdust.io","cust.disrec.thingdust.io","cust.prod.thingdust.io","cust.testing.thingdust.io","bloxcms.com","townnews-staging.com","12hp.at","2ix.at","4lima.at","lima-city.at","12hp.ch","2ix.ch","4lima.ch","lima-city.ch","trafficplex.cloud","de.cool","12hp.de","2ix.de","4lima.de","lima-city.de","1337.pictures","clan.rip","lima-city.rocks","webspace.rocks","lima.zone","*.transurl.be","*.transurl.eu","*.transurl.nl","tuxfamily.org","dd-dns.de","diskstation.eu","diskstation.org","dray-dns.de","draydns.de","dyn-vpn.de","dynvpn.de","mein-vigor.de","my-vigor.de","my-wan.de","syno-ds.de","synology-diskstation.de","synology-ds.de","uber.space","*.uberspace.de","hk.com","hk.org","ltd.hk","inc.hk","virtualuser.de","virtual-user.de","lib.de.us","2038.io","router.management","v-info.info","wedeploy.io","wedeploy.me","wedeploy.sh","remotewd.com","wmflabs.org","half.host","xnbay.com","u2.xnbay.com","u2-local.xnbay.com","cistron.nl","demon.nl","xs4all.space","official.academy","yolasite.com","ybo.faith","yombo.me","homelink.one","ybo.party","ybo.review","ybo.science","ybo.trade","nohost.me","noho.st","za.net","za.org","now.sh","zone.id"]
+
+/***/ }),
+/* 17 */
+/***/ (function(module, exports) {
+
+	/*!
+	 * Copyright (c) 2015, Salesforce.com, Inc.
+	 * All rights reserved.
+	 *
+	 * Redistribution and use in source and binary forms, with or without
+	 * modification, are permitted provided that the following conditions are met:
+	 *
+	 * 1. Redistributions of source code must retain the above copyright notice,
+	 * this list of conditions and the following disclaimer.
+	 *
+	 * 2. Redistributions in binary form must reproduce the above copyright notice,
+	 * this list of conditions and the following disclaimer in the documentation
+	 * and/or other materials provided with the distribution.
+	 *
+	 * 3. Neither the name of Salesforce.com nor the names of its contributors may
+	 * be used to endorse or promote products derived from this software without
+	 * specific prior written permission.
+	 *
+	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+	 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+	 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+	 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+	 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+	 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+	 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+	 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+	 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+	 * POSSIBILITY OF SUCH DAMAGE.
+	 */
+	'use strict';
+	/*jshint unused:false */
+
+	function Store() {
+	}
+	exports.Store = Store;
+
+	// Stores may be synchronous, but are still required to use a
+	// Continuation-Passing Style API.  The CookieJar itself will expose a "*Sync"
+	// API that converts from synchronous-callbacks to imperative style.
+	Store.prototype.synchronous = false;
+
+	Store.prototype.findCookie = function(domain, path, key, cb) {
+	  throw new Error('findCookie is not implemented');
+	};
+
+	Store.prototype.findCookies = function(domain, path, cb) {
+	  throw new Error('findCookies is not implemented');
+	};
+
+	Store.prototype.putCookie = function(cookie, cb) {
+	  throw new Error('putCookie is not implemented');
+	};
+
+	Store.prototype.updateCookie = function(oldCookie, newCookie, cb) {
+	  // recommended default implementation:
+	  // return this.putCookie(newCookie, cb);
+	  throw new Error('updateCookie is not implemented');
+	};
+
+	Store.prototype.removeCookie = function(domain, path, key, cb) {
+	  throw new Error('removeCookie is not implemented');
+	};
+
+	Store.prototype.removeCookies = function(domain, path, cb) {
+	  throw new Error('removeCookies is not implemented');
+	};
+
+	Store.prototype.getAllCookies = function(cb) {
+	  throw new Error('getAllCookies is not implemented (therefore jar cannot be serialized)');
+	};
+
+
+/***/ }),
+/* 18 */
+/***/ (function(module, exports, __webpack_require__) {
+
+	/*!
+	 * Copyright (c) 2015, Salesforce.com, Inc.
+	 * All rights reserved.
+	 *
+	 * Redistribution and use in source and binary forms, with or without
+	 * modification, are permitted provided that the following conditions are met:
+	 *
+	 * 1. Redistributions of source code must retain the above copyright notice,
+	 * this list of conditions and the following disclaimer.
+	 *
+	 * 2. Redistributions in binary form must reproduce the above copyright notice,
+	 * this list of conditions and the following disclaimer in the documentation
+	 * and/or other materials provided with the distribution.
+	 *
+	 * 3. Neither the name of Salesforce.com nor the names of its contributors may
+	 * be used to endorse or promote products derived from this software without
+	 * specific prior written permission.
+	 *
+	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+	 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+	 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+	 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+	 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+	 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+	 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+	 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+	 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+	 * POSSIBILITY OF SUCH DAMAGE.
+	 */
+	'use strict';
+	var Store = __webpack_require__(17).Store;
+	var permuteDomain = __webpack_require__(19).permuteDomain;
+	var pathMatch = __webpack_require__(20).pathMatch;
+	var util = __webpack_require__(9);
+
+	function MemoryCookieStore() {
+	  Store.call(this);
+	  this.idx = {};
+	}
+	util.inherits(MemoryCookieStore, Store);
+	exports.MemoryCookieStore = MemoryCookieStore;
+	MemoryCookieStore.prototype.idx = null;
+
+	// Since it's just a struct in RAM, this Store is synchronous
+	MemoryCookieStore.prototype.synchronous = true;
+
+	// force a default depth:
+	MemoryCookieStore.prototype.inspect = function() {
+	  return "{ idx: "+util.inspect(this.idx, false, 2)+' }';
+	};
+
+	// Use the new custom inspection symbol to add the custom inspect function if
+	// available.
+	if (util.inspect.custom) {
+	  MemoryCookieStore.prototype[util.inspect.custom] = MemoryCookieStore.prototype.inspect;
+	}
+
+	MemoryCookieStore.prototype.findCookie = function(domain, path, key, cb) {
+	  if (!this.idx[domain]) {
+	    return cb(null,undefined);
+	  }
+	  if (!this.idx[domain][path]) {
+	    return cb(null,undefined);
+	  }
+	  return cb(null,this.idx[domain][path][key]||null);
+	};
+
+	MemoryCookieStore.prototype.findCookies = function(domain, path, cb) {
+	  var results = [];
+	  if (!domain) {
+	    return cb(null,[]);
+	  }
+
+	  var pathMatcher;
+	  if (!path) {
+	    // null means "all paths"
+	    pathMatcher = function matchAll(domainIndex) {
+	      for (var curPath in domainIndex) {
+	        var pathIndex = domainIndex[curPath];
+	        for (var key in pathIndex) {
+	          results.push(pathIndex[key]);
+	        }
+	      }
+	    };
+
+	  } else {
+	    pathMatcher = function matchRFC(domainIndex) {
+	       //NOTE: we should use path-match algorithm from S5.1.4 here
+	       //(see : https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/canonical_cookie.cc#L299)
+	       Object.keys(domainIndex).forEach(function (cookiePath) {
+	         if (pathMatch(path, cookiePath)) {
+	           var pathIndex = domainIndex[cookiePath];
+
+	           for (var key in pathIndex) {
+	             results.push(pathIndex[key]);
+	           }
+	         }
+	       });
+	     };
+	  }
+
+	  var domains = permuteDomain(domain) || [domain];
+	  var idx = this.idx;
+	  domains.forEach(function(curDomain) {
+	    var domainIndex = idx[curDomain];
+	    if (!domainIndex) {
+	      return;
+	    }
+	    pathMatcher(domainIndex);
+	  });
+
+	  cb(null,results);
+	};
+
+	MemoryCookieStore.prototype.putCookie = function(cookie, cb) {
+	  if (!this.idx[cookie.domain]) {
+	    this.idx[cookie.domain] = {};
+	  }
+	  if (!this.idx[cookie.domain][cookie.path]) {
+	    this.idx[cookie.domain][cookie.path] = {};
+	  }
+	  this.idx[cookie.domain][cookie.path][cookie.key] = cookie;
+	  cb(null);
+	};
+
+	MemoryCookieStore.prototype.updateCookie = function(oldCookie, newCookie, cb) {
+	  // updateCookie() may avoid updating cookies that are identical.  For example,
+	  // lastAccessed may not be important to some stores and an equality
+	  // comparison could exclude that field.
+	  this.putCookie(newCookie,cb);
+	};
+
+	MemoryCookieStore.prototype.removeCookie = function(domain, path, key, cb) {
+	  if (this.idx[domain] && this.idx[domain][path] && this.idx[domain][path][key]) {
+	    delete this.idx[domain][path][key];
+	  }
+	  cb(null);
+	};
+
+	MemoryCookieStore.prototype.removeCookies = function(domain, path, cb) {
+	  if (this.idx[domain]) {
+	    if (path) {
+	      delete this.idx[domain][path];
+	    } else {
+	      delete this.idx[domain];
+	    }
+	  }
+	  return cb(null);
+	};
+
+	MemoryCookieStore.prototype.getAllCookies = function(cb) {
+	  var cookies = [];
+	  var idx = this.idx;
+
+	  var domains = Object.keys(idx);
+	  domains.forEach(function(domain) {
+	    var paths = Object.keys(idx[domain]);
+	    paths.forEach(function(path) {
+	      var keys = Object.keys(idx[domain][path]);
+	      keys.forEach(function(key) {
+	        if (key !== null) {
+	          cookies.push(idx[domain][path][key]);
+	        }
+	      });
+	    });
+	  });
+
+	  // Sort by creationIndex so deserializing retains the creation order.
+	  // When implementing your own store, this SHOULD retain the order too
+	  cookies.sort(function(a,b) {
+	    return (a.creationIndex||0) - (b.creationIndex||0);
+	  });
+
+	  cb(null, cookies);
+	};
+
+
+/***/ }),
+/* 19 */
+/***/ (function(module, exports, __webpack_require__) {
+
+	/*!
+	 * Copyright (c) 2015, Salesforce.com, Inc.
+	 * All rights reserved.
+	 *
+	 * Redistribution and use in source and binary forms, with or without
+	 * modification, are permitted provided that the following conditions are met:
+	 *
+	 * 1. Redistributions of source code must retain the above copyright notice,
+	 * this list of conditions and the following disclaimer.
+	 *
+	 * 2. Redistributions in binary form must reproduce the above copyright notice,
+	 * this list of conditions and the following disclaimer in the documentation
+	 * and/or other materials provided with the distribution.
+	 *
+	 * 3. Neither the name of Salesforce.com nor the names of its contributors may
+	 * be used to endorse or promote products derived from this software without
+	 * specific prior written permission.
+	 *
+	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+	 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+	 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+	 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+	 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+	 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+	 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+	 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+	 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+	 * POSSIBILITY OF SUCH DAMAGE.
+	 */
+	"use strict";
+	var pubsuffix = __webpack_require__(13);
+
+	// Gives the permutation of all possible domainMatch()es of a given domain. The
+	// array is in shortest-to-longest order.  Handy for indexing.
+	function permuteDomain (domain) {
+	  var pubSuf = pubsuffix.getPublicSuffix(domain);
+	  if (!pubSuf) {
+	    return null;
+	  }
+	  if (pubSuf == domain) {
+	    return [domain];
+	  }
+
+	  var prefix = domain.slice(0, -(pubSuf.length + 1)); // ".example.com"
+	  var parts = prefix.split('.').reverse();
+	  var cur = pubSuf;
+	  var permutations = [cur];
+	  while (parts.length) {
+	    cur = parts.shift() + '.' + cur;
+	    permutations.push(cur);
+	  }
+	  return permutations;
+	}
+
+	exports.permuteDomain = permuteDomain;
+
+
+/***/ }),
+/* 20 */
+/***/ (function(module, exports) {
+
+	/*!
+	 * Copyright (c) 2015, Salesforce.com, Inc.
+	 * All rights reserved.
+	 *
+	 * Redistribution and use in source and binary forms, with or without
+	 * modification, are permitted provided that the following conditions are met:
+	 *
+	 * 1. Redistributions of source code must retain the above copyright notice,
+	 * this list of conditions and the following disclaimer.
+	 *
+	 * 2. Redistributions in binary form must reproduce the above copyright notice,
+	 * this list of conditions and the following disclaimer in the documentation
+	 * and/or other materials provided with the distribution.
+	 *
+	 * 3. Neither the name of Salesforce.com nor the names of its contributors may
+	 * be used to endorse or promote products derived from this software without
+	 * specific prior written permission.
+	 *
+	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+	 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+	 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+	 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+	 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+	 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+	 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+	 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+	 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+	 * POSSIBILITY OF SUCH DAMAGE.
+	 */
+	"use strict";
+	/*
+	 * "A request-path path-matches a given cookie-path if at least one of the
+	 * following conditions holds:"
+	 */
+	function pathMatch (reqPath, cookiePath) {
+	  // "o  The cookie-path and the request-path are identical."
+	  if (cookiePath === reqPath) {
+	    return true;
+	  }
+
+	  var idx = reqPath.indexOf(cookiePath);
+	  if (idx === 0) {
+	    // "o  The cookie-path is a prefix of the request-path, and the last
+	    // character of the cookie-path is %x2F ("/")."
+	    if (cookiePath.substr(-1) === "/") {
+	      return true;
+	    }
+
+	    // " o  The cookie-path is a prefix of the request-path, and the first
+	    // character of the request-path that is not included in the cookie- path
+	    // is a %x2F ("/") character."
+	    if (reqPath.substr(cookiePath.length, 1) === "/") {
+	      return true;
+	    }
+	  }
+
+	  return false;
+	}
+
+	exports.pathMatch = pathMatch;
+
+
+/***/ }),
+/* 21 */
+/***/ (function(module, exports) {
+
+	module.exports = {"_from":"tough-cookie@2.4.3","_id":"tough-cookie@2.4.3","_inBundle":false,"_integrity":"sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==","_location":"/tough-cookie","_phantomChildren":{},"_requested":{"type":"version","registry":true,"raw":"tough-cookie@2.4.3","name":"tough-cookie","escapedName":"tough-cookie","rawSpec":"2.4.3","saveSpec":null,"fetchSpec":"2.4.3"},"_requiredBy":["#DEV:/"],"_resolved":"https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz","_shasum":"53f36da3f47783b0925afa06ff9f3b165280f781","_spec":"tough-cookie@2.4.3","_where":"/Users/sefa/Entwicklung/umd-tough-cookie","author":{"name":"Jeremy Stashewsky","email":"jstash@gmail.com"},"bugs":{"url":"https://github.com/salesforce/tough-cookie/issues"},"bundleDependencies":false,"contributors":[{"name":"Alexander Savin"},{"name":"Ian Livingstone"},{"name":"Ivan Nikulin"},{"name":"Lalit Kapoor"},{"name":"Sam Thompson"},{"name":"Sebastian Mayr"}],"dependencies":{"psl":"^1.1.24","punycode":"^1.4.1"},"deprecated":false,"description":"RFC6265 Cookies and Cookie Jar for node.js","devDependencies":{"async":"^1.4.2","nyc":"^11.6.0","string.prototype.repeat":"^0.2.0","vows":"^0.8.1"},"engines":{"node":">=0.8"},"files":["lib"],"homepage":"https://github.com/salesforce/tough-cookie","keywords":["HTTP","cookie","cookies","set-cookie","cookiejar","jar","RFC6265","RFC2965"],"license":"BSD-3-Clause","main":"./lib/cookie","name":"tough-cookie","repository":{"type":"git","url":"git://github.com/salesforce/tough-cookie.git"},"scripts":{"cover":"nyc --reporter=lcov --reporter=html vows test/*_test.js","test":"vows test/*_test.js"},"version":"2.4.3"}
+
+/***/ })
+/******/ ])
+});
+;
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/url-util.js b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/url-util.js
new file mode 100644
index 0000000..2e92be6
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-advanced-http/www/url-util.js
@@ -0,0 +1,105 @@
+cordova.define("cordova-plugin-advanced-http.url-util", function(require, exports, module) { module.exports = function init(jsUtil) {
+  return {
+    parseUrl: parseUrl,
+    appendQueryParamsString: appendQueryParamsString,
+    serializeQueryParams: serializeQueryParams
+  }
+
+  function parseUrl(url) {
+    var match = url.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/);
+
+    return match && {
+        protocol: match[1],
+        host: match[2],
+        hostname: match[3],
+        port: match[4] || '',
+        pathname: match[5],
+        search: match[6],
+        hash: match[7]
+    }
+  }
+
+  function appendQueryParamsString(url, params) {
+    if (!url.length || !params.length) {
+      return url;
+    }
+
+    var parsed = parseUrl(url);
+
+    return parsed.protocol
+      + '//'
+      + parsed.host
+      + parsed.pathname
+      + (parsed.search.length ? parsed.search + '&' + params : '?' + params)
+      + parsed.hash;
+  }
+
+  function serializeQueryParams(params, encode) {
+    return serializeObject('', params, encode);
+  }
+
+  function serializeObject(parentKey, object, encode) {
+    var parts = [];
+
+    for (var key in object) {
+      if (!object.hasOwnProperty(key)) {
+        continue;
+      }
+
+      var identifier = parentKey.length ? parentKey + '[' + key + ']' : key;
+
+      if (jsUtil.getTypeOf(object[key]) === 'Array') {
+        parts.push(serializeArray(identifier, object[key], encode));
+        continue;
+      } else if (jsUtil.getTypeOf(object[key]) === 'Object') {
+        parts.push(serializeObject(identifier, object[key], encode));
+        continue;
+      }
+
+      parts.push(serializeIdentifier(parentKey, key, encode) + '=' + serializeValue(object[key], encode));
+    }
+
+    return parts.join('&');
+  }
+
+  function serializeArray(parentKey, array, encode) {
+    var parts = [];
+
+    for (var i = 0; i < array.length; ++i) {
+      if (jsUtil.getTypeOf(array[i]) === 'Array') {
+        parts.push(serializeArray(parentKey + '[]', array[i], encode));
+        continue;
+      } else if (jsUtil.getTypeOf(array[i]) === 'Object') {
+        parts.push(serializeObject(parentKey + '[]' + array[i], encode));
+        continue;
+      }
+
+      parts.push(serializeIdentifier(parentKey, '', encode) + '=' + serializeValue(array[i], encode));
+    }
+
+    return parts.join('&');
+  }
+
+  function serializeIdentifier(parentKey, key, encode) {
+    if (!parentKey.length) {
+      return encode ? encodeURIComponent(key) : key;
+    }
+
+    if (encode) {
+      return encodeURIComponent(parentKey) + '[' + encodeURIComponent(key) + ']';
+    } else {
+      return parentKey + '[' + key + ']';
+    }
+  }
+
+  function serializeValue(value, encode) {
+    if (encode) {
+      return encodeURIComponent(value);
+    } else {
+      return value;
+    }
+  }
+};
+
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-camera/src/browser/CameraProxy.js b/platforms/browser/platform_www/plugins/cordova-plugin-camera/src/browser/CameraProxy.js
new file mode 100644
index 0000000..50d8bff
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-camera/src/browser/CameraProxy.js
@@ -0,0 +1,125 @@
+cordova.define("cordova-plugin-camera.CameraProxy", function(require, exports, module) { /*
+ *
+ * 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 HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
+
+function takePicture (success, error, opts) {
+    if (opts && opts[2] === 1) {
+        capture(success, error, opts);
+    } else {
+        var input = document.createElement('input');
+        input.style.position = 'relative';
+        input.style.zIndex = HIGHEST_POSSIBLE_Z_INDEX;
+        input.className = 'cordova-camera-select';
+        input.type = 'file';
+        input.name = 'files[]';
+
+        input.onchange = function (inputEvent) {
+            var reader = new FileReader(); /* eslint no-undef : 0 */
+            reader.onload = function (readerEvent) {
+                input.parentNode.removeChild(input);
+
+                var imageData = readerEvent.target.result;
+
+                return success(imageData.substr(imageData.indexOf(',') + 1));
+            };
+
+            reader.readAsDataURL(inputEvent.target.files[0]);
+        };
+
+        document.body.appendChild(input);
+    }
+}
+
+function capture (success, errorCallback, opts) {
+    var localMediaStream;
+    var targetWidth = opts[3];
+    var targetHeight = opts[4];
+
+    targetWidth = targetWidth === -1 ? 320 : targetWidth;
+    targetHeight = targetHeight === -1 ? 240 : targetHeight;
+
+    var video = document.createElement('video');
+    var button = document.createElement('button');
+    var parent = document.createElement('div');
+    parent.style.position = 'relative';
+    parent.style.zIndex = HIGHEST_POSSIBLE_Z_INDEX;
+    parent.className = 'cordova-camera-capture';
+    parent.appendChild(video);
+    parent.appendChild(button);
+
+    video.width = targetWidth;
+    video.height = targetHeight;
+    button.innerHTML = 'Capture!';
+
+    button.onclick = function () {
+        // create a canvas and capture a frame from video stream
+        var canvas = document.createElement('canvas');
+        canvas.width = targetWidth;
+        canvas.height = targetHeight;
+        canvas.getContext('2d').drawImage(video, 0, 0, targetWidth, targetHeight);
+
+        // convert image stored in canvas to base64 encoded image
+        var imageData = canvas.toDataURL('image/png');
+        imageData = imageData.replace('data:image/png;base64,', '');
+
+        // stop video stream, remove video and button.
+        // Note that MediaStream.stop() is deprecated as of Chrome 47.
+        if (localMediaStream.stop) {
+            localMediaStream.stop();
+        } else {
+            localMediaStream.getTracks().forEach(function (track) {
+                track.stop();
+            });
+        }
+        parent.parentNode.removeChild(parent);
+
+        return success(imageData);
+    };
+
+    navigator.getUserMedia = navigator.getUserMedia ||
+                             navigator.webkitGetUserMedia ||
+                             navigator.mozGetUserMedia ||
+                             navigator.msGetUserMedia;
+
+    var successCallback = function (stream) {
+        localMediaStream = stream;
+        video.src = window.URL.createObjectURL(localMediaStream);
+        video.play();
+
+        document.body.appendChild(parent);
+    };
+
+    if (navigator.getUserMedia) {
+        navigator.getUserMedia({video: true, audio: true}, successCallback, errorCallback);
+    } else {
+        alert('Browser does not support camera :(');
+    }
+}
+
+module.exports = {
+    takePicture: takePicture,
+    cleanup: function () {}
+};
+
+require('cordova/exec/proxy').add('Camera', module.exports);
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-camera/www/Camera.js b/platforms/browser/platform_www/plugins/cordova-plugin-camera/www/Camera.js
new file mode 100644
index 0000000..3959553
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-camera/www/Camera.js
@@ -0,0 +1,187 @@
+cordova.define("cordova-plugin-camera.camera", function(require, exports, module) { /*
+ *
+ * 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');
+var exec = require('cordova/exec');
+var Camera = require('./Camera');
+// XXX: commented out
+// CameraPopoverHandle = require('./CameraPopoverHandle');
+
+/**
+ * @namespace navigator
+ */
+
+/**
+ * @exports camera
+ */
+var cameraExport = {};
+
+// Tack on the Camera Constants to the base camera plugin.
+for (var key in Camera) {
+    cameraExport[key] = Camera[key];
+}
+
+/**
+ * Callback function that provides an error message.
+ * @callback module:camera.onError
+ * @param {string} message - The message is provided by the device's native code.
+ */
+
+/**
+ * Callback function that provides the image data.
+ * @callback module:camera.onSuccess
+ * @param {string} imageData - Base64 encoding of the image data, _or_ the image file URI, depending on [`cameraOptions`]{@link module:camera.CameraOptions} in effect.
+ * @example
+ * // Show image
+ * //
+ * function cameraCallback(imageData) {
+ *    var image = document.getElementById('myImage');
+ *    image.src = "data:image/jpeg;base64," + imageData;
+ * }
+ */
+
+/**
+ * Optional parameters to customize the camera settings.
+ * * [Quirks](#CameraOptions-quirks)
+ * @typedef module:camera.CameraOptions
+ * @type {Object}
+ * @property {number} [quality=50] - Quality of the saved image, expressed as a range of 0-100, where 100 is typically full resolution with no loss from file compression. (Note that information about the camera's resolution is unavailable.)
+ * @property {module:Camera.DestinationType} [destinationType=FILE_URI] - Choose the format of the return value.
+ * @property {module:Camera.PictureSourceType} [sourceType=CAMERA] - Set the source of the picture.
+ * @property {Boolean} [allowEdit=false] - Allow simple editing of image before selection.
+ * @property {module:Camera.EncodingType} [encodingType=JPEG] - Choose the  returned image file's encoding.
+ * @property {number} [targetWidth] - Width in pixels to scale image. Must be used with `targetHeight`. Aspect ratio remains constant.
+ * @property {number} [targetHeight] - Height in pixels to scale image. Must be used with `targetWidth`. Aspect ratio remains constant.
+ * @property {module:Camera.MediaType} [mediaType=PICTURE] - Set the type of media to select from.  Only works when `PictureSourceType` is `PHOTOLIBRARY` or `SAVEDPHOTOALBUM`.
+ * @property {Boolean} [correctOrientation] - Rotate the image to correct for the orientation of the device during capture.
+ * @property {Boolean} [saveToPhotoAlbum] - Save the image to the photo album on the device after capture.
+ * @property {module:CameraPopoverOptions} [popoverOptions] - iOS-only options that specify popover location in iPad.
+ * @property {module:Camera.Direction} [cameraDirection=BACK] - Choose the camera to use (front- or back-facing).
+ */
+
+/**
+ * @description Takes a photo using the camera, or retrieves a photo from the device's
+ * image gallery.  The image is passed to the success callback as a
+ * Base64-encoded `String`, or as the URI for the image file.
+ *
+ * The `camera.getPicture` function opens the device's default camera
+ * application that allows users to snap pictures by default - this behavior occurs,
+ * when `Camera.sourceType` equals [`Camera.PictureSourceType.CAMERA`]{@link module:Camera.PictureSourceType}.
+ * Once the user snaps the photo, the camera application closes and the application is restored.
+ *
+ * If `Camera.sourceType` is `Camera.PictureSourceType.PHOTOLIBRARY` or
+ * `Camera.PictureSourceType.SAVEDPHOTOALBUM`, then a dialog displays
+ * that allows users to select an existing image.
+ *
+ * The return value is sent to the [`cameraSuccess`]{@link module:camera.onSuccess} callback function, in
+ * one of the following formats, depending on the specified
+ * `cameraOptions`:
+ *
+ * - A `String` containing the Base64-encoded photo image.
+ * - A `String` representing the image file location on local storage (default).
+ *
+ * You can do whatever you want with the encoded image or URI, for
+ * example:
+ *
+ * - Render the image in an `<img>` tag, as in the example below
+ * - Save the data locally (`LocalStorage`, [Lawnchair](http://brianleroux.github.com/lawnchair/), etc.)
+ * - Post the data to a remote server
+ *
+ * __NOTE__: Photo resolution on newer devices is quite good. Photos
+ * selected from the device's gallery are not downscaled to a lower
+ * quality, even if a `quality` parameter is specified.  To avoid common
+ * memory problems, set `Camera.destinationType` to `FILE_URI` rather
+ * than `DATA_URL`.
+ *
+ * __Supported Platforms__
+ *
+ * - Android
+ * - BlackBerry
+ * - Browser
+ * - Firefox
+ * - FireOS
+ * - iOS
+ * - Windows
+ * - WP8
+ * - Ubuntu
+ *
+ * More examples [here](#camera-getPicture-examples). Quirks [here](#camera-getPicture-quirks).
+ *
+ * @example
+ * navigator.camera.getPicture(cameraSuccess, cameraError, cameraOptions);
+ * @param {module:camera.onSuccess} successCallback
+ * @param {module:camera.onError} errorCallback
+ * @param {module:camera.CameraOptions} options CameraOptions
+ */
+cameraExport.getPicture = function (successCallback, errorCallback, options) {
+    argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
+    options = options || {};
+    var getValue = argscheck.getValue;
+
+    var quality = getValue(options.quality, 50);
+    var destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
+    var sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
+    var targetWidth = getValue(options.targetWidth, -1);
+    var targetHeight = getValue(options.targetHeight, -1);
+    var encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG);
+    var mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE);
+    var allowEdit = !!options.allowEdit;
+    var correctOrientation = !!options.correctOrientation;
+    var saveToPhotoAlbum = !!options.saveToPhotoAlbum;
+    var popoverOptions = getValue(options.popoverOptions, null);
+    var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
+
+    var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
+        mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
+
+    exec(successCallback, errorCallback, 'Camera', 'takePicture', args);
+    // XXX: commented out
+    // return new CameraPopoverHandle();
+};
+
+/**
+ * Removes intermediate image files that are kept in temporary storage
+ * after calling [`camera.getPicture`]{@link module:camera.getPicture}. Applies only when the value of
+ * `Camera.sourceType` equals `Camera.PictureSourceType.CAMERA` and the
+ * `Camera.destinationType` equals `Camera.DestinationType.FILE_URI`.
+ *
+ * __Supported Platforms__
+ *
+ * - iOS
+ *
+ * @example
+ * navigator.camera.cleanup(onSuccess, onFail);
+ *
+ * function onSuccess() {
+ *     console.log("Camera cleanup success.")
+ * }
+ *
+ * function onFail(message) {
+ *     alert('Failed because: ' + message);
+ * }
+ */
+cameraExport.cleanup = function (successCallback, errorCallback) {
+    exec(successCallback, errorCallback, 'Camera', 'cleanup', []);
+};
+
+module.exports = cameraExport;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-camera/www/CameraConstants.js b/platforms/browser/platform_www/plugins/cordova-plugin-camera/www/CameraConstants.js
new file mode 100644
index 0000000..c271df6
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-camera/www/CameraConstants.js
@@ -0,0 +1,103 @@
+cordova.define("cordova-plugin-camera.Camera", function(require, exports, module) { /*
+ *
+ * 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 Camera
+ */
+module.exports = {
+    /**
+     * @description
+     * Defines the output format of `Camera.getPicture` call.
+     * _Note:_ On iOS passing `DestinationType.NATIVE_URI` along with
+     * `PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM` will
+     * disable any image modifications (resize, quality change, cropping, etc.) due
+     * to implementation specific.
+     *
+     * @enum {number}
+     */
+    DestinationType: {
+        /** Return base64 encoded string. DATA_URL can be very memory intensive and cause app crashes or out of memory errors. Use FILE_URI or NATIVE_URI if possible */
+        DATA_URL: 0,
+        /** Return file uri (content://media/external/images/media/2 for Android) */
+        FILE_URI: 1,
+        /** Return native uri (eg. asset-library://... for iOS) */
+        NATIVE_URI: 2
+    },
+    /**
+     * @enum {number}
+     */
+    EncodingType: {
+        /** Return JPEG encoded image */
+        JPEG: 0,
+        /** Return PNG encoded image */
+        PNG: 1
+    },
+    /**
+     * @enum {number}
+     */
+    MediaType: {
+        /** Allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType */
+        PICTURE: 0,
+        /** Allow selection of video only, ONLY RETURNS URL */
+        VIDEO: 1,
+        /** Allow selection from all media types */
+        ALLMEDIA: 2
+    },
+    /**
+     * @description
+     * Defines the output format of `Camera.getPicture` call.
+     * _Note:_ On iOS passing `PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM`
+     * along with `DestinationType.NATIVE_URI` will disable any image modifications (resize, quality
+     * change, cropping, etc.) due to implementation specific.
+     *
+     * @enum {number}
+     */
+    PictureSourceType: {
+        /** Choose image from the device's photo library (same as SAVEDPHOTOALBUM for Android) */
+        PHOTOLIBRARY: 0,
+        /** Take picture from camera */
+        CAMERA: 1,
+        /** Choose image only from the device's Camera Roll album (same as PHOTOLIBRARY for Android) */
+        SAVEDPHOTOALBUM: 2
+    },
+    /**
+     * Matches iOS UIPopoverArrowDirection constants to specify arrow location on popover.
+     * @enum {number}
+     */
+    PopoverArrowDirection: {
+        ARROW_UP: 1,
+        ARROW_DOWN: 2,
+        ARROW_LEFT: 4,
+        ARROW_RIGHT: 8,
+        ARROW_ANY: 15
+    },
+    /**
+     * @enum {number}
+     */
+    Direction: {
+        /** Use the back-facing camera */
+        BACK: 0,
+        /** Use the front-facing camera */
+        FRONT: 1
+    }
+};
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-camera/www/CameraPopoverOptions.js b/platforms/browser/platform_www/plugins/cordova-plugin-camera/www/CameraPopoverOptions.js
new file mode 100644
index 0000000..9b42e8b
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-camera/www/CameraPopoverOptions.js
@@ -0,0 +1,54 @@
+cordova.define("cordova-plugin-camera.CameraPopoverOptions", function(require, exports, module) { /*
+ *
+ * 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 Camera = require('./Camera');
+
+/**
+ * @namespace navigator
+ */
+
+/**
+ * iOS-only parameters that specify the anchor element location and arrow
+ * direction of the popover when selecting images from an iPad's library
+ * or album.
+ * Note that the size of the popover may change to adjust to the
+ * direction of the arrow and orientation of the screen.  Make sure to
+ * account for orientation changes when specifying the anchor element
+ * location.
+ * @module CameraPopoverOptions
+ * @param {Number} [x=0] - x pixel coordinate of screen element onto which to anchor the popover.
+ * @param {Number} [y=32] - y pixel coordinate of screen element onto which to anchor the popover.
+ * @param {Number} [width=320] - width, in pixels, of the screen element onto which to anchor the popover.
+ * @param {Number} [height=480] - height, in pixels, of the screen element onto which to anchor the popover.
+ * @param {module:Camera.PopoverArrowDirection} [arrowDir=ARROW_ANY] - Direction the arrow on the popover should point.
+ */
+var CameraPopoverOptions = function (x, y, width, height, arrowDir) {
+    // information of rectangle that popover should be anchored to
+    this.x = x || 0;
+    this.y = y || 32;
+    this.width = width || 320;
+    this.height = height || 480;
+    this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY;
+};
+
+module.exports = CameraPopoverOptions;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-device/src/browser/DeviceProxy.js b/platforms/browser/platform_www/plugins/cordova-plugin-device/src/browser/DeviceProxy.js
new file mode 100644
index 0000000..a4998fe
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-device/src/browser/DeviceProxy.js
@@ -0,0 +1,86 @@
+cordova.define("cordova-plugin-device.DeviceProxy", function(require, exports, module) { /*
+ *
+ * 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 browser = require('cordova/platform');
+
+function getPlatform () {
+    return 'browser';
+}
+
+function getModel () {
+    return getBrowserInfo(true);
+}
+
+function getVersion () {
+    return getBrowserInfo(false);
+}
+
+function getBrowserInfo (getModel) {
+    var userAgent = navigator.userAgent;
+    var returnVal = '';
+    var offset;
+
+    if ((offset = userAgent.indexOf('Edge')) !== -1) {
+        returnVal = (getModel) ? 'Edge' : userAgent.substring(offset + 5);
+    } else if ((offset = userAgent.indexOf('Chrome')) !== -1) {
+        returnVal = (getModel) ? 'Chrome' : userAgent.substring(offset + 7);
+    } else if ((offset = userAgent.indexOf('Safari')) !== -1) {
+        if (getModel) {
+            returnVal = 'Safari';
+        } else {
+            returnVal = userAgent.substring(offset + 7);
+
+            if ((offset = userAgent.indexOf('Version')) !== -1) {
+                returnVal = userAgent.substring(offset + 8);
+            }
+        }
+    } else if ((offset = userAgent.indexOf('Firefox')) !== -1) {
+        returnVal = (getModel) ? 'Firefox' : userAgent.substring(offset + 8);
+    } else if ((offset = userAgent.indexOf('MSIE')) !== -1) {
+        returnVal = (getModel) ? 'MSIE' : userAgent.substring(offset + 5);
+    } else if ((offset = userAgent.indexOf('Trident')) !== -1) {
+        returnVal = (getModel) ? 'MSIE' : '11';
+    }
+
+    if ((offset = returnVal.indexOf(';')) !== -1 || (offset = returnVal.indexOf(' ')) !== -1) {
+        returnVal = returnVal.substring(0, offset);
+    }
+
+    return returnVal;
+}
+
+module.exports = {
+    getDeviceInfo: function (success, error) {
+        setTimeout(function () {
+            success({
+                cordova: browser.cordovaVersion,
+                platform: getPlatform(),
+                model: getModel(),
+                version: getVersion(),
+                uuid: null,
+                isVirtual: false
+            });
+        }, 0);
+    }
+};
+
+require('cordova/exec/proxy').add('Device', module.exports);
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-device/www/device.js b/platforms/browser/platform_www/plugins/cordova-plugin-device/www/device.js
new file mode 100644
index 0000000..059351d
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-device/www/device.js
@@ -0,0 +1,85 @@
+cordova.define("cordova-plugin-device.device", function(require, exports, module) { /*
+ *
+ * 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');
+var channel = require('cordova/channel');
+var utils = require('cordova/utils');
+var exec = require('cordova/exec');
+var cordova = require('cordova');
+
+channel.createSticky('onCordovaInfoReady');
+// Tell cordova channel to wait on the CordovaInfoReady event
+channel.waitForInitialization('onCordovaInfoReady');
+
+/**
+ * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
+ * phone, etc.
+ * @constructor
+ */
+function Device () {
+    this.available = false;
+    this.platform = null;
+    this.version = null;
+    this.uuid = null;
+    this.cordova = null;
+    this.model = null;
+    this.manufacturer = null;
+    this.isVirtual = null;
+    this.serial = null;
+
+    var me = this;
+
+    channel.onCordovaReady.subscribe(function () {
+        me.getInfo(function (info) {
+            // ignoring info.cordova returning from native, we should use value from cordova.version defined in cordova.js
+            // TODO: CB-5105 native implementations should not return info.cordova
+            var buildLabel = cordova.version;
+            me.available = true;
+            me.platform = info.platform;
+            me.version = info.version;
+            me.uuid = info.uuid;
+            me.cordova = buildLabel;
+            me.model = info.model;
+            me.isVirtual = info.isVirtual;
+            me.manufacturer = info.manufacturer || 'unknown';
+            me.serial = info.serial || 'unknown';
+            channel.onCordovaInfoReady.fire();
+        }, function (e) {
+            me.available = false;
+            utils.alert('[ERROR] Error initializing Cordova: ' + e);
+        });
+    });
+}
+
+/**
+ * Get device info
+ *
+ * @param {Function} successCallback The function to call when the heading data is available
+ * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
+ */
+Device.prototype.getInfo = function (successCallback, errorCallback) {
+    argscheck.checkArgs('fF', 'Device.getInfo', arguments);
+    exec(successCallback, errorCallback, 'Device', 'getDeviceInfo', []);
+};
+
+module.exports = new Device();
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/src/browser/FileProxy.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/src/browser/FileProxy.js
new file mode 100644
index 0000000..d7b0541
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/src/browser/FileProxy.js
@@ -0,0 +1,986 @@
+cordova.define("cordova-plugin-file.browserFileProxy", function(require, exports, module) { /*
+ *
+ * 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 () {
+    /* global require, exports, module */
+    /* global FILESYSTEM_PREFIX */
+    /* global IDBKeyRange */
+
+    /* Heavily based on https://github.com/ebidel/idb.filesystem.js */
+
+    // For chrome we don't need to implement proxy methods
+    // All functionality can be accessed natively.
+    if (require('./isChrome')()) {
+        var pathsPrefix = {
+            // Read-only directory where the application is installed.
+            applicationDirectory: location.origin + '/', // eslint-disable-line no-undef
+            // Where to put app-specific data files.
+            dataDirectory: 'filesystem:file:///persistent/',
+            // Cached files that should survive app restarts.
+            // Apps should not rely on the OS to delete files in here.
+            cacheDirectory: 'filesystem:file:///temporary/'
+        };
+
+        exports.requestAllPaths = function (successCallback) {
+            successCallback(pathsPrefix);
+        };
+
+        require('cordova/exec/proxy').add('File', module.exports);
+        return;
+    }
+
+    var LocalFileSystem = require('./LocalFileSystem');
+    var FileSystem = require('./FileSystem');
+    var FileEntry = require('./FileEntry');
+    var FileError = require('./FileError');
+    var DirectoryEntry = require('./DirectoryEntry');
+    var File = require('./File');
+
+    (function (exports, global) {
+        var indexedDB = global.indexedDB || global.mozIndexedDB;
+        if (!indexedDB) {
+            throw 'Firefox OS File plugin: indexedDB not supported';
+        }
+
+        var fs_ = null;
+
+        var idb_ = {};
+        idb_.db = null;
+        var FILE_STORE_ = 'entries';
+
+        var DIR_SEPARATOR = '/';
+
+        var pathsPrefix = {
+            // Read-only directory where the application is installed.
+            applicationDirectory: location.origin + '/', // eslint-disable-line no-undef
+            // Where to put app-specific data files.
+            dataDirectory: 'file:///persistent/',
+            // Cached files that should survive app restarts.
+            // Apps should not rely on the OS to delete files in here.
+            cacheDirectory: 'file:///temporary/'
+        };
+
+        var unicodeLastChar = 65535;
+
+    /** * Exported functionality ***/
+
+        exports.requestFileSystem = function (successCallback, errorCallback, args) {
+            var type = args[0];
+            // Size is ignored since IDB filesystem size depends
+            // on browser implementation and can't be set up by user
+            var size = args[1]; // eslint-disable-line no-unused-vars
+
+            if (type !== LocalFileSystem.TEMPORARY && type !== LocalFileSystem.PERSISTENT) {
+                if (errorCallback) {
+                    errorCallback(FileError.INVALID_MODIFICATION_ERR);
+                }
+                return;
+            }
+
+            var name = type === LocalFileSystem.TEMPORARY ? 'temporary' : 'persistent';
+            var storageName = (location.protocol + location.host).replace(/:/g, '_'); // eslint-disable-line no-undef
+
+            var root = new DirectoryEntry('', DIR_SEPARATOR);
+            fs_ = new FileSystem(name, root);
+
+            idb_.open(storageName, function () {
+                successCallback(fs_);
+            }, errorCallback);
+        };
+
+        // Overridden by Android, BlackBerry 10 and iOS to populate fsMap
+        require('./fileSystems').getFs = function (name, callback) {
+            callback(new FileSystem(name, fs_.root));
+        };
+
+        // list a directory's contents (files and folders).
+        exports.readEntries = function (successCallback, errorCallback, args) {
+            var fullPath = args[0];
+
+            if (typeof successCallback !== 'function') {
+                throw Error('Expected successCallback argument.');
+            }
+
+            var path = resolveToFullPath_(fullPath);
+
+            exports.getDirectory(function () {
+                idb_.getAllEntries(path.fullPath + DIR_SEPARATOR, path.storagePath, function (entries) {
+                    successCallback(entries);
+                }, errorCallback);
+            }, function () {
+                if (errorCallback) {
+                    errorCallback(FileError.NOT_FOUND_ERR);
+                }
+            }, [path.storagePath, path.fullPath, {create: false}]);
+        };
+
+        exports.getFile = function (successCallback, errorCallback, args) {
+            var fullPath = args[0];
+            var path = args[1];
+            var options = args[2] || {};
+
+            // Create an absolute path if we were handed a relative one.
+            path = resolveToFullPath_(fullPath, path);
+
+            idb_.get(path.storagePath, function (fileEntry) {
+                if (options.create === true && options.exclusive === true && fileEntry) {
+                    // If create and exclusive are both true, and the path already exists,
+                    // getFile must fail.
+
+                    if (errorCallback) {
+                        errorCallback(FileError.PATH_EXISTS_ERR);
+                    }
+                } else if (options.create === true && !fileEntry) {
+                    // If create is true, the path doesn't exist, and no other error occurs,
+                    // getFile must create it as a zero-length file and return a corresponding
+                    // FileEntry.
+                    var newFileEntry = new FileEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root));
+
+                    newFileEntry.file_ = new MyFile({
+                        size: 0,
+                        name: newFileEntry.name,
+                        lastModifiedDate: new Date(),
+                        storagePath: path.storagePath
+                    });
+
+                    idb_.put(newFileEntry, path.storagePath, successCallback, errorCallback);
+                } else if (options.create === true && fileEntry) {
+                    if (fileEntry.isFile) {
+                        // Overwrite file, delete then create new.
+                        idb_['delete'](path.storagePath, function () {
+                            var newFileEntry = new FileEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root));
+
+                            newFileEntry.file_ = new MyFile({
+                                size: 0,
+                                name: newFileEntry.name,
+                                lastModifiedDate: new Date(),
+                                storagePath: path.storagePath
+                            });
+
+                            idb_.put(newFileEntry, path.storagePath, successCallback, errorCallback);
+                        }, errorCallback);
+                    } else {
+                        if (errorCallback) {
+                            errorCallback(FileError.INVALID_MODIFICATION_ERR);
+                        }
+                    }
+                } else if ((!options.create || options.create === false) && !fileEntry) {
+                    // If create is not true and the path doesn't exist, getFile must fail.
+                    if (errorCallback) {
+                        errorCallback(FileError.NOT_FOUND_ERR);
+                    }
+                } else if ((!options.create || options.create === false) && fileEntry &&
+                    fileEntry.isDirectory) {
+                    // If create is not true and the path exists, but is a directory, getFile
+                    // must fail.
+                    if (errorCallback) {
+                        errorCallback(FileError.TYPE_MISMATCH_ERR);
+                    }
+                } else {
+                    // Otherwise, if no other error occurs, getFile must return a FileEntry
+                    // corresponding to path.
+
+                    successCallback(fileEntryFromIdbEntry(fileEntry));
+                }
+            }, errorCallback);
+        };
+
+        exports.getFileMetadata = function (successCallback, errorCallback, args) {
+            var fullPath = args[0];
+
+            exports.getFile(function (fileEntry) {
+                successCallback(new File(fileEntry.file_.name, fileEntry.fullPath, '', fileEntry.file_.lastModifiedDate,
+                    fileEntry.file_.size));
+            }, errorCallback, [fullPath, null]);
+        };
+
+        exports.getMetadata = function (successCallback, errorCallback, args) {
+            exports.getFile(function (fileEntry) {
+                successCallback(
+                    {
+                        modificationTime: fileEntry.file_.lastModifiedDate,
+                        size: fileEntry.file_.lastModifiedDate
+                    });
+            }, errorCallback, args);
+        };
+
+        exports.setMetadata = function (successCallback, errorCallback, args) {
+            var fullPath = args[0];
+            var metadataObject = args[1];
+
+            exports.getFile(function (fileEntry) {
+                fileEntry.file_.lastModifiedDate = metadataObject.modificationTime;
+                idb_.put(fileEntry, fileEntry.file_.storagePath, successCallback, errorCallback);
+            }, errorCallback, [fullPath, null]);
+        };
+
+        exports.write = function (successCallback, errorCallback, args) {
+            var fileName = args[0];
+            var data = args[1];
+            var position = args[2];
+            var isBinary = args[3]; // eslint-disable-line no-unused-vars
+
+            if (!data) {
+                if (errorCallback) {
+                    errorCallback(FileError.INVALID_MODIFICATION_ERR);
+                }
+                return;
+            }
+
+            if (typeof data === 'string' || data instanceof String) {
+                data = new Blob([data]); // eslint-disable-line no-undef
+            }
+
+            exports.getFile(function (fileEntry) {
+                var blob_ = fileEntry.file_.blob_;
+
+                if (!blob_) {
+                    blob_ = new Blob([data], {type: data.type}); // eslint-disable-line no-undef
+                } else {
+                    // Calc the head and tail fragments
+                    var head = blob_.slice(0, position);
+                    var tail = blob_.slice(position + (data.size || data.byteLength));
+
+                    // Calc the padding
+                    var padding = position - head.size;
+                    if (padding < 0) {
+                        padding = 0;
+                    }
+
+                    // Do the "write". In fact, a full overwrite of the Blob.
+                    blob_ = new Blob([head, new Uint8Array(padding), data, tail], // eslint-disable-line no-undef
+                        {type: data.type});
+                }
+
+                // Set the blob we're writing on this file entry so we can recall it later.
+                fileEntry.file_.blob_ = blob_;
+                fileEntry.file_.lastModifiedDate = new Date() || null;
+                fileEntry.file_.size = blob_.size;
+                fileEntry.file_.name = blob_.name;
+                fileEntry.file_.type = blob_.type;
+
+                idb_.put(fileEntry, fileEntry.file_.storagePath, function () {
+                    successCallback(data.size || data.byteLength);
+                }, errorCallback);
+            }, errorCallback, [fileName, null]);
+        };
+
+        exports.readAsText = function (successCallback, errorCallback, args) {
+            var fileName = args[0];
+            var enc = args[1];
+            var startPos = args[2];
+            var endPos = args[3];
+
+            readAs('text', fileName, enc, startPos, endPos, successCallback, errorCallback);
+        };
+
+        exports.readAsDataURL = function (successCallback, errorCallback, args) {
+            var fileName = args[0];
+            var startPos = args[1];
+            var endPos = args[2];
+
+            readAs('dataURL', fileName, null, startPos, endPos, successCallback, errorCallback);
+        };
+
+        exports.readAsBinaryString = function (successCallback, errorCallback, args) {
+            var fileName = args[0];
+            var startPos = args[1];
+            var endPos = args[2];
+
+            readAs('binaryString', fileName, null, startPos, endPos, successCallback, errorCallback);
+        };
+
+        exports.readAsArrayBuffer = function (successCallback, errorCallback, args) {
+            var fileName = args[0];
+            var startPos = args[1];
+            var endPos = args[2];
+
+            readAs('arrayBuffer', fileName, null, startPos, endPos, successCallback, errorCallback);
+        };
+
+        exports.removeRecursively = exports.remove = function (successCallback, errorCallback, args) {
+            if (typeof successCallback !== 'function') {
+                throw Error('Expected successCallback argument.');
+            }
+
+            var fullPath = resolveToFullPath_(args[0]).storagePath;
+            if (fullPath === pathsPrefix.cacheDirectory || fullPath === pathsPrefix.dataDirectory) {
+                errorCallback(FileError.NO_MODIFICATION_ALLOWED_ERR);
+                return;
+            }
+
+            function deleteEntry (isDirectory) {
+                // TODO: This doesn't protect against directories that have content in it.
+                // Should throw an error instead if the dirEntry is not empty.
+                idb_['delete'](fullPath, function () {
+                    successCallback();
+                }, function () {
+                    if (errorCallback) { errorCallback(); }
+                }, isDirectory);
+            }
+
+            // We need to to understand what we are deleting:
+            exports.getDirectory(function (entry) {
+                deleteEntry(entry.isDirectory);
+            }, function () {
+                // DirectoryEntry was already deleted or entry is FileEntry
+                deleteEntry(false);
+            }, [fullPath, null, {create: false}]);
+        };
+
+        exports.getDirectory = function (successCallback, errorCallback, args) {
+            var fullPath = args[0];
+            var path = args[1];
+            var options = args[2];
+
+            // Create an absolute path if we were handed a relative one.
+            path = resolveToFullPath_(fullPath, path);
+
+            idb_.get(path.storagePath, function (folderEntry) {
+                if (!options) {
+                    options = {};
+                }
+
+                if (options.create === true && options.exclusive === true && folderEntry) {
+                    // If create and exclusive are both true, and the path already exists,
+                    // getDirectory must fail.
+                    if (errorCallback) {
+                        errorCallback(FileError.PATH_EXISTS_ERR);
+                    }
+                    // There is a strange bug in mobilespec + FF, which results in coming to multiple else-if's
+                    // so we are shielding from it with returns.
+                    return;
+                }
+
+                if (options.create === true && !folderEntry) {
+                    // If create is true, the path doesn't exist, and no other error occurs,
+                    // getDirectory must create it as a zero-length file and return a corresponding
+                    // MyDirectoryEntry.
+                    var dirEntry = new DirectoryEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root));
+
+                    idb_.put(dirEntry, path.storagePath, successCallback, errorCallback);
+                    return;
+                }
+
+                if (options.create === true && folderEntry) {
+
+                    if (folderEntry.isDirectory) {
+                        // IDB won't save methods, so we need re-create the MyDirectoryEntry.
+                        successCallback(new DirectoryEntry(folderEntry.name, folderEntry.fullPath, folderEntry.filesystem));
+                    } else {
+                        if (errorCallback) {
+                            errorCallback(FileError.INVALID_MODIFICATION_ERR);
+                        }
+                    }
+                    return;
+                }
+
+                if ((!options.create || options.create === false) && !folderEntry) {
+                    // Handle root special. It should always exist.
+                    if (path.fullPath === DIR_SEPARATOR) {
+                        successCallback(fs_.root);
+                        return;
+                    }
+
+                    // If create is not true and the path doesn't exist, getDirectory must fail.
+                    if (errorCallback) {
+                        errorCallback(FileError.NOT_FOUND_ERR);
+                    }
+
+                    return;
+                }
+                if ((!options.create || options.create === false) && folderEntry && folderEntry.isFile) {
+                    // If create is not true and the path exists, but is a file, getDirectory
+                    // must fail.
+                    if (errorCallback) {
+                        errorCallback(FileError.TYPE_MISMATCH_ERR);
+                    }
+                    return;
+                }
+
+                // Otherwise, if no other error occurs, getDirectory must return a
+                // MyDirectoryEntry corresponding to path.
+
+                // IDB won't' save methods, so we need re-create MyDirectoryEntry.
+                successCallback(new DirectoryEntry(folderEntry.name, folderEntry.fullPath, folderEntry.filesystem));
+            }, errorCallback);
+        };
+
+        exports.getParent = function (successCallback, errorCallback, args) {
+            if (typeof successCallback !== 'function') {
+                throw Error('Expected successCallback argument.');
+            }
+
+            var fullPath = args[0];
+            // fullPath is like this:
+            // file:///persistent/path/to/file or
+            // file:///persistent/path/to/directory/
+
+            if (fullPath === DIR_SEPARATOR || fullPath === pathsPrefix.cacheDirectory ||
+                fullPath === pathsPrefix.dataDirectory) {
+                successCallback(fs_.root);
+                return;
+            }
+
+            // To delete all slashes at the end
+            while (fullPath[fullPath.length - 1] === '/') {
+                fullPath = fullPath.substr(0, fullPath.length - 1);
+            }
+
+            var pathArr = fullPath.split(DIR_SEPARATOR);
+            pathArr.pop();
+            var parentName = pathArr.pop();
+            var path = pathArr.join(DIR_SEPARATOR) + DIR_SEPARATOR;
+
+            // To get parent of root files
+            var joined = path + parentName + DIR_SEPARATOR;// is like this: file:///persistent/
+            if (joined === pathsPrefix.cacheDirectory || joined === pathsPrefix.dataDirectory) {
+                exports.getDirectory(successCallback, errorCallback, [joined, DIR_SEPARATOR, {create: false}]);
+                return;
+            }
+
+            exports.getDirectory(successCallback, errorCallback, [path, parentName, {create: false}]);
+        };
+
+        exports.copyTo = function (successCallback, errorCallback, args) {
+            var srcPath = args[0];
+            var parentFullPath = args[1];
+            var name = args[2];
+
+            if (name.indexOf('/') !== -1 || srcPath === parentFullPath + name) {
+                if (errorCallback) {
+                    errorCallback(FileError.INVALID_MODIFICATION_ERR);
+                }
+
+                return;
+            }
+
+            // Read src file
+            exports.getFile(function (srcFileEntry) {
+
+                var path = resolveToFullPath_(parentFullPath);
+                // Check directory
+                exports.getDirectory(function () {
+
+                    // Create dest file
+                    exports.getFile(function (dstFileEntry) {
+
+                        exports.write(function () {
+                            successCallback(dstFileEntry);
+                        }, errorCallback, [dstFileEntry.file_.storagePath, srcFileEntry.file_.blob_, 0]);
+
+                    }, errorCallback, [parentFullPath, name, {create: true}]);
+
+                }, function () { if (errorCallback) { errorCallback(FileError.NOT_FOUND_ERR); } },
+                [path.storagePath, null, {create: false}]);
+
+            }, errorCallback, [srcPath, null]);
+        };
+
+        exports.moveTo = function (successCallback, errorCallback, args) {
+            var srcPath = args[0];
+            // parentFullPath and name parameters is ignored because
+            // args is being passed downstream to exports.copyTo method
+            var parentFullPath = args[1]; // eslint-disable-line
+            var name = args[2]; // eslint-disable-line
+
+            exports.copyTo(function (fileEntry) {
+
+                exports.remove(function () {
+                    successCallback(fileEntry);
+                }, errorCallback, [srcPath]);
+
+            }, errorCallback, args);
+        };
+
+        exports.resolveLocalFileSystemURI = function (successCallback, errorCallback, args) {
+            var path = args[0];
+
+            // Ignore parameters
+            if (path.indexOf('?') !== -1) {
+                path = String(path).split('?')[0];
+            }
+
+            // support for encodeURI
+            if (/\%5/g.test(path) || /\%20/g.test(path)) {  // eslint-disable-line no-useless-escape
+                path = decodeURI(path);
+            }
+
+            if (path.trim()[0] === '/') {
+                if (errorCallback) {
+                    errorCallback(FileError.ENCODING_ERR);
+                }
+                return;
+            }
+
+            // support for cdvfile
+            if (path.trim().substr(0, 7) === 'cdvfile') {
+                if (path.indexOf('cdvfile://localhost') === -1) {
+                    if (errorCallback) {
+                        errorCallback(FileError.ENCODING_ERR);
+                    }
+                    return;
+                }
+
+                var indexPersistent = path.indexOf('persistent');
+                var indexTemporary = path.indexOf('temporary');
+
+                // cdvfile://localhost/persistent/path/to/file
+                if (indexPersistent !== -1) {
+                    path = 'file:///persistent' + path.substr(indexPersistent + 10);
+                } else if (indexTemporary !== -1) {
+                    path = 'file:///temporary' + path.substr(indexTemporary + 9);
+                } else {
+                    if (errorCallback) {
+                        errorCallback(FileError.ENCODING_ERR);
+                    }
+                    return;
+                }
+            }
+
+            // to avoid path form of '///path/to/file'
+            function handlePathSlashes (path) {
+                var cutIndex = 0;
+                for (var i = 0; i < path.length - 1; i++) {
+                    if (path[i] === DIR_SEPARATOR && path[i + 1] === DIR_SEPARATOR) {
+                        cutIndex = i + 1;
+                    } else break;
+                }
+
+                return path.substr(cutIndex);
+            }
+
+            // Handle localhost containing paths (see specs )
+            if (path.indexOf('file://localhost/') === 0) {
+                path = path.replace('file://localhost/', 'file:///');
+            }
+
+            if (path.indexOf(pathsPrefix.dataDirectory) === 0) {
+                path = path.substring(pathsPrefix.dataDirectory.length - 1);
+                path = handlePathSlashes(path);
+
+                exports.requestFileSystem(function () {
+                    exports.getFile(successCallback, function () {
+                        exports.getDirectory(successCallback, errorCallback, [pathsPrefix.dataDirectory, path,
+                        {create: false}]);
+                    }, [pathsPrefix.dataDirectory, path, {create: false}]);
+                }, errorCallback, [LocalFileSystem.PERSISTENT]);
+            } else if (path.indexOf(pathsPrefix.cacheDirectory) === 0) {
+                path = path.substring(pathsPrefix.cacheDirectory.length - 1);
+                path = handlePathSlashes(path);
+
+                exports.requestFileSystem(function () {
+                    exports.getFile(successCallback, function () {
+                        exports.getDirectory(successCallback, errorCallback, [pathsPrefix.cacheDirectory, path,
+                        {create: false}]);
+                    }, [pathsPrefix.cacheDirectory, path, {create: false}]);
+                }, errorCallback, [LocalFileSystem.TEMPORARY]);
+            } else if (path.indexOf(pathsPrefix.applicationDirectory) === 0) {
+                path = path.substring(pathsPrefix.applicationDirectory.length);
+                // TODO: need to cut out redundant slashes?
+
+                var xhr = new XMLHttpRequest(); // eslint-disable-line no-undef
+                xhr.open('GET', path, true);
+                xhr.onreadystatechange = function () {
+                    if (xhr.status === 200 && xhr.readyState === 4) {
+                        exports.requestFileSystem(function (fs) {
+                            fs.name = location.hostname; // eslint-disable-line no-undef
+
+                            // TODO: need to call exports.getFile(...) to handle errors correct
+                            fs.root.getFile(path, {create: true}, writeFile, errorCallback);
+                        }, errorCallback, [LocalFileSystem.PERSISTENT]);
+                    }
+                };
+
+                xhr.onerror = function () {
+                    if (errorCallback) {
+                        errorCallback(FileError.NOT_READABLE_ERR);
+                    }
+                };
+
+                xhr.send();
+            } else {
+                if (errorCallback) {
+                    errorCallback(FileError.NOT_FOUND_ERR);
+                }
+            }
+
+            function writeFile (entry) {
+                entry.createWriter(function (fileWriter) {
+                    fileWriter.onwriteend = function (evt) {
+                        if (!evt.target.error) {
+                            entry.filesystemName = location.hostname; // eslint-disable-line no-undef
+                            successCallback(entry);
+                        }
+                    };
+                    fileWriter.onerror = function () {
+                        if (errorCallback) {
+                            errorCallback(FileError.NOT_READABLE_ERR);
+                        }
+                    };
+                    fileWriter.write(new Blob([xhr.response])); // eslint-disable-line no-undef
+                }, errorCallback); // eslint-disable-line no-undef
+            }
+        };
+
+        exports.requestAllPaths = function (successCallback) {
+            successCallback(pathsPrefix);
+        };
+
+    /** * Helpers ***/
+
+        /**
+         * Interface to wrap the native File interface.
+         *
+         * This interface is necessary for creating zero-length (empty) files,
+         * something the Filesystem API allows you to do. Unfortunately, File's
+         * constructor cannot be called directly, making it impossible to instantiate
+         * an empty File in JS.
+         *
+         * @param {Object} opts Initial values.
+         * @constructor
+         */
+        function MyFile (opts) {
+            var blob_ = new Blob(); // eslint-disable-line no-undef
+
+            this.size = opts.size || 0;
+            this.name = opts.name || '';
+            this.type = opts.type || '';
+            this.lastModifiedDate = opts.lastModifiedDate || null;
+            this.storagePath = opts.storagePath || '';
+
+            // Need some black magic to correct the object's size/name/type based on the
+            // blob that is saved.
+            Object.defineProperty(this, 'blob_', {
+                enumerable: true,
+                get: function () {
+                    return blob_;
+                },
+                set: function (val) {
+                    blob_ = val;
+                    this.size = blob_.size;
+                    this.name = blob_.name;
+                    this.type = blob_.type;
+                    this.lastModifiedDate = blob_.lastModifiedDate;
+                }.bind(this)
+            });
+        }
+
+        MyFile.prototype.constructor = MyFile;
+
+        // When saving an entry, the fullPath should always lead with a slash and never
+        // end with one (e.g. a directory). Also, resolve '.' and '..' to an absolute
+        // one. This method ensures path is legit!
+        function resolveToFullPath_ (cwdFullPath, path) {
+            path = path || '';
+            var fullPath = path;
+            var prefix = '';
+
+            cwdFullPath = cwdFullPath || DIR_SEPARATOR;
+            if (cwdFullPath.indexOf(FILESYSTEM_PREFIX) === 0) {
+                prefix = cwdFullPath.substring(0, cwdFullPath.indexOf(DIR_SEPARATOR, FILESYSTEM_PREFIX.length));
+                cwdFullPath = cwdFullPath.substring(cwdFullPath.indexOf(DIR_SEPARATOR, FILESYSTEM_PREFIX.length));
+            }
+
+            var relativePath = path[0] !== DIR_SEPARATOR;
+            if (relativePath) {
+                fullPath = cwdFullPath;
+                if (cwdFullPath !== DIR_SEPARATOR) {
+                    fullPath += DIR_SEPARATOR + path;
+                } else {
+                    fullPath += path;
+                }
+            }
+
+            // Remove doubled separator substrings
+            var re = new RegExp(DIR_SEPARATOR + DIR_SEPARATOR, 'g');
+            fullPath = fullPath.replace(re, DIR_SEPARATOR);
+
+            // Adjust '..'s by removing parent directories when '..' flows in path.
+            var parts = fullPath.split(DIR_SEPARATOR);
+            for (var i = 0; i < parts.length; ++i) {
+                var part = parts[i];
+                if (part === '..') {
+                    parts[i - 1] = '';
+                    parts[i] = '';
+                }
+            }
+            fullPath = parts.filter(function (el) {
+                return el;
+            }).join(DIR_SEPARATOR);
+
+            // Add back in leading slash.
+            if (fullPath[0] !== DIR_SEPARATOR) {
+                fullPath = DIR_SEPARATOR + fullPath;
+            }
+
+            // Replace './' by current dir. ('./one/./two' -> one/two)
+            fullPath = fullPath.replace(/\.\//g, DIR_SEPARATOR);
+
+            // Replace '//' with '/'.
+            fullPath = fullPath.replace(/\/\//g, DIR_SEPARATOR);
+
+            // Replace '/.' with '/'.
+            fullPath = fullPath.replace(/\/\./g, DIR_SEPARATOR);
+
+            // Remove '/' if it appears on the end.
+            if (fullPath[fullPath.length - 1] === DIR_SEPARATOR &&
+                fullPath !== DIR_SEPARATOR) {
+                fullPath = fullPath.substring(0, fullPath.length - 1);
+            }
+
+            var storagePath = prefix + fullPath;
+            storagePath = decodeURI(storagePath);
+            fullPath = decodeURI(fullPath);
+
+            return {
+                storagePath: storagePath,
+                fullPath: fullPath,
+                fileName: fullPath.split(DIR_SEPARATOR).pop(),
+                fsName: prefix.split(DIR_SEPARATOR).pop()
+            };
+        }
+
+        function fileEntryFromIdbEntry (fileEntry) {
+            // IDB won't save methods, so we need re-create the FileEntry.
+            var clonedFileEntry = new FileEntry(fileEntry.name, fileEntry.fullPath, fileEntry.filesystem);
+            clonedFileEntry.file_ = fileEntry.file_;
+
+            return clonedFileEntry;
+        }
+
+        function readAs (what, fullPath, encoding, startPos, endPos, successCallback, errorCallback) {
+            exports.getFile(function (fileEntry) {
+                var fileReader = new FileReader(); // eslint-disable-line no-undef
+                var blob = fileEntry.file_.blob_.slice(startPos, endPos);
+
+                fileReader.onload = function (e) {
+                    successCallback(e.target.result);
+                };
+
+                fileReader.onerror = errorCallback;
+
+                switch (what) {
+                case 'text':
+                    fileReader.readAsText(blob, encoding);
+                    break;
+                case 'dataURL':
+                    fileReader.readAsDataURL(blob);
+                    break;
+                case 'arrayBuffer':
+                    fileReader.readAsArrayBuffer(blob);
+                    break;
+                case 'binaryString':
+                    fileReader.readAsBinaryString(blob);
+                    break;
+                }
+
+            }, errorCallback, [fullPath, null]);
+        }
+
+    /** * Core logic to handle IDB operations ***/
+
+        idb_.open = function (dbName, successCallback, errorCallback) {
+            var self = this;
+
+            // TODO: FF 12.0a1 isn't liking a db name with : in it.
+            var request = indexedDB.open(dbName.replace(':', '_')/*, 1 /*version */);
+
+            request.onerror = errorCallback || onError;
+
+            request.onupgradeneeded = function (e) {
+                // First open was called or higher db version was used.
+
+                // console.log('onupgradeneeded: oldVersion:' + e.oldVersion,
+                //           'newVersion:' + e.newVersion);
+
+                self.db = e.target.result;
+                self.db.onerror = onError;
+
+                if (!self.db.objectStoreNames.contains(FILE_STORE_)) {
+                    self.db.createObjectStore(FILE_STORE_/*, {keyPath: 'id', autoIncrement: true} */);
+                }
+            };
+
+            request.onsuccess = function (e) {
+                self.db = e.target.result;
+                self.db.onerror = onError;
+                successCallback(e);
+            };
+
+            request.onblocked = errorCallback || onError;
+        };
+
+        idb_.close = function () {
+            this.db.close();
+            this.db = null;
+        };
+
+        idb_.get = function (fullPath, successCallback, errorCallback) {
+            if (!this.db) {
+                if (errorCallback) {
+                    errorCallback(FileError.INVALID_MODIFICATION_ERR);
+                }
+                return;
+            }
+
+            var tx = this.db.transaction([FILE_STORE_], 'readonly');
+
+            var request = tx.objectStore(FILE_STORE_).get(fullPath);
+
+            tx.onabort = errorCallback || onError;
+            tx.oncomplete = function () {
+                successCallback(request.result);
+            };
+        };
+
+        idb_.getAllEntries = function (fullPath, storagePath, successCallback, errorCallback) {
+            if (!this.db) {
+                if (errorCallback) {
+                    errorCallback(FileError.INVALID_MODIFICATION_ERR);
+                }
+                return;
+            }
+
+            var results = [];
+
+            if (storagePath[storagePath.length - 1] === DIR_SEPARATOR) {
+                storagePath = storagePath.substring(0, storagePath.length - 1);
+            }
+
+            var range = IDBKeyRange.bound(storagePath + DIR_SEPARATOR + ' ',
+                storagePath + DIR_SEPARATOR + String.fromCharCode(unicodeLastChar));
+
+            var tx = this.db.transaction([FILE_STORE_], 'readonly');
+            tx.onabort = errorCallback || onError;
+            tx.oncomplete = function () {
+                results = results.filter(function (val) {
+                    var pathWithoutSlash = val.fullPath;
+
+                    if (val.fullPath[val.fullPath.length - 1] === DIR_SEPARATOR) {
+                        pathWithoutSlash = pathWithoutSlash.substr(0, pathWithoutSlash.length - 1);
+                    }
+
+                    var valPartsLen = pathWithoutSlash.split(DIR_SEPARATOR).length;
+                    var fullPathPartsLen = fullPath.split(DIR_SEPARATOR).length;
+
+                    /* Input fullPath parameter  equals '//' for root folder */
+                    /* Entries in root folder has valPartsLen equals 2 (see below) */
+                    if (fullPath[fullPath.length - 1] === DIR_SEPARATOR && fullPath.trim().length === 2) {
+                        fullPathPartsLen = 1;
+                    } else if (fullPath[fullPath.length - 1] === DIR_SEPARATOR) {
+                        fullPathPartsLen = fullPath.substr(0, fullPath.length - 1).split(DIR_SEPARATOR).length;
+                    } else {
+                        fullPathPartsLen = fullPath.split(DIR_SEPARATOR).length;
+                    }
+
+                    if (valPartsLen === fullPathPartsLen + 1) {
+                        // If this a subfolder and entry is a direct child, include it in
+                        // the results. Otherwise, it's not an entry of this folder.
+                        return val;
+                    } else return false;
+                });
+
+                successCallback(results);
+            };
+
+            var request = tx.objectStore(FILE_STORE_).openCursor(range);
+
+            request.onsuccess = function (e) {
+                var cursor = e.target.result;
+                if (cursor) {
+                    var val = cursor.value;
+
+                    results.push(val.isFile ? fileEntryFromIdbEntry(val) : new DirectoryEntry(val.name, val.fullPath, val.filesystem));
+                    cursor['continue']();
+                }
+            };
+        };
+
+        idb_['delete'] = function (fullPath, successCallback, errorCallback, isDirectory) {
+            if (!idb_.db) {
+                if (errorCallback) {
+                    errorCallback(FileError.INVALID_MODIFICATION_ERR);
+                }
+                return;
+            }
+
+            var tx = this.db.transaction([FILE_STORE_], 'readwrite');
+            tx.oncomplete = successCallback;
+            tx.onabort = errorCallback || onError;
+            tx.oncomplete = function () {
+                if (isDirectory) {
+                    // We delete nested files and folders after deleting parent folder
+                    // We use ranges: https://developer.mozilla.org/en-US/docs/Web/API/IDBKeyRange
+                    fullPath = fullPath + DIR_SEPARATOR;
+
+                    // Range contains all entries in the form fullPath<symbol> where
+                    // symbol in the range from ' ' to symbol which has code `unicodeLastChar`
+                    var range = IDBKeyRange.bound(fullPath + ' ', fullPath + String.fromCharCode(unicodeLastChar));
+
+                    var newTx = this.db.transaction([FILE_STORE_], 'readwrite');
+                    newTx.oncomplete = successCallback;
+                    newTx.onabort = errorCallback || onError;
+                    newTx.objectStore(FILE_STORE_)['delete'](range);
+                } else {
+                    successCallback();
+                }
+            };
+            tx.objectStore(FILE_STORE_)['delete'](fullPath);
+        };
+
+        idb_.put = function (entry, storagePath, successCallback, errorCallback) {
+            if (!this.db) {
+                if (errorCallback) {
+                    errorCallback(FileError.INVALID_MODIFICATION_ERR);
+                }
+                return;
+            }
+
+            var tx = this.db.transaction([FILE_STORE_], 'readwrite');
+            tx.onabort = errorCallback || onError;
+            tx.oncomplete = function () {
+                // TODO: Error is thrown if we pass the request event back instead.
+                successCallback(entry);
+            };
+
+            tx.objectStore(FILE_STORE_).put(entry, storagePath);
+        };
+
+        // Global error handler. Errors bubble from request, to transaction, to db.
+        function onError (e) {
+            switch (e.target.errorCode) {
+            case 12:
+                console.log('Error - Attempt to open db with a lower version than the ' +
+                        'current one.');
+                break;
+            default:
+                console.log('errorCode: ' + e.target.errorCode);
+            }
+
+            console.log(e, e.code, e.message);
+        }
+
+    })(module.exports, window);
+
+    require('cordova/exec/proxy').add('File', module.exports);
+})();
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/DirectoryEntry.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/DirectoryEntry.js
new file mode 100644
index 0000000..7e1d3f7
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/DirectoryEntry.js
@@ -0,0 +1,119 @@
+cordova.define("cordova-plugin-file.DirectoryEntry", function(require, exports, module) { /*
+ *
+ * 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');
+var utils = require('cordova/utils');
+var exec = require('cordova/exec');
+var Entry = require('./Entry');
+var FileError = require('./FileError');
+var DirectoryReader = require('./DirectoryReader');
+
+/**
+ * An interface representing a directory on the file system.
+ *
+ * {boolean} isFile always false (readonly)
+ * {boolean} isDirectory always true (readonly)
+ * {DOMString} name of the directory, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the directory (readonly)
+ * {FileSystem} filesystem on which the directory resides (readonly)
+ */
+var DirectoryEntry = function (name, fullPath, fileSystem, nativeURL) {
+
+    // add trailing slash if it is missing
+    if ((fullPath) && !/\/$/.test(fullPath)) {
+        fullPath += '/';
+    }
+    // add trailing slash if it is missing
+    if (nativeURL && !/\/$/.test(nativeURL)) {
+        nativeURL += '/';
+    }
+    DirectoryEntry.__super__.constructor.call(this, false, true, name, fullPath, fileSystem, nativeURL);
+};
+
+utils.extend(DirectoryEntry, Entry);
+
+/**
+ * Creates a new DirectoryReader to read entries from this directory
+ */
+DirectoryEntry.prototype.createReader = function () {
+    return new DirectoryReader(this.toInternalURL());
+};
+
+/**
+ * Creates or looks up a directory
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
+ * @param {Flags} options to create or exclusively create the directory
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getDirectory = function (path, options, successCallback, errorCallback) {
+    argscheck.checkArgs('sOFF', 'DirectoryEntry.getDirectory', arguments);
+    var fs = this.filesystem;
+    var win = successCallback && function (result) {
+        var entry = new DirectoryEntry(result.name, result.fullPath, fs, result.nativeURL);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function (code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, 'File', 'getDirectory', [this.toInternalURL(), path, options]);
+};
+
+/**
+ * Deletes a directory and all of it's contents
+ *
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.removeRecursively = function (successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'DirectoryEntry.removeRecursively', arguments);
+    var fail = errorCallback && function (code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, 'File', 'removeRecursively', [this.toInternalURL()]);
+};
+
+/**
+ * Creates or looks up a file
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
+ * @param {Flags} options to create or exclusively create the file
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getFile = function (path, options, successCallback, errorCallback) {
+    argscheck.checkArgs('sOFF', 'DirectoryEntry.getFile', arguments);
+    var fs = this.filesystem;
+    var win = successCallback && function (result) {
+        var FileEntry = require('./FileEntry');
+        var entry = new FileEntry(result.name, result.fullPath, fs, result.nativeURL);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function (code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, 'File', 'getFile', [this.toInternalURL(), path, options]);
+};
+
+module.exports = DirectoryEntry;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/DirectoryReader.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/DirectoryReader.js
new file mode 100644
index 0000000..ce74ade
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/DirectoryReader.js
@@ -0,0 +1,74 @@
+cordova.define("cordova-plugin-file.DirectoryReader", function(require, exports, module) { /*
+ *
+ * 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 exec = require('cordova/exec');
+var FileError = require('./FileError');
+
+/**
+ * An interface that lists the files and directories in a directory.
+ */
+function DirectoryReader (localURL) {
+    this.localURL = localURL || null;
+    this.hasReadEntries = false;
+}
+
+/**
+ * Returns a list of entries from a directory.
+ *
+ * @param {Function} successCallback is called with a list of entries
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryReader.prototype.readEntries = function (successCallback, errorCallback) {
+    // If we've already read and passed on this directory's entries, return an empty list.
+    if (this.hasReadEntries) {
+        successCallback([]);
+        return;
+    }
+    var reader = this;
+    var win = typeof successCallback !== 'function' ? null : function (result) {
+        var retVal = [];
+        for (var i = 0; i < result.length; i++) {
+            var entry = null;
+            if (result[i].isDirectory) {
+                entry = new (require('./DirectoryEntry'))();
+            } else if (result[i].isFile) {
+                entry = new (require('./FileEntry'))();
+            }
+            entry.isDirectory = result[i].isDirectory;
+            entry.isFile = result[i].isFile;
+            entry.name = result[i].name;
+            entry.fullPath = result[i].fullPath;
+            entry.filesystem = new (require('./FileSystem'))(result[i].filesystemName);
+            entry.nativeURL = result[i].nativeURL;
+            retVal.push(entry);
+        }
+        reader.hasReadEntries = true;
+        successCallback(retVal);
+    };
+    var fail = typeof errorCallback !== 'function' ? null : function (code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, 'File', 'readEntries', [this.localURL]);
+};
+
+module.exports = DirectoryReader;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/Entry.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/Entry.js
new file mode 100644
index 0000000..973cf14
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/Entry.js
@@ -0,0 +1,262 @@
+cordova.define("cordova-plugin-file.Entry", function(require, exports, module) { /*
+ *
+ * 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');
+var exec = require('cordova/exec');
+var FileError = require('./FileError');
+var Metadata = require('./Metadata');
+
+/**
+ * Represents a file or directory on the local file system.
+ *
+ * @param isFile
+ *            {boolean} true if Entry is a file (readonly)
+ * @param isDirectory
+ *            {boolean} true if Entry is a directory (readonly)
+ * @param name
+ *            {DOMString} name of the file or directory, excluding the path
+ *            leading to it (readonly)
+ * @param fullPath
+ *            {DOMString} the absolute full path to the file or directory
+ *            (readonly)
+ * @param fileSystem
+ *            {FileSystem} the filesystem on which this entry resides
+ *            (readonly)
+ * @param nativeURL
+ *            {DOMString} an alternate URL which can be used by native
+ *            webview controls, for example media players.
+ *            (optional, readonly)
+ */
+function Entry (isFile, isDirectory, name, fullPath, fileSystem, nativeURL) {
+    this.isFile = !!isFile;
+    this.isDirectory = !!isDirectory;
+    this.name = name || '';
+    this.fullPath = fullPath || '';
+    this.filesystem = fileSystem || null;
+    this.nativeURL = nativeURL || null;
+}
+
+/**
+ * Look up the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ */
+Entry.prototype.getMetadata = function (successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.getMetadata', arguments);
+    var success = successCallback && function (entryMetadata) {
+        var metadata = new Metadata({
+            size: entryMetadata.size,
+            modificationTime: entryMetadata.lastModifiedDate
+        });
+        successCallback(metadata);
+    };
+    var fail = errorCallback && function (code) {
+        errorCallback(new FileError(code));
+    };
+    exec(success, fail, 'File', 'getFileMetadata', [this.toInternalURL()]);
+};
+
+/**
+ * Set the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ * @param metadataObject
+ *            {Object} keys and values to set
+ */
+Entry.prototype.setMetadata = function (successCallback, errorCallback, metadataObject) {
+    argscheck.checkArgs('FFO', 'Entry.setMetadata', arguments);
+    exec(successCallback, errorCallback, 'File', 'setMetadata', [this.toInternalURL(), metadataObject]);
+};
+
+/**
+ * Move a file or directory to a new location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to move this entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new DirectoryEntry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.moveTo = function (parent, newName, successCallback, errorCallback) {
+    argscheck.checkArgs('oSFF', 'Entry.moveTo', arguments);
+    var fail = errorCallback && function (code) {
+        errorCallback(new FileError(code));
+    };
+    var srcURL = this.toInternalURL();
+    // entry name
+    var name = newName || this.name;
+    var success = function (entry) {
+        if (entry) {
+            if (successCallback) {
+                // create appropriate Entry object
+                var newFSName = entry.filesystemName || (entry.filesystem && entry.filesystem.name);
+                var fs = newFSName ? new FileSystem(newFSName, { name: '', fullPath: '/' }) : new FileSystem(parent.filesystem.name, { name: '', fullPath: '/' }); // eslint-disable-line no-undef
+                var result = (entry.isDirectory) ? new (require('./DirectoryEntry'))(entry.name, entry.fullPath, fs, entry.nativeURL) : new (require('cordova-plugin-file.FileEntry'))(entry.name, entry.fullPath, fs, entry.nativeURL);
+                successCallback(result);
+            }
+        } else {
+            // no Entry object returned
+            if (fail) {
+                fail(FileError.NOT_FOUND_ERR);
+            }
+        }
+    };
+
+    // copy
+    exec(success, fail, 'File', 'moveTo', [srcURL, parent.toInternalURL(), name]);
+};
+
+/**
+ * Copy a directory to a different location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to copy the entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new Entry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.copyTo = function (parent, newName, successCallback, errorCallback) {
+    argscheck.checkArgs('oSFF', 'Entry.copyTo', arguments);
+    var fail = errorCallback && function (code) {
+        errorCallback(new FileError(code));
+    };
+    var srcURL = this.toInternalURL();
+        // entry name
+    var name = newName || this.name;
+    // success callback
+    var success = function (entry) {
+        if (entry) {
+            if (successCallback) {
+                // create appropriate Entry object
+                var newFSName = entry.filesystemName || (entry.filesystem && entry.filesystem.name);
+                var fs = newFSName ? new FileSystem(newFSName, { name: '', fullPath: '/' }) : new FileSystem(parent.filesystem.name, { name: '', fullPath: '/' }); // eslint-disable-line no-undef
+                var result = (entry.isDirectory) ? new (require('./DirectoryEntry'))(entry.name, entry.fullPath, fs, entry.nativeURL) : new (require('cordova-plugin-file.FileEntry'))(entry.name, entry.fullPath, fs, entry.nativeURL);
+                successCallback(result);
+            }
+        } else {
+            // no Entry object returned
+            if (fail) {
+                fail(FileError.NOT_FOUND_ERR);
+            }
+        }
+    };
+
+    // copy
+    exec(success, fail, 'File', 'copyTo', [srcURL, parent.toInternalURL(), name]);
+};
+
+/**
+ * Return a URL that can be passed across the bridge to identify this entry.
+ */
+Entry.prototype.toInternalURL = function () {
+    if (this.filesystem && this.filesystem.__format__) {
+        return this.filesystem.__format__(this.fullPath, this.nativeURL);
+    }
+};
+
+/**
+ * Return a URL that can be used to identify this entry.
+ * Use a URL that can be used to as the src attribute of a <video> or
+ * <audio> tag. If that is not possible, construct a cdvfile:// URL.
+ */
+Entry.prototype.toURL = function () {
+    if (this.nativeURL) {
+        return this.nativeURL;
+    }
+    // fullPath attribute may contain the full URL in the case that
+    // toInternalURL fails.
+    return this.toInternalURL() || 'file://localhost' + this.fullPath;
+};
+
+/**
+ * Backwards-compatibility: In v1.0.0 - 1.0.2, .toURL would only return a
+ * cdvfile:// URL, and this method was necessary to obtain URLs usable by the
+ * webview.
+ * See CB-6051, CB-6106, CB-6117, CB-6152, CB-6199, CB-6201, CB-6243, CB-6249,
+ * and CB-6300.
+ */
+Entry.prototype.toNativeURL = function () {
+    console.log("DEPRECATED: Update your code to use 'toURL'");
+    return this.toURL();
+};
+
+/**
+ * Returns a URI that can be used to identify this entry.
+ *
+ * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
+ * @return uri
+ */
+Entry.prototype.toURI = function (mimeType) {
+    console.log("DEPRECATED: Update your code to use 'toURL'");
+    return this.toURL();
+};
+
+/**
+ * Remove a file or directory. It is an error to attempt to delete a
+ * directory that is not empty. It is an error to attempt to delete a
+ * root directory of a file system.
+ *
+ * @param successCallback {Function} called with no parameters
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.remove = function (successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.remove', arguments);
+    var fail = errorCallback && function (code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, 'File', 'remove', [this.toInternalURL()]);
+};
+
+/**
+ * Look up the parent DirectoryEntry of this entry.
+ *
+ * @param successCallback {Function} called with the parent DirectoryEntry object
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.getParent = function (successCallback, errorCallback) {
+    argscheck.checkArgs('FF', 'Entry.getParent', arguments);
+    var fs = this.filesystem;
+    var win = successCallback && function (result) {
+        var DirectoryEntry = require('./DirectoryEntry');
+        var entry = new DirectoryEntry(result.name, result.fullPath, fs, result.nativeURL);
+        successCallback(entry);
+    };
+    var fail = errorCallback && function (code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, 'File', 'getParent', [this.toInternalURL()]);
+};
+
+module.exports = Entry;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/File.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/File.js
new file mode 100644
index 0000000..974b09f
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/File.js
@@ -0,0 +1,80 @@
+cordova.define("cordova-plugin-file.File", function(require, exports, module) { /*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * Constructor.
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+
+var File = function (name, localURL, type, lastModifiedDate, size) {
+    this.name = name || '';
+    this.localURL = localURL || null;
+    this.type = type || null;
+    this.lastModified = lastModifiedDate || null;
+    // For backwards compatibility, store the timestamp in lastModifiedDate as well
+    this.lastModifiedDate = lastModifiedDate || null;
+    this.size = size || 0;
+
+    // These store the absolute start and end for slicing the file.
+    this.start = 0;
+    this.end = this.size;
+};
+
+/**
+ * Returns a "slice" of the file. Since Cordova Files don't contain the actual
+ * content, this really returns a File with adjusted start and end.
+ * Slices of slices are supported.
+ * start {Number} The index at which to start the slice (inclusive).
+ * end {Number} The index at which to end the slice (exclusive).
+ */
+File.prototype.slice = function (start, end) {
+    var size = this.end - this.start;
+    var newStart = 0;
+    var newEnd = size;
+    if (arguments.length) {
+        if (start < 0) {
+            newStart = Math.max(size + start, 0);
+        } else {
+            newStart = Math.min(size, start);
+        }
+    }
+
+    if (arguments.length >= 2) {
+        if (end < 0) {
+            newEnd = Math.max(size + end, 0);
+        } else {
+            newEnd = Math.min(end, size);
+        }
+    }
+
+    var newFile = new File(this.name, this.localURL, this.type, this.lastModified, this.size);
+    newFile.start = this.start + newStart;
+    newFile.end = this.start + newEnd;
+    return newFile;
+};
+
+module.exports = File;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileEntry.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileEntry.js
new file mode 100644
index 0000000..b16c851
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileEntry.js
@@ -0,0 +1,94 @@
+cordova.define("cordova-plugin-file.FileEntry", function(require, exports, module) { /*
+ *
+ * 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 utils = require('cordova/utils');
+var exec = require('cordova/exec');
+var Entry = require('./Entry');
+var FileWriter = require('./FileWriter');
+var File = require('./File');
+var FileError = require('./FileError');
+
+/**
+ * An interface representing a file on the file system.
+ *
+ * {boolean} isFile always true (readonly)
+ * {boolean} isDirectory always false (readonly)
+ * {DOMString} name of the file, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the file (readonly)
+ * {FileSystem} filesystem on which the file resides (readonly)
+ */
+var FileEntry = function (name, fullPath, fileSystem, nativeURL) {
+    // remove trailing slash if it is present
+    if (fullPath && /\/$/.test(fullPath)) {
+        fullPath = fullPath.substring(0, fullPath.length - 1);
+    }
+    if (nativeURL && /\/$/.test(nativeURL)) {
+        nativeURL = nativeURL.substring(0, nativeURL.length - 1);
+    }
+
+    FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath, fileSystem, nativeURL]);
+};
+
+utils.extend(FileEntry, Entry);
+
+/**
+ * Creates a new FileWriter associated with the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new FileWriter
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.createWriter = function (successCallback, errorCallback) {
+    this.file(function (filePointer) {
+        var writer = new FileWriter(filePointer);
+
+        if (writer.localURL === null || writer.localURL === '') {
+            if (errorCallback) {
+                errorCallback(new FileError(FileError.INVALID_STATE_ERR));
+            }
+        } else {
+            if (successCallback) {
+                successCallback(writer);
+            }
+        }
+    }, errorCallback);
+};
+
+/**
+ * Returns a File that represents the current state of the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new File object
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.file = function (successCallback, errorCallback) {
+    var localURL = this.toInternalURL();
+    var win = successCallback && function (f) {
+        var file = new File(f.name, localURL, f.type, f.lastModifiedDate, f.size);
+        successCallback(file);
+    };
+    var fail = errorCallback && function (code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, 'File', 'getFileMetadata', [localURL]);
+};
+
+module.exports = FileEntry;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileError.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileError.js
new file mode 100644
index 0000000..7a0a66e
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileError.js
@@ -0,0 +1,48 @@
+cordova.define("cordova-plugin-file.FileError", function(require, exports, module) { /*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * FileError
+ */
+function FileError (error) {
+    this.code = error || null;
+}
+
+// File error codes
+// Found in DOMException
+FileError.NOT_FOUND_ERR = 1;
+FileError.SECURITY_ERR = 2;
+FileError.ABORT_ERR = 3;
+
+// Added by File API specification
+FileError.NOT_READABLE_ERR = 4;
+FileError.ENCODING_ERR = 5;
+FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
+FileError.INVALID_STATE_ERR = 7;
+FileError.SYNTAX_ERR = 8;
+FileError.INVALID_MODIFICATION_ERR = 9;
+FileError.QUOTA_EXCEEDED_ERR = 10;
+FileError.TYPE_MISMATCH_ERR = 11;
+FileError.PATH_EXISTS_ERR = 12;
+
+module.exports = FileError;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileReader.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileReader.js
new file mode 100644
index 0000000..1227dd8
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileReader.js
@@ -0,0 +1,300 @@
+cordova.define("cordova-plugin-file.FileReader", function(require, exports, module) { /*
+ *
+ * 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 exec = require('cordova/exec');
+var modulemapper = require('cordova/modulemapper');
+var utils = require('cordova/utils');
+var FileError = require('./FileError');
+var ProgressEvent = require('./ProgressEvent');
+var origFileReader = modulemapper.getOriginalSymbol(window, 'FileReader');
+
+/**
+ * This class reads the mobile device file system.
+ *
+ * For Android:
+ *      The root directory is the root of the file system.
+ *      To read from the SD card, the file name is "sdcard/my_file.txt"
+ * @constructor
+ */
+var FileReader = function () {
+    this._readyState = 0;
+    this._error = null;
+    this._result = null;
+    this._progress = null;
+    this._localURL = '';
+    this._realReader = origFileReader ? new origFileReader() : {}; // eslint-disable-line new-cap
+};
+
+/**
+ * Defines the maximum size to read at a time via the native API. The default value is a compromise between
+ * minimizing the overhead of many exec() calls while still reporting progress frequently enough for large files.
+ * (Note attempts to allocate more than a few MB of contiguous memory on the native side are likely to cause
+ * OOM exceptions, while the JS engine seems to have fewer problems managing large strings or ArrayBuffers.)
+ */
+FileReader.READ_CHUNK_SIZE = 256 * 1024;
+
+// States
+FileReader.EMPTY = 0;
+FileReader.LOADING = 1;
+FileReader.DONE = 2;
+
+utils.defineGetter(FileReader.prototype, 'readyState', function () {
+    return this._localURL ? this._readyState : this._realReader.readyState;
+});
+
+utils.defineGetter(FileReader.prototype, 'error', function () {
+    return this._localURL ? this._error : this._realReader.error;
+});
+
+utils.defineGetter(FileReader.prototype, 'result', function () {
+    return this._localURL ? this._result : this._realReader.result;
+});
+
+function defineEvent (eventName) {
+    utils.defineGetterSetter(FileReader.prototype, eventName, function () {
+        return this._realReader[eventName] || null;
+    }, function (value) {
+        this._realReader[eventName] = value;
+    });
+}
+defineEvent('onloadstart');    // When the read starts.
+defineEvent('onprogress');     // While reading (and decoding) file or fileBlob data, and reporting partial file data (progress.loaded/progress.total)
+defineEvent('onload');         // When the read has successfully completed.
+defineEvent('onerror');        // When the read has failed (see errors).
+defineEvent('onloadend');      // When the request has completed (either in success or failure).
+defineEvent('onabort');        // When the read has been aborted. For instance, by invoking the abort() method.
+
+function initRead (reader, file) {
+    // Already loading something
+    if (reader.readyState === FileReader.LOADING) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    reader._result = null;
+    reader._error = null;
+    reader._progress = 0;
+    reader._readyState = FileReader.LOADING;
+
+    if (typeof file.localURL === 'string') {
+        reader._localURL = file.localURL;
+    } else {
+        reader._localURL = '';
+        return true;
+    }
+
+    if (reader.onloadstart) {
+        reader.onloadstart(new ProgressEvent('loadstart', {target: reader}));
+    }
+}
+
+/**
+ * Callback used by the following read* functions to handle incremental or final success.
+ * Must be bound to the FileReader's this along with all but the last parameter,
+ * e.g. readSuccessCallback.bind(this, "readAsText", "UTF-8", offset, totalSize, accumulate)
+ * @param readType The name of the read function to call.
+ * @param encoding Text encoding, or null if this is not a text type read.
+ * @param offset Starting offset of the read.
+ * @param totalSize Total number of bytes or chars to read.
+ * @param accumulate A function that takes the callback result and accumulates it in this._result.
+ * @param r Callback result returned by the last read exec() call, or null to begin reading.
+ */
+function readSuccessCallback (readType, encoding, offset, totalSize, accumulate, r) {
+    if (this._readyState === FileReader.DONE) {
+        return;
+    }
+
+    var CHUNK_SIZE = FileReader.READ_CHUNK_SIZE;
+    if (readType === 'readAsDataURL') {
+        // Windows proxy does not support reading file slices as Data URLs
+        // so read the whole file at once.
+        CHUNK_SIZE = cordova.platformId === 'windows' ? totalSize : // eslint-disable-line no-undef
+            // Calculate new chunk size for data URLs to be multiply of 3
+            // Otherwise concatenated base64 chunks won't be valid base64 data
+            FileReader.READ_CHUNK_SIZE - (FileReader.READ_CHUNK_SIZE % 3) + 3;
+    }
+
+    if (typeof r !== 'undefined') {
+        accumulate(r);
+        this._progress = Math.min(this._progress + CHUNK_SIZE, totalSize);
+
+        if (typeof this.onprogress === 'function') {
+            this.onprogress(new ProgressEvent('progress', {loaded: this._progress, total: totalSize}));
+        }
+    }
+
+    if (typeof r === 'undefined' || this._progress < totalSize) {
+        var execArgs = [
+            this._localURL,
+            offset + this._progress,
+            offset + this._progress + Math.min(totalSize - this._progress, CHUNK_SIZE)];
+        if (encoding) {
+            execArgs.splice(1, 0, encoding);
+        }
+        exec(
+            readSuccessCallback.bind(this, readType, encoding, offset, totalSize, accumulate),
+            readFailureCallback.bind(this),
+            'File', readType, execArgs);
+    } else {
+        this._readyState = FileReader.DONE;
+
+        if (typeof this.onload === 'function') {
+            this.onload(new ProgressEvent('load', {target: this}));
+        }
+
+        if (typeof this.onloadend === 'function') {
+            this.onloadend(new ProgressEvent('loadend', {target: this}));
+        }
+    }
+}
+
+/**
+ * Callback used by the following read* functions to handle errors.
+ * Must be bound to the FileReader's this, e.g. readFailureCallback.bind(this)
+ */
+function readFailureCallback (e) {
+    if (this._readyState === FileReader.DONE) {
+        return;
+    }
+
+    this._readyState = FileReader.DONE;
+    this._result = null;
+    this._error = new FileError(e);
+
+    if (typeof this.onerror === 'function') {
+        this.onerror(new ProgressEvent('error', {target: this}));
+    }
+
+    if (typeof this.onloadend === 'function') {
+        this.onloadend(new ProgressEvent('loadend', {target: this}));
+    }
+}
+
+/**
+ * Abort reading file.
+ */
+FileReader.prototype.abort = function () {
+    if (origFileReader && !this._localURL) {
+        return this._realReader.abort();
+    }
+    this._result = null;
+
+    if (this._readyState === FileReader.DONE || this._readyState === FileReader.EMPTY) {
+        return;
+    }
+
+    this._readyState = FileReader.DONE;
+
+    // If abort callback
+    if (typeof this.onabort === 'function') {
+        this.onabort(new ProgressEvent('abort', {target: this}));
+    }
+    // If load end callback
+    if (typeof this.onloadend === 'function') {
+        this.onloadend(new ProgressEvent('loadend', {target: this}));
+    }
+};
+
+/**
+ * Read text file.
+ *
+ * @param file          {File} File object containing file properties
+ * @param encoding      [Optional] (see http://www.iana.org/assignments/character-sets)
+ */
+FileReader.prototype.readAsText = function (file, encoding) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsText(file, encoding);
+    }
+
+    // Default encoding is UTF-8
+    var enc = encoding || 'UTF-8';
+
+    var totalSize = file.end - file.start;
+    readSuccessCallback.bind(this)('readAsText', enc, file.start, totalSize, function (r) {
+        if (this._progress === 0) {
+            this._result = '';
+        }
+        this._result += r;
+    }.bind(this));
+};
+
+/**
+ * Read file and return data as a base64 encoded data url.
+ * A data url is of the form:
+ *      data:[<mediatype>][;base64],<data>
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsDataURL = function (file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsDataURL(file);
+    }
+
+    var totalSize = file.end - file.start;
+    readSuccessCallback.bind(this)('readAsDataURL', null, file.start, totalSize, function (r) {
+        var commaIndex = r.indexOf(',');
+        if (this._progress === 0) {
+            this._result = r;
+        } else {
+            this._result += r.substring(commaIndex + 1);
+        }
+    }.bind(this));
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsBinaryString = function (file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsBinaryString(file);
+    }
+
+    var totalSize = file.end - file.start;
+    readSuccessCallback.bind(this)('readAsBinaryString', null, file.start, totalSize, function (r) {
+        if (this._progress === 0) {
+            this._result = '';
+        }
+        this._result += r;
+    }.bind(this));
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsArrayBuffer = function (file) {
+    if (initRead(this, file)) {
+        return this._realReader.readAsArrayBuffer(file);
+    }
+
+    var totalSize = file.end - file.start;
+    readSuccessCallback.bind(this)('readAsArrayBuffer', null, file.start, totalSize, function (r) {
+        var resultArray = (this._progress === 0 ? new Uint8Array(totalSize) : new Uint8Array(this._result));
+        resultArray.set(new Uint8Array(r), this._progress);
+        this._result = resultArray.buffer;
+    }.bind(this));
+};
+
+module.exports = FileReader;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileSystem.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileSystem.js
new file mode 100644
index 0000000..f7e751e
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileSystem.js
@@ -0,0 +1,57 @@
+cordova.define("cordova-plugin-file.FileSystem", function(require, exports, module) { /*
+ *
+ * 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 DirectoryEntry = require('./DirectoryEntry');
+
+/**
+ * An interface representing a file system
+ *
+ * @constructor
+ * {DOMString} name the unique name of the file system (readonly)
+ * {DirectoryEntry} root directory of the file system (readonly)
+ */
+var FileSystem = function (name, root) {
+    this.name = name;
+    if (root) {
+        this.root = new DirectoryEntry(root.name, root.fullPath, this, root.nativeURL);
+    } else {
+        this.root = new DirectoryEntry(this.name, '/', this);
+    }
+};
+
+FileSystem.prototype.__format__ = function (fullPath, nativeUrl) {
+    return fullPath;
+};
+
+FileSystem.prototype.toJSON = function () {
+    return '<FileSystem: ' + this.name + '>';
+};
+
+// Use instead of encodeURI() when encoding just the path part of a URI rather than an entire URI.
+FileSystem.encodeURIPath = function (path) {
+    // Because # is a valid filename character, it must be encoded to prevent part of the
+    // path from being parsed as a URI fragment.
+    return encodeURI(path).replace(/#/g, '%23');
+};
+
+module.exports = FileSystem;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileUploadOptions.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileUploadOptions.js
new file mode 100644
index 0000000..908091b
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileUploadOptions.js
@@ -0,0 +1,43 @@
+cordova.define("cordova-plugin-file.FileUploadOptions", function(require, exports, module) { /*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * Options to customize the HTTP request used to upload files.
+ * @constructor
+ * @param fileKey {String}   Name of file request parameter.
+ * @param fileName {String}  Filename to be used by the server. Defaults to image.jpg.
+ * @param mimeType {String}  Mimetype of the uploaded file. Defaults to image/jpeg.
+ * @param params {Object}    Object with key: value params to send to the server.
+ * @param headers {Object}   Keys are header names, values are header values. Multiple
+ *                           headers of the same name are not supported.
+ */
+var FileUploadOptions = function (fileKey, fileName, mimeType, params, headers, httpMethod) {
+    this.fileKey = fileKey || null;
+    this.fileName = fileName || null;
+    this.mimeType = mimeType || null;
+    this.params = params || null;
+    this.headers = headers || null;
+    this.httpMethod = httpMethod || null;
+};
+
+module.exports = FileUploadOptions;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileUploadResult.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileUploadResult.js
new file mode 100644
index 0000000..a94cb9a
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileUploadResult.js
@@ -0,0 +1,32 @@
+cordova.define("cordova-plugin-file.FileUploadResult", function(require, exports, module) { /*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * FileUploadResult
+ * @constructor
+ */
+module.exports = function FileUploadResult (size, code, content) {
+    this.bytesSent = size;
+    this.responseCode = code;
+    this.response = content;
+};
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileWriter.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileWriter.js
new file mode 100644
index 0000000..1f978bb
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/FileWriter.js
@@ -0,0 +1,326 @@
+cordova.define("cordova-plugin-file.FileWriter", function(require, exports, module) { /*
+ *
+ * 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 exec = require('cordova/exec');
+var FileError = require('./FileError');
+var ProgressEvent = require('./ProgressEvent');
+
+/**
+ * This class writes to the mobile device file system.
+ *
+ * For Android:
+ *      The root directory is the root of the file system.
+ *      To write to the SD card, the file name is "sdcard/my_file.txt"
+ *
+ * @constructor
+ * @param file {File} File object containing file properties
+ * @param append if true write to the end of the file, otherwise overwrite the file
+ */
+var FileWriter = function (file) {
+    this.fileName = '';
+    this.length = 0;
+    if (file) {
+        this.localURL = file.localURL || file;
+        this.length = file.size || 0;
+    }
+    // default is to write at the beginning of the file
+    this.position = 0;
+
+    this.readyState = 0; // EMPTY
+
+    this.result = null;
+
+    // Error
+    this.error = null;
+
+    // Event handlers
+    this.onwritestart = null;   // When writing starts
+    this.onprogress = null;     // While writing the file, and reporting partial file data
+    this.onwrite = null;        // When the write has successfully completed.
+    this.onwriteend = null;     // When the request has completed (either in success or failure).
+    this.onabort = null;        // When the write has been aborted. For instance, by invoking the abort() method.
+    this.onerror = null;        // When the write has failed (see errors).
+};
+
+// States
+FileWriter.INIT = 0;
+FileWriter.WRITING = 1;
+FileWriter.DONE = 2;
+
+/**
+ * Abort writing file.
+ */
+FileWriter.prototype.abort = function () {
+    // check for invalid state
+    if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    // set error
+    this.error = new FileError(FileError.ABORT_ERR);
+
+    this.readyState = FileWriter.DONE;
+
+    // If abort callback
+    if (typeof this.onabort === 'function') {
+        this.onabort(new ProgressEvent('abort', {'target': this}));
+    }
+
+    // If write end callback
+    if (typeof this.onwriteend === 'function') {
+        this.onwriteend(new ProgressEvent('writeend', {'target': this}));
+    }
+};
+
+/**
+ * Writes data to the file
+ *
+ * @param data text or blob to be written
+ * @param isPendingBlobReadResult {Boolean} true if the data is the pending blob read operation result
+ */
+FileWriter.prototype.write = function (data, isPendingBlobReadResult) {
+
+    var that = this;
+    var supportsBinary = (typeof window.Blob !== 'undefined' && typeof window.ArrayBuffer !== 'undefined');
+    /* eslint-disable no-undef */
+    var isProxySupportBlobNatively = (cordova.platformId === 'windows8' || cordova.platformId === 'windows');
+    var isBinary;
+
+    // Check to see if the incoming data is a blob
+    if (data instanceof File || (!isProxySupportBlobNatively && supportsBinary && data instanceof Blob)) {
+        var fileReader = new FileReader();
+        /* eslint-enable no-undef */
+        fileReader.onload = function () {
+            // Call this method again, with the arraybuffer as argument
+            FileWriter.prototype.write.call(that, this.result, true /* isPendingBlobReadResult */);
+        };
+        fileReader.onerror = function () {
+            // DONE state
+            that.readyState = FileWriter.DONE;
+
+            // Save error
+            that.error = this.error;
+
+            // If onerror callback
+            if (typeof that.onerror === 'function') {
+                that.onerror(new ProgressEvent('error', {'target': that}));
+            }
+
+            // If onwriteend callback
+            if (typeof that.onwriteend === 'function') {
+                that.onwriteend(new ProgressEvent('writeend', {'target': that}));
+            }
+        };
+
+        // WRITING state
+        this.readyState = FileWriter.WRITING;
+
+        if (supportsBinary) {
+            fileReader.readAsArrayBuffer(data);
+        } else {
+            fileReader.readAsText(data);
+        }
+        return;
+    }
+
+    // Mark data type for safer transport over the binary bridge
+    isBinary = supportsBinary && (data instanceof ArrayBuffer);
+    if (isBinary && cordova.platformId === 'windowsphone') { // eslint-disable-line no-undef
+        // create a plain array, using the keys from the Uint8Array view so that we can serialize it
+        data = Array.apply(null, new Uint8Array(data));
+    }
+
+    // Throw an exception if we are already writing a file
+    if (this.readyState === FileWriter.WRITING && !isPendingBlobReadResult) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    // WRITING state
+    this.readyState = FileWriter.WRITING;
+
+    var me = this;
+
+    // If onwritestart callback
+    if (typeof me.onwritestart === 'function') {
+        me.onwritestart(new ProgressEvent('writestart', {'target': me}));
+    }
+
+    // Write file
+    exec(
+        // Success callback
+        function (r) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // position always increases by bytes written because file would be extended
+            me.position += r;
+            // The length of the file is now where we are done writing.
+
+            me.length = me.position;
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // If onwrite callback
+            if (typeof me.onwrite === 'function') {
+                me.onwrite(new ProgressEvent('write', {'target': me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === 'function') {
+                me.onwriteend(new ProgressEvent('writeend', {'target': me}));
+            }
+        },
+        // Error callback
+        function (e) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // Save error
+            me.error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === 'function') {
+                me.onerror(new ProgressEvent('error', {'target': me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === 'function') {
+                me.onwriteend(new ProgressEvent('writeend', {'target': me}));
+            }
+        }, 'File', 'write', [this.localURL, data, this.position, isBinary]);
+};
+
+/**
+ * Moves the file pointer to the location specified.
+ *
+ * If the offset is a negative number the position of the file
+ * pointer is rewound.  If the offset is greater than the file
+ * size the position is set to the end of the file.
+ *
+ * @param offset is the location to move the file pointer to.
+ */
+FileWriter.prototype.seek = function (offset) {
+    // Throw an exception if we are already writing a file
+    if (this.readyState === FileWriter.WRITING) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    if (!offset && offset !== 0) {
+        return;
+    }
+
+    // See back from end of file.
+    if (offset < 0) {
+        this.position = Math.max(offset + this.length, 0);
+    // Offset is bigger than file size so set position
+    // to the end of the file.
+    } else if (offset > this.length) {
+        this.position = this.length;
+    // Offset is between 0 and file size so set the position
+    // to start writing.
+    } else {
+        this.position = offset;
+    }
+};
+
+/**
+ * Truncates the file to the size specified.
+ *
+ * @param size to chop the file at.
+ */
+FileWriter.prototype.truncate = function (size) {
+    // Throw an exception if we are already writing a file
+    if (this.readyState === FileWriter.WRITING) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    // WRITING state
+    this.readyState = FileWriter.WRITING;
+
+    var me = this;
+
+    // If onwritestart callback
+    if (typeof me.onwritestart === 'function') {
+        me.onwritestart(new ProgressEvent('writestart', {'target': this}));
+    }
+
+    // Write file
+    exec(
+        // Success callback
+        function (r) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // Update the length of the file
+            me.length = r;
+            me.position = Math.min(me.position, r);
+
+            // If onwrite callback
+            if (typeof me.onwrite === 'function') {
+                me.onwrite(new ProgressEvent('write', {'target': me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === 'function') {
+                me.onwriteend(new ProgressEvent('writeend', {'target': me}));
+            }
+        },
+        // Error callback
+        function (e) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // Save error
+            me.error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === 'function') {
+                me.onerror(new ProgressEvent('error', {'target': me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === 'function') {
+                me.onwriteend(new ProgressEvent('writeend', {'target': me}));
+            }
+        }, 'File', 'truncate', [this.localURL, size]);
+};
+
+module.exports = FileWriter;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/Flags.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/Flags.js
new file mode 100644
index 0000000..f51db30
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/Flags.js
@@ -0,0 +1,38 @@
+cordova.define("cordova-plugin-file.Flags", function(require, exports, module) { /*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * Supplies arguments to methods that lookup or create files and directories.
+ *
+ * @param create
+ *            {boolean} file or directory if it doesn't exist
+ * @param exclusive
+ *            {boolean} used with create; if true the command will fail if
+ *            target path exists
+ */
+function Flags (create, exclusive) {
+    this.create = create || false;
+    this.exclusive = exclusive || false;
+}
+
+module.exports = Flags;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/LocalFileSystem.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/LocalFileSystem.js
new file mode 100644
index 0000000..e424c10
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/LocalFileSystem.js
@@ -0,0 +1,25 @@
+cordova.define("cordova-plugin-file.LocalFileSystem", function(require, exports, module) { /*
+ *
+ * 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.TEMPORARY = 0;
+exports.PERSISTENT = 1;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/Metadata.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/Metadata.js
new file mode 100644
index 0000000..5550f8a
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/Metadata.js
@@ -0,0 +1,42 @@
+cordova.define("cordova-plugin-file.Metadata", function(require, exports, module) { /*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * Information about the state of the file or directory
+ *
+ * {Date} modificationTime (readonly)
+ */
+var Metadata = function (metadata) {
+    if (typeof metadata === 'object') {
+        this.modificationTime = new Date(metadata.modificationTime);
+        this.size = metadata.size || 0;
+    } else if (typeof metadata === 'undefined') {
+        this.modificationTime = null;
+        this.size = 0;
+    } else {
+        /* Backwards compatiblity with platforms that only return a timestamp */
+        this.modificationTime = new Date(metadata);
+    }
+};
+
+module.exports = Metadata;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/ProgressEvent.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/ProgressEvent.js
new file mode 100644
index 0000000..0315adf
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/ProgressEvent.js
@@ -0,0 +1,69 @@
+cordova.define("cordova-plugin-file.ProgressEvent", function(require, exports, module) { /*
+ *
+ * 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.
+ *
+*/
+
+// If ProgressEvent exists in global context, use it already, otherwise use our own polyfill
+// Feature test: See if we can instantiate a native ProgressEvent;
+// if so, use that approach,
+// otherwise fill-in with our own implementation.
+//
+// NOTE: right now we always fill in with our own. Down the road would be nice if we can use whatever is native in the webview.
+var ProgressEvent = (function () {
+    /*
+    var createEvent = function(data) {
+        var event = document.createEvent('Events');
+        event.initEvent('ProgressEvent', false, false);
+        if (data) {
+            for (var i in data) {
+                if (data.hasOwnProperty(i)) {
+                    event[i] = data[i];
+                }
+            }
+            if (data.target) {
+                // TODO: cannot call <some_custom_object>.dispatchEvent
+                // need to first figure out how to implement EventTarget
+            }
+        }
+        return event;
+    };
+    try {
+        var ev = createEvent({type:"abort",target:document});
+        return function ProgressEvent(type, data) {
+            data.type = type;
+            return createEvent(data);
+        };
+    } catch(e){
+    */
+    return function ProgressEvent (type, dict) {
+        this.type = type;
+        this.bubbles = false;
+        this.cancelBubble = false;
+        this.cancelable = false;
+        this.lengthComputable = false;
+        this.loaded = dict && dict.loaded ? dict.loaded : 0;
+        this.total = dict && dict.total ? dict.total : 0;
+        this.target = dict && dict.target ? dict.target : null;
+    };
+    // }
+})();
+
+module.exports = ProgressEvent;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/browser/FileSystem.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/browser/FileSystem.js
new file mode 100644
index 0000000..0f49c2c
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/browser/FileSystem.js
@@ -0,0 +1,32 @@
+cordova.define("cordova-plugin-file.firefoxFileSystem", function(require, exports, module) { /*
+ *
+ * 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.
+ *
+*/
+
+/* global FILESYSTEM_PREFIX: true, module */
+
+FILESYSTEM_PREFIX = 'file:///';
+
+module.exports = {
+    __format__: function (fullPath) {
+        return (FILESYSTEM_PREFIX + this.name + (fullPath[0] === '/' ? '' : '/') + FileSystem.encodeURIPath(fullPath)); // eslint-disable-line no-undef
+    }
+};
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/browser/Preparing.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/browser/Preparing.js
new file mode 100644
index 0000000..24ea404
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/browser/Preparing.js
@@ -0,0 +1,194 @@
+cordova.define("cordova-plugin-file.Preparing", function(require, exports, module) { /*
+ *
+ * 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 () {
+    /* global require */
+
+    // Only Chrome uses this file.
+    if (!require('./isChrome')()) {
+        return;
+    }
+
+    var channel = require('cordova/channel');
+    var FileError = require('./FileError');
+    var PERSISTENT_FS_QUOTA = 5 * 1024 * 1024;
+    var filePluginIsReadyEvent = new Event('filePluginIsReady'); // eslint-disable-line no-undef
+
+    var entryFunctionsCreated = false;
+    var quotaWasRequested = false;
+    var eventWasThrown = false;
+
+    if (!window.requestFileSystem) {
+        window.requestFileSystem = function (type, size, win, fail) {
+            if (fail) {
+                fail('Not supported');
+            }
+        };
+    } else {
+        window.requestFileSystem(window.TEMPORARY, 1, createFileEntryFunctions, function () {});
+    }
+
+    if (!window.resolveLocalFileSystemURL) {
+        window.resolveLocalFileSystemURL = function (url, win, fail) {
+            if (fail) {
+                fail('Not supported');
+            }
+        };
+    }
+
+    // Resolves a filesystem entry by its path - which is passed either in standard (filesystem:file://) or
+    // Cordova-specific (cdvfile://) universal way.
+    // Aligns with specification: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#widl-LocalFileSystem-resolveLocalFileSystemURL
+    var nativeResolveLocalFileSystemURL = window.resolveLocalFileSystemURL || window.webkitResolveLocalFileSystemURL;
+    window.resolveLocalFileSystemURL = function (url, win, fail) {
+        /* If url starts with `cdvfile` then we need convert it to Chrome real url first:
+          cdvfile://localhost/persistent/path/to/file -> filesystem:file://persistent/path/to/file */
+        if (url.trim().substr(0, 7) === 'cdvfile') {
+            /* Quirk:
+            Plugin supports cdvfile://localhost (local resources) only.
+            I.e. external resources are not supported via cdvfile. */
+            if (url.indexOf('cdvfile://localhost') !== -1) {
+                // Browser supports temporary and persistent only
+                var indexPersistent = url.indexOf('persistent');
+                var indexTemporary = url.indexOf('temporary');
+
+                /* Chrome urls start with 'filesystem:' prefix. See quirk:
+                   toURL function in Chrome returns filesystem:-prefixed path depending on application host.
+                   For example, filesystem:file:///persistent/somefile.txt,
+                   filesystem:http://localhost:8080/persistent/somefile.txt. */
+                var prefix = 'filesystem:file:///';
+                if (location.protocol !== 'file:') { // eslint-disable-line no-undef
+                    prefix = 'filesystem:' + location.origin + '/'; // eslint-disable-line no-undef
+                }
+
+                var result;
+                if (indexPersistent !== -1) {
+                    // cdvfile://localhost/persistent/path/to/file -> filesystem:file://persistent/path/to/file
+                    // or filesystem:http://localhost:8080/persistent/path/to/file
+                    result = prefix + 'persistent' + url.substr(indexPersistent + 10);
+                    nativeResolveLocalFileSystemURL(result, win, fail);
+                    return;
+                }
+
+                if (indexTemporary !== -1) {
+                    // cdvfile://localhost/temporary/path/to/file -> filesystem:file://temporary/path/to/file
+                    // or filesystem:http://localhost:8080/temporary/path/to/file
+                    result = prefix + 'temporary' + url.substr(indexTemporary + 9);
+                    nativeResolveLocalFileSystemURL(result, win, fail);
+                    return;
+                }
+            }
+
+            // cdvfile other than local file resource is not supported
+            if (fail) {
+                fail(new FileError(FileError.ENCODING_ERR));
+            }
+        } else {
+            nativeResolveLocalFileSystemURL(url, win, fail);
+        }
+    };
+
+    function createFileEntryFunctions (fs) {
+        fs.root.getFile('todelete_658674_833_4_cdv', {create: true}, function (fileEntry) {
+            var fileEntryType = Object.getPrototypeOf(fileEntry);
+            var entryType = Object.getPrototypeOf(fileEntryType);
+
+            // Save the original method
+            var origToURL = entryType.toURL;
+            entryType.toURL = function () {
+                var origURL = origToURL.call(this);
+                if (this.isDirectory && origURL.substr(-1) !== '/') {
+                    return origURL + '/';
+                }
+                return origURL;
+            };
+
+            entryType.toNativeURL = function () {
+                console.warn("DEPRECATED: Update your code to use 'toURL'");
+                return this.toURL();
+            };
+
+            entryType.toInternalURL = function () {
+                if (this.toURL().indexOf('persistent') > -1) {
+                    return 'cdvfile://localhost/persistent' + this.fullPath;
+                }
+
+                if (this.toURL().indexOf('temporary') > -1) {
+                    return 'cdvfile://localhost/temporary' + this.fullPath;
+                }
+            };
+
+            entryType.setMetadata = function (win, fail /*, metadata */) {
+                if (fail) {
+                    fail('Not supported');
+                }
+            };
+
+            fileEntry.createWriter(function (writer) {
+                var originalWrite = writer.write;
+                var writerProto = Object.getPrototypeOf(writer);
+                writerProto.write = function (blob) {
+                    if (blob instanceof Blob) { // eslint-disable-line no-undef
+                        originalWrite.apply(this, [blob]);
+                    } else {
+                        var realBlob = new Blob([blob]); // eslint-disable-line no-undef
+                        originalWrite.apply(this, [realBlob]);
+                    }
+                };
+
+                fileEntry.remove(function () { entryFunctionsCreated = true; }, function () { /* empty callback */ });
+            });
+        });
+    }
+
+    window.initPersistentFileSystem = function (size, win, fail) {
+        if (navigator.webkitPersistentStorage) {
+            navigator.webkitPersistentStorage.requestQuota(size, win, fail);
+            return;
+        }
+
+        fail('This browser does not support this function');
+    };
+
+    window.isFilePluginReadyRaised = function () { return eventWasThrown; };
+
+    window.initPersistentFileSystem(PERSISTENT_FS_QUOTA, function () {
+        console.log('Persistent fs quota granted');
+        quotaWasRequested = true;
+    }, function (e) {
+        console.log('Error occured while trying to request Persistent fs quota: ' + JSON.stringify(e));
+    });
+
+    channel.onCordovaReady.subscribe(function () {
+        function dispatchEventIfReady () {
+            if (entryFunctionsCreated && quotaWasRequested) {
+                window.dispatchEvent(filePluginIsReadyEvent);
+                eventWasThrown = true;
+            } else {
+                setTimeout(dispatchEventIfReady, 100);
+            }
+        }
+
+        dispatchEventIfReady();
+    }, false);
+})();
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/browser/isChrome.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/browser/isChrome.js
new file mode 100644
index 0000000..1421b21
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/browser/isChrome.js
@@ -0,0 +1,28 @@
+cordova.define("cordova-plugin-file.isChrome", function(require, exports, module) { /*

+ *

+ * 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 = function () {

+    // window.webkitRequestFileSystem and window.webkitResolveLocalFileSystemURL are available only in Chrome and

+    // possibly a good flag to indicate that we're running in Chrome

+    return window.webkitRequestFileSystem && window.webkitResolveLocalFileSystemURL;

+};

+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/fileSystemPaths.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/fileSystemPaths.js
new file mode 100644
index 0000000..be7278f
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/fileSystemPaths.js
@@ -0,0 +1,64 @@
+cordova.define("cordova-plugin-file.fileSystemPaths", function(require, exports, module) { /*
+ *
+ * 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 exec = require('cordova/exec');
+var channel = require('cordova/channel');
+
+exports.file = {
+    // Read-only directory where the application is installed.
+    applicationDirectory: null,
+    // Root of app's private writable storage
+    applicationStorageDirectory: null,
+    // Where to put app-specific data files.
+    dataDirectory: null,
+    // Cached files that should survive app restarts.
+    // Apps should not rely on the OS to delete files in here.
+    cacheDirectory: null,
+    // Android: the application space on external storage.
+    externalApplicationStorageDirectory: null,
+    // Android: Where to put app-specific data files on external storage.
+    externalDataDirectory: null,
+    // Android: the application cache on external storage.
+    externalCacheDirectory: null,
+    // Android: the external storage (SD card) root.
+    externalRootDirectory: null,
+    // iOS: Temp directory that the OS can clear at will.
+    tempDirectory: null,
+    // iOS: Holds app-specific files that should be synced (e.g. to iCloud).
+    syncedDataDirectory: null,
+    // iOS: Files private to the app, but that are meaningful to other applications (e.g. Office files)
+    documentsDirectory: null,
+    // BlackBerry10: Files globally available to all apps
+    sharedDirectory: null
+};
+
+channel.waitForInitialization('onFileSystemPathsReady');
+channel.onCordovaReady.subscribe(function () {
+    function after (paths) {
+        for (var k in paths) {
+            exports.file[k] = paths[k];
+        }
+        channel.initializationComplete('onFileSystemPathsReady');
+    }
+    exec(after, null, 'File', 'requestAllPaths', []);
+});
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/fileSystems.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/fileSystems.js
new file mode 100644
index 0000000..99b38f6
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/fileSystems.js
@@ -0,0 +1,27 @@
+cordova.define("cordova-plugin-file.fileSystems", function(require, exports, module) { /*
+ *
+ * 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.
+ *
+*/
+
+// Overridden by Android, BlackBerry 10 and iOS to populate fsMap.
+module.exports.getFs = function (name, callback) {
+    callback(null);
+};
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/requestFileSystem.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/requestFileSystem.js
new file mode 100644
index 0000000..e182354
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/requestFileSystem.js
@@ -0,0 +1,83 @@
+cordova.define("cordova-plugin-file.requestFileSystem", function(require, exports, module) { /*
+ *
+ * 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 () {
+    // For browser platform: not all browsers use this file.
+    function checkBrowser () {
+        if (cordova.platformId === 'browser' && require('./isChrome')()) { // eslint-disable-line no-undef
+            module.exports = window.requestFileSystem || window.webkitRequestFileSystem;
+            return true;
+        }
+        return false;
+    }
+    if (checkBrowser()) {
+        return;
+    }
+
+    var argscheck = require('cordova/argscheck');
+    var FileError = require('./FileError');
+    var FileSystem = require('./FileSystem');
+    var exec = require('cordova/exec');
+    var fileSystems = require('./fileSystems');
+
+    /**
+     * Request a file system in which to store application data.
+     * @param type  local file system type
+     * @param size  indicates how much storage space, in bytes, the application expects to need
+     * @param successCallback  invoked with a FileSystem object
+     * @param errorCallback  invoked if error occurs retrieving file system
+     */
+    var requestFileSystem = function (type, size, successCallback, errorCallback) {
+        argscheck.checkArgs('nnFF', 'requestFileSystem', arguments);
+        var fail = function (code) {
+            if (errorCallback) {
+                errorCallback(new FileError(code));
+            }
+        };
+
+        if (type < 0) {
+            fail(FileError.SYNTAX_ERR);
+        } else {
+            // if successful, return a FileSystem object
+            var success = function (file_system) {
+                if (file_system) {
+                    if (successCallback) {
+                        fileSystems.getFs(file_system.name, function (fs) {
+                            // This should happen only on platforms that haven't implemented requestAllFileSystems (windows)
+                            if (!fs) {
+                                fs = new FileSystem(file_system.name, file_system.root);
+                            }
+                            successCallback(fs);
+                        });
+                    }
+                } else {
+                    // no FileSystem object returned
+                    fail(FileError.NOT_FOUND_ERR);
+                }
+            };
+            exec(success, fail, 'File', 'requestFileSystem', [type, size]);
+        }
+    };
+
+    module.exports = requestFileSystem;
+})();
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-file/www/resolveLocalFileSystemURI.js b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/resolveLocalFileSystemURI.js
new file mode 100644
index 0000000..659d417
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-file/www/resolveLocalFileSystemURI.js
@@ -0,0 +1,93 @@
+cordova.define("cordova-plugin-file.resolveLocalFileSystemURI", function(require, exports, module) { /*
+ *
+ * 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 () {
+    // For browser platform: not all browsers use overrided `resolveLocalFileSystemURL`.
+    function checkBrowser () {
+        if (cordova.platformId === 'browser' && require('./isChrome')()) { // eslint-disable-line no-undef
+            module.exports.resolveLocalFileSystemURL = window.resolveLocalFileSystemURL || window.webkitResolveLocalFileSystemURL;
+            return true;
+        }
+        return false;
+    }
+    if (checkBrowser()) {
+        return;
+    }
+
+    var argscheck = require('cordova/argscheck');
+    var DirectoryEntry = require('./DirectoryEntry');
+    var FileEntry = require('./FileEntry');
+    var FileError = require('./FileError');
+    var exec = require('cordova/exec');
+    var fileSystems = require('./fileSystems');
+
+    /**
+     * Look up file system Entry referred to by local URI.
+     * @param {DOMString} uri  URI referring to a local file or directory
+     * @param successCallback  invoked with Entry object corresponding to URI
+     * @param errorCallback    invoked if error occurs retrieving file system entry
+     */
+    module.exports.resolveLocalFileSystemURL = module.exports.resolveLocalFileSystemURL || function (uri, successCallback, errorCallback) {
+        argscheck.checkArgs('sFF', 'resolveLocalFileSystemURI', arguments);
+        // error callback
+        var fail = function (error) {
+            if (errorCallback) {
+                errorCallback(new FileError(error));
+            }
+        };
+        // sanity check for 'not:valid:filename' or '/not:valid:filename'
+        // file.spec.12 window.resolveLocalFileSystemURI should error (ENCODING_ERR) when resolving invalid URI with leading /.
+        if (!uri || uri.split(':').length > 2) {
+            setTimeout(function () {
+                fail(FileError.ENCODING_ERR);
+            }, 0);
+            return;
+        }
+        // if successful, return either a file or directory entry
+        var success = function (entry) {
+            if (entry) {
+                if (successCallback) {
+                    // create appropriate Entry object
+                    var fsName = entry.filesystemName || (entry.filesystem && entry.filesystem.name) || (entry.filesystem === window.PERSISTENT ? 'persistent' : 'temporary'); // eslint-disable-line no-undef
+                    fileSystems.getFs(fsName, function (fs) {
+                        // This should happen only on platforms that haven't implemented requestAllFileSystems (windows)
+                        if (!fs) {
+                            fs = new FileSystem(fsName, {name: '', fullPath: '/'}); // eslint-disable-line no-undef
+                        }
+                        var result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath, fs, entry.nativeURL) : new FileEntry(entry.name, entry.fullPath, fs, entry.nativeURL);
+                        successCallback(result);
+                    });
+                }
+            } else {
+                // no Entry object returned
+                fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+
+        exec(success, fail, 'File', 'resolveLocalFileSystemURI', [uri]);
+    };
+
+    module.exports.resolveLocalFileSystemURI = function () {
+        console.log('resolveLocalFileSystemURI is deprecated. Please call resolveLocalFileSystemURL instead.');
+        module.exports.resolveLocalFileSystemURL.apply(this, arguments);
+    };
+})();
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js b/platforms/browser/platform_www/plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js
new file mode 100644
index 0000000..6c8203d
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js
@@ -0,0 +1,28 @@
+cordova.define("cordova-plugin-fingerprint-aio.Fingerprint", function(require, exports, module) { /*global cordova */
+
+function Fingerprint() {
+}
+
+Fingerprint.prototype.show = function (params, successCallback, errorCallback) {
+  cordova.exec(
+    successCallback,
+    errorCallback,
+    "Fingerprint",
+    "authenticate",
+    [params]
+  );
+};
+
+Fingerprint.prototype.isAvailable = function (successCallback, errorCallback) {
+  cordova.exec(
+    successCallback,
+    errorCallback,
+    "Fingerprint",
+    "isAvailable",
+    [{}]
+  );
+};
+
+module.exports = new Fingerprint();
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-inappbrowser/src/browser/InAppBrowserProxy.js b/platforms/browser/platform_www/plugins/cordova-plugin-inappbrowser/src/browser/InAppBrowserProxy.js
new file mode 100644
index 0000000..86e120f
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-inappbrowser/src/browser/InAppBrowserProxy.js
@@ -0,0 +1,249 @@
+cordova.define("cordova-plugin-inappbrowser.InAppBrowserProxy", function(require, exports, module) { /*
+ *
+ * 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 modulemapper = require('cordova/modulemapper');
+
+var browserWrap,
+    popup,
+    navigationButtonsDiv,
+    navigationButtonsDivInner,
+    backButton,
+    forwardButton,
+    closeButton;
+
+function attachNavigationEvents (element, callback) {
+    var onError = function () {
+        try {
+            callback({ type: 'loaderror', url: this.contentWindow.location.href }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal
+        } catch (err) {
+            // blocked by CORS :\
+            callback({ type: 'loaderror', url: null }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal
+        }
+    };
+
+    element.addEventListener('pageshow', function () {
+        try {
+            callback({ type: 'loadstart', url: this.contentWindow.location.href }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal
+        } catch (err) {
+            // blocked by CORS :\
+            callback({ type: 'loadstart', url: null }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal
+        }
+    });
+
+    element.addEventListener('load', function () {
+        try {
+            callback({ type: 'loadstop', url: this.contentWindow.location.href }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal
+        } catch (err) {
+            // blocked by CORS :\
+            callback({ type: 'loadstop', url: null }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal
+        }
+    });
+
+    element.addEventListener('error', onError);
+    element.addEventListener('abort', onError);
+}
+
+var IAB = {
+    close: function (win, lose) {
+        if (browserWrap) {
+            // use the "open" function callback so that the exit event is fired properly
+            if (IAB._win) IAB._win({ type: 'exit' });
+
+            browserWrap.parentNode.removeChild(browserWrap);
+            browserWrap = null;
+            popup = null;
+        }
+    },
+
+    show: function (win, lose) {
+        if (browserWrap) {
+            browserWrap.style.display = 'block';
+        }
+    },
+
+    open: function (win, lose, args) {
+        var strUrl = args[0];
+        var target = args[1];
+        var features = args[2];
+
+        IAB._win = win;
+
+        if (target === '_self' || !target) {
+            window.location = strUrl;
+        } else if (target === '_system') {
+            modulemapper.getOriginalSymbol(window, 'window.open').call(window, strUrl, '_blank');
+        } else {
+            // "_blank" or anything else
+            if (!browserWrap) {
+                browserWrap = document.createElement('div');
+                browserWrap.style.position = 'absolute';
+                browserWrap.style.top = '0';
+                browserWrap.style.left = '0';
+                browserWrap.style.boxSizing = 'border-box';
+                browserWrap.style.borderWidth = '40px';
+                browserWrap.style.width = '100vw';
+                browserWrap.style.height = '100vh';
+                browserWrap.style.borderStyle = 'solid';
+                browserWrap.style.borderColor = 'rgba(0,0,0,0.25)';
+
+                browserWrap.onclick = function () {
+                    setTimeout(function () {
+                        IAB.close();
+                    }, 0);
+                };
+
+                document.body.appendChild(browserWrap);
+            }
+
+            if (features.indexOf('hidden=yes') !== -1) {
+                browserWrap.style.display = 'none';
+            }
+
+            popup = document.createElement('iframe');
+            popup.style.borderWidth = '0px';
+            popup.style.width = '100%';
+
+            browserWrap.appendChild(popup);
+
+            if (features.indexOf('location=yes') !== -1 || features.indexOf('location') === -1) {
+                popup.style.height = 'calc(100% - 60px)';
+                popup.style.marginBottom = '-4px';
+
+                navigationButtonsDiv = document.createElement('div');
+                navigationButtonsDiv.style.height = '60px';
+                navigationButtonsDiv.style.backgroundColor = '#404040';
+                navigationButtonsDiv.style.zIndex = '999';
+                navigationButtonsDiv.onclick = function (e) {
+                    e.cancelBubble = true;
+                };
+
+                navigationButtonsDivInner = document.createElement('div');
+                navigationButtonsDivInner.style.paddingTop = '10px';
+                navigationButtonsDivInner.style.height = '50px';
+                navigationButtonsDivInner.style.width = '160px';
+                navigationButtonsDivInner.style.margin = '0 auto';
+                navigationButtonsDivInner.style.backgroundColor = '#404040';
+                navigationButtonsDivInner.style.zIndex = '999';
+                navigationButtonsDivInner.onclick = function (e) {
+                    e.cancelBubble = true;
+                };
+
+                backButton = document.createElement('button');
+                backButton.style.width = '40px';
+                backButton.style.height = '40px';
+                backButton.style.borderRadius = '40px';
+
+                backButton.innerHTML = '←';
+                backButton.addEventListener('click', function (e) {
+                    if (popup.canGoBack) { popup.goBack(); }
+                });
+
+                forwardButton = document.createElement('button');
+                forwardButton.style.marginLeft = '20px';
+                forwardButton.style.width = '40px';
+                forwardButton.style.height = '40px';
+                forwardButton.style.borderRadius = '40px';
+
+                forwardButton.innerHTML = '→';
+                forwardButton.addEventListener('click', function (e) {
+                    if (popup.canGoForward) { popup.goForward(); }
+                });
+
+                closeButton = document.createElement('button');
+                closeButton.style.marginLeft = '20px';
+                closeButton.style.width = '40px';
+                closeButton.style.height = '40px';
+                closeButton.style.borderRadius = '40px';
+
+                closeButton.innerHTML = '✖';
+                closeButton.addEventListener('click', function (e) {
+                    setTimeout(function () {
+                        IAB.close();
+                    }, 0);
+                });
+
+                // iframe navigation is not yet supported
+                backButton.disabled = true;
+                forwardButton.disabled = true;
+
+                navigationButtonsDivInner.appendChild(backButton);
+                navigationButtonsDivInner.appendChild(forwardButton);
+                navigationButtonsDivInner.appendChild(closeButton);
+                navigationButtonsDiv.appendChild(navigationButtonsDivInner);
+
+                browserWrap.appendChild(navigationButtonsDiv);
+            } else {
+                popup.style.height = '100%';
+            }
+
+            // start listening for navigation events
+            attachNavigationEvents(popup, win);
+
+            popup.src = strUrl;
+        }
+    },
+
+    injectScriptCode: function (win, fail, args) {
+        var code = args[0];
+        var hasCallback = args[1];
+
+        if (browserWrap && popup) {
+            try {
+                popup.contentWindow.eval(code);
+                if (hasCallback) {
+                    win([]);
+                }
+            } catch (e) {
+                console.error('Error occured while trying to injectScriptCode: ' + JSON.stringify(e));
+            }
+        }
+    },
+
+    injectScriptFile: function (win, fail, args) {
+        var msg = 'Browser cordova-plugin-inappbrowser injectScriptFile is not yet implemented';
+        console.warn(msg);
+        if (fail) {
+            fail(msg);
+        }
+    },
+
+    injectStyleCode: function (win, fail, args) {
+        var msg = 'Browser cordova-plugin-inappbrowser injectStyleCode is not yet implemented';
+        console.warn(msg);
+        if (fail) {
+            fail(msg);
+        }
+    },
+
+    injectStyleFile: function (win, fail, args) {
+        var msg = 'Browser cordova-plugin-inappbrowser injectStyleFile is not yet implemented';
+        console.warn(msg);
+        if (fail) {
+            fail(msg);
+        }
+    }
+};
+
+module.exports = IAB;
+
+require('cordova/exec/proxy').add('InAppBrowser', module.exports);
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-inappbrowser/www/inappbrowser.js b/platforms/browser/platform_www/plugins/cordova-plugin-inappbrowser/www/inappbrowser.js
new file mode 100644
index 0000000..759dacf
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-inappbrowser/www/inappbrowser.js
@@ -0,0 +1,117 @@
+cordova.define("cordova-plugin-inappbrowser.inappbrowser", function(require, exports, module) { /*
+ *
+ * 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 () {
+    // special patch to correctly work on Ripple emulator (CB-9760)
+    if (window.parent && !!window.parent.ripple) { // https://gist.github.com/triceam/4658021
+        module.exports = window.open.bind(window); // fallback to default window.open behaviour
+        return;
+    }
+
+    var exec = require('cordova/exec');
+    var channel = require('cordova/channel');
+    var modulemapper = require('cordova/modulemapper');
+    var urlutil = require('cordova/urlutil');
+
+    function InAppBrowser () {
+        this.channels = {
+            'loadstart': channel.create('loadstart'),
+            'loadstop': channel.create('loadstop'),
+            'loaderror': channel.create('loaderror'),
+            'exit': channel.create('exit'),
+            'customscheme': channel.create('customscheme')
+        };
+    }
+
+    InAppBrowser.prototype = {
+        _eventHandler: function (event) {
+            if (event && (event.type in this.channels)) {
+                this.channels[event.type].fire(event);
+            }
+        },
+        close: function (eventname) {
+            exec(null, null, 'InAppBrowser', 'close', []);
+        },
+        show: function (eventname) {
+            exec(null, null, 'InAppBrowser', 'show', []);
+        },
+        hide: function (eventname) {
+            exec(null, null, 'InAppBrowser', 'hide', []);
+        },
+        addEventListener: function (eventname, f) {
+            if (eventname in this.channels) {
+                this.channels[eventname].subscribe(f);
+            }
+        },
+        removeEventListener: function (eventname, f) {
+            if (eventname in this.channels) {
+                this.channels[eventname].unsubscribe(f);
+            }
+        },
+
+        executeScript: function (injectDetails, cb) {
+            if (injectDetails.code) {
+                exec(cb, null, 'InAppBrowser', 'injectScriptCode', [injectDetails.code, !!cb]);
+            } else if (injectDetails.file) {
+                exec(cb, null, 'InAppBrowser', 'injectScriptFile', [injectDetails.file, !!cb]);
+            } else {
+                throw new Error('executeScript requires exactly one of code or file to be specified');
+            }
+        },
+
+        insertCSS: function (injectDetails, cb) {
+            if (injectDetails.code) {
+                exec(cb, null, 'InAppBrowser', 'injectStyleCode', [injectDetails.code, !!cb]);
+            } else if (injectDetails.file) {
+                exec(cb, null, 'InAppBrowser', 'injectStyleFile', [injectDetails.file, !!cb]);
+            } else {
+                throw new Error('insertCSS requires exactly one of code or file to be specified');
+            }
+        }
+    };
+
+    module.exports = function (strUrl, strWindowName, strWindowFeatures, callbacks) {
+        // Don't catch calls that write to existing frames (e.g. named iframes).
+        if (window.frames && window.frames[strWindowName]) {
+            var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
+            return origOpenFunc.apply(window, arguments);
+        }
+
+        strUrl = urlutil.makeAbsolute(strUrl);
+        var iab = new InAppBrowser();
+
+        callbacks = callbacks || {};
+        for (var callbackName in callbacks) {
+            iab.addEventListener(callbackName, callbacks[callbackName]);
+        }
+
+        var cb = function (eventname) {
+            iab._eventHandler(eventname);
+        };
+
+        strWindowFeatures = strWindowFeatures || '';
+
+        exec(cb, cb, 'InAppBrowser', 'open', [strUrl, strWindowName, strWindowFeatures]);
+        return iab;
+    };
+})();
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-qrscanner/src/browser/plugin.min.js b/platforms/browser/platform_www/plugins/cordova-plugin-qrscanner/src/browser/plugin.min.js
new file mode 100644
index 0000000..44888a7
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-qrscanner/src/browser/plugin.min.js
@@ -0,0 +1,4243 @@
+cordova.define("cordova-plugin-qrscanner.QRScannerProxy", function(require, exports, module) { // This file is generated by `npm run build`.
+
+/*global exports:false*/
+/*jshint unused:false */
+// remap parameter names from cordova.define
+// see `externals` in webpack.cordova.config.js
+var cordovaRequire = require;
+var cordovaExports = exports;
+var cordovaModule = module;
+/******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+/******/
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+/******/
+/******/ 	// identity function for calling harmony imports with the correct context
+/******/ 	__webpack_require__.i = function(value) { return value; };
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, {
+/******/ 				configurable: false,
+/******/ 				enumerable: true,
+/******/ 				get: getter
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 17);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+ /* eslint-env node */
+
+
+var logDisabled_ = true;
+
+// Utility methods.
+var utils = {
+  disableLog: function(bool) {
+    if (typeof bool !== 'boolean') {
+      return new Error('Argument type: ' + typeof bool +
+          '. Please use a boolean.');
+    }
+    logDisabled_ = bool;
+    return (bool) ? 'adapter.js logging disabled' :
+        'adapter.js logging enabled';
+  },
+
+  log: function() {
+    if (typeof window === 'object') {
+      if (logDisabled_) {
+        return;
+      }
+      if (typeof console !== 'undefined' && typeof console.log === 'function') {
+        console.log.apply(console, arguments);
+      }
+    }
+  },
+
+  /**
+   * Extract browser version out of the provided user agent string.
+   *
+   * @param {!string} uastring userAgent string.
+   * @param {!string} expr Regular expression used as match criteria.
+   * @param {!number} pos position in the version string to be returned.
+   * @return {!number} browser version.
+   */
+  extractVersion: function(uastring, expr, pos) {
+    var match = uastring.match(expr);
+    return match && match.length >= pos && parseInt(match[pos], 10);
+  },
+
+  /**
+   * Browser detector.
+   *
+   * @return {object} result containing browser and version
+   *     properties.
+   */
+  detectBrowser: function() {
+    // Returned result object.
+    var result = {};
+    result.browser = null;
+    result.version = null;
+
+    // Fail early if it's not a browser
+    if (typeof window === 'undefined' || !window.navigator) {
+      result.browser = 'Not a browser.';
+      return result;
+    }
+
+    // Firefox.
+    if (navigator.mozGetUserMedia) {
+      result.browser = 'firefox';
+      result.version = this.extractVersion(navigator.userAgent,
+          /Firefox\/(\d+)\./, 1);
+    } else if (navigator.webkitGetUserMedia) {
+      // Chrome, Chromium, Webview, Opera, all use the chrome shim for now
+      if (window.webkitRTCPeerConnection) {
+        result.browser = 'chrome';
+        result.version = this.extractVersion(navigator.userAgent,
+          /Chrom(e|ium)\/(\d+)\./, 2);
+      } else { // Safari (in an unpublished version) or unknown webkit-based.
+        if (navigator.userAgent.match(/Version\/(\d+).(\d+)/)) {
+          result.browser = 'safari';
+          result.version = this.extractVersion(navigator.userAgent,
+            /AppleWebKit\/(\d+)\./, 1);
+        } else { // unknown webkit-based browser.
+          result.browser = 'Unsupported webkit-based browser ' +
+              'with GUM support but no WebRTC support.';
+          return result;
+        }
+      }
+    } else if (navigator.mediaDevices &&
+        navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { // Edge.
+      result.browser = 'edge';
+      result.version = this.extractVersion(navigator.userAgent,
+          /Edge\/(\d+).(\d+)$/, 2);
+    } else if (navigator.mediaDevices &&
+        navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
+        // Safari, with webkitGetUserMedia removed.
+      result.browser = 'safari';
+      result.version = this.extractVersion(navigator.userAgent,
+          /AppleWebKit\/(\d+)\./, 1);
+    } else { // Default fallthrough: not supported.
+      result.browser = 'Not a supported browser.';
+      return result;
+    }
+
+    return result;
+  },
+
+  // shimCreateObjectURL must be called before shimSourceObject to avoid loop.
+
+  shimCreateObjectURL: function() {
+    if (!(typeof window === 'object' && window.HTMLMediaElement &&
+          'srcObject' in window.HTMLMediaElement.prototype)) {
+      // Only shim CreateObjectURL using srcObject if srcObject exists.
+      return undefined;
+    }
+
+    var nativeCreateObjectURL = URL.createObjectURL.bind(URL);
+    var nativeRevokeObjectURL = URL.revokeObjectURL.bind(URL);
+    var streams = new Map(), newId = 0;
+
+    URL.createObjectURL = function(stream) {
+      if ('getTracks' in stream) {
+        var url = 'polyblob:' + (++newId);
+        streams.set(url, stream);
+        console.log('URL.createObjectURL(stream) is deprecated! ' +
+                    'Use elem.srcObject = stream instead!');
+        return url;
+      }
+      return nativeCreateObjectURL(stream);
+    };
+    URL.revokeObjectURL = function(url) {
+      nativeRevokeObjectURL(url);
+      streams.delete(url);
+    };
+
+    var dsc = Object.getOwnPropertyDescriptor(window.HTMLMediaElement.prototype,
+                                              'src');
+    Object.defineProperty(window.HTMLMediaElement.prototype, 'src', {
+      get: function() {
+        return dsc.get.apply(this);
+      },
+      set: function(url) {
+        this.srcObject = streams.get(url) || null;
+        return dsc.set.apply(this, [url]);
+      }
+    });
+
+    var nativeSetAttribute = HTMLMediaElement.prototype.setAttribute;
+    HTMLMediaElement.prototype.setAttribute = function() {
+      if (arguments.length === 2 &&
+          ('' + arguments[0]).toLowerCase() === 'src') {
+        this.srcObject = streams.get(arguments[1]) || null;
+      }
+      return nativeSetAttribute.apply(this, arguments);
+    };
+  }
+};
+
+// Export.
+module.exports = {
+  log: utils.log,
+  disableLog: utils.disableLog,
+  browserDetails: utils.detectBrowser(),
+  extractVersion: utils.extractVersion,
+  shimCreateObjectURL: utils.shimCreateObjectURL,
+  detectBrowser: utils.detectBrowser.bind(utils)
+};
+
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports) {
+
+module.exports = cordovaModule;
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports, __webpack_require__) {
+
+__webpack_require__(8);
+var workerScript = __webpack_require__(6);
+
+module.exports = function(){
+
+  var ELEMENTS = {
+    preview: 'cordova-plugin-qrscanner-video-preview',
+    still: 'cordova-plugin-qrscanner-still'
+  };
+  var ZINDEXES = {
+    preview: -100,
+    still: -99
+  };
+  var backCamera = null;
+  var frontCamera = null;
+  var currentCamera = 0;
+  var activeMediaStream = null;
+  var scanning = false;
+  var previewing = false;
+  var scanWorker = null;
+  var thisScanCycle = null;
+  var nextScan = null;
+  var cancelNextScan = null;
+
+  // standard screen widths/heights, from 4k down to 320x240
+  // widths and heights are each tested separately to account for screen rotation
+  var standardWidthsAndHeights = [
+    5120, 4096, 3840, 3440, 3200, 3072, 3000, 2880, 2800, 2736, 2732, 2560,
+    2538, 2400, 2304, 2160, 2100, 2048, 2000, 1920, 1856, 1824, 1800, 1792,
+    1776, 1728, 1700, 1680, 1600, 1536, 1440, 1400, 1392, 1366, 1344, 1334,
+    1280, 1200, 1152, 1136, 1120, 1080, 1050, 1024, 1000, 960, 900, 854, 848,
+    832, 800, 768, 750, 720, 640, 624, 600, 576, 544, 540, 512, 480, 320, 240
+  ];
+
+  var facingModes = [
+    'environment',
+    'user'
+  ];
+
+  //utils
+  function killStream(mediaStream){
+    mediaStream.getTracks().forEach(function(track){
+      track.stop();
+    });
+  }
+
+  // For performance, we test best-to-worst constraints. Once we find a match,
+  // we move to the next test. Since `ConstraintNotSatisfiedError`s are thrown
+  // much faster than streams can be started and stopped, the scan is much
+  // faster, even though it may iterate through more constraint objects.
+  function getCameraSpecsById(deviceId){
+
+    // return a getUserMedia Constraints
+    function getConstraintObj(deviceId, facingMode, width, height){
+      var obj = { audio: false, video: {} };
+      obj.video.deviceId = {exact: deviceId};
+      if(facingMode) {
+        obj.video.facingMode = {exact: facingMode};
+      }
+      if(width) {
+        obj.video.width = {exact: width};
+      }
+      if(height) {
+        obj.video.height = {exact: height};
+      }
+      return obj;
+    }
+
+    var facingModeConstraints = facingModes.map(function(mode){
+    	return getConstraintObj(deviceId, mode);
+    });
+    var widthConstraints = standardWidthsAndHeights.map(function(width){
+    	return getConstraintObj(deviceId, null, width);
+    });
+    var heightConstraints = standardWidthsAndHeights.map(function(height){
+    	return getConstraintObj(deviceId, null, null, height);
+    });
+
+    // create a promise which tries to resolve the best constraints for this deviceId
+    // rather than reject, failures return a value of `null`
+    function getFirstResolvingConstraint(constraintsBestToWorst){
+      return new Promise(function(resolveBestConstraints){
+        // build a chain of promises which either resolves or continues searching
+        return constraintsBestToWorst.reduce(function(chain, next){
+          return chain.then(function(searchState){
+            if(searchState.found){
+              // The best working constraint was found. Skip further tests.
+              return searchState;
+            } else {
+              searchState.nextConstraint = next;
+              return window.navigator.mediaDevices.getUserMedia(searchState.nextConstraint).then(function(mediaStream){
+                // We found the first working constraint object, now we can stop
+                // the stream and short-circuit the search.
+                killStream(mediaStream);
+                searchState.found = true;
+                return searchState;
+              }, function(){
+                // didn't get a media stream. The search continues:
+                return searchState;
+              });
+            }
+          });
+        }, Promise.resolve({
+          // kick off the search:
+          found: false,
+          nextConstraint: {}
+        })).then(function(searchState){
+          if(searchState.found){
+            resolveBestConstraints(searchState.nextConstraint);
+          } else {
+            resolveBestConstraints(null);
+          }
+        });
+      });
+    }
+
+    return getFirstResolvingConstraint(facingModeConstraints).then(function(facingModeSpecs){
+      return getFirstResolvingConstraint(widthConstraints).then(function(widthSpecs){
+        return getFirstResolvingConstraint(heightConstraints).then(function(heightSpecs){
+          return {
+            deviceId: deviceId,
+            facingMode: facingModeSpecs === null ? null : facingModeSpecs.video.facingMode.exact,
+            width: widthSpecs === null ? null : widthSpecs.video.width.exact,
+            height: heightSpecs === null ? null : heightSpecs.video.height.exact
+          };
+        });
+      });
+    });
+  }
+
+  function chooseCameras(){
+    var devices = window.navigator.mediaDevices.enumerateDevices();
+    return devices.then(function(mediaDeviceInfoList){
+      var videoDeviceIds = mediaDeviceInfoList.filter(function(elem){
+        return elem.kind === 'videoinput';
+      }).map(function(elem){
+        return elem.deviceId;
+      });
+      return videoDeviceIds;
+    }).then(function(videoDeviceIds){
+      // there is no standardized way for us to get the specs of each camera
+      // (due to concerns over user fingerprinting), so we're forced to
+      // iteratively test each camera for it's capabilities
+      var searches = [];
+      videoDeviceIds.forEach(function(id){
+        searches.push(getCameraSpecsById(id));
+      });
+      return Promise.all(searches);
+    }).then(function(cameraSpecsArray){
+      return cameraSpecsArray.filter(function(camera){
+        // filter out any cameras where width and height could not be captured
+        if(camera !== null && camera.width !== null && camera.height !== null){
+          return true;
+        }
+      }).sort(function(a, b){
+        // sort cameras from highest resolution (by width) to lowest
+        return b.width - a.width;
+      });
+    }).then(function(bestToWorstCameras){
+      var backCamera = null,
+          frontCamera = null;
+      // choose backCamera
+      for(var i = 0; i < bestToWorstCameras.length; i++){
+        if (bestToWorstCameras[i].facingMode === 'environment'){
+          backCamera = bestToWorstCameras[i];
+          // (shouldn't be used for frontCamera)
+          bestToWorstCameras.splice(i, 1);
+          break;
+        }
+      }
+      // if no back-facing cameras were found, choose the highest resolution
+      if(backCamera === null){
+        if(bestToWorstCameras.length > 0){
+          backCamera = bestToWorstCameras[0];
+          // (shouldn't be used for frontCamera)
+          bestToWorstCameras.splice(0, 1);
+        } else {
+          // user doesn't have any available cameras
+          backCamera = false;
+        }
+      }
+      if(bestToWorstCameras.length > 0){
+        // frontCamera should simply be the next-best resolution camera
+        frontCamera = bestToWorstCameras[0];
+      } else {
+        // user doesn't have any more cameras
+        frontCamera = false;
+      }
+      return {
+        backCamera: backCamera,
+        frontCamera: frontCamera
+      };
+    });
+  }
+
+  function mediaStreamIsActive(){
+    return activeMediaStream !== null;
+  }
+
+  function killActiveMediaStream(){
+    killStream(activeMediaStream);
+    activeMediaStream = null;
+  }
+
+  function getVideoPreview(){
+    return document.getElementById(ELEMENTS.preview);
+  }
+
+  function getImg(){
+    return document.getElementById(ELEMENTS.still);
+  }
+
+  function getCurrentCameraIndex(){
+    return currentCamera;
+  }
+
+  function getCurrentCamera(){
+    return currentCamera === 1 ? frontCamera : backCamera;
+  }
+
+  function bringStillToFront(){
+    var img = getImg();
+    if(img){
+      img.style.visibility = 'visible';
+      previewing = false;
+    }
+  }
+
+  function bringPreviewToFront(){
+    var img = getImg();
+    if(img){
+      img.style.visibility = 'hidden';
+      previewing = true;
+    }
+  }
+
+  function isInitialized(){
+    return backCamera !== null;
+  }
+
+  function canChangeCamera(){
+    return !!backCamera && !!frontCamera;
+  }
+
+  function calcStatus(){
+    return {
+      // !authorized means the user either has no camera or has denied access.
+      // This would leave a value of `null` before prepare(), and `false` after.
+      authorized: (backCamera !== null && backCamera !== false)? '1': '0',
+      // No applicable API
+      denied: '0',
+      // No applicable API
+      restricted: '0',
+      prepared: isInitialized() ? '1' : '0',
+      scanning: scanning? '1' : '0',
+      previewing: previewing? '1' : '0',
+      // We leave this true after prepare() to match the mobile experience as
+      // closely as possible. (Without additional covering, the preview will
+      // always be visible to the user).
+      showing: getVideoPreview()? '1' : '0',
+      // No applicable API
+      lightEnabled: '0',
+      // No applicable API
+      canOpenSettings: '0',
+      // No applicable API
+      canEnableLight: '0',
+      canChangeCamera: canChangeCamera() ? '1' : '0',
+      currentCamera: currentCamera.toString()
+    };
+  }
+
+  function startCamera(success, error){
+      var currentCameraIndex = getCurrentCameraIndex();
+      var currentCamera = getCurrentCamera();
+      window.navigator.mediaDevices.getUserMedia({
+        audio: false,
+        video: {
+          deviceId: {exact: currentCamera.deviceId},
+          width: {ideal: currentCamera.width},
+          height: {ideal: currentCamera.height}
+        }
+      }).then(function(mediaStream){
+        activeMediaStream = mediaStream;
+        var video = getVideoPreview();
+        video.src = URL.createObjectURL(mediaStream);
+        success(calcStatus());
+      }, function(err){
+        // something bad happened
+        err = null;
+        var code = currentCameraIndex? 4 : 3;
+        error(code); // FRONT_CAMERA_UNAVAILABLE : BACK_CAMERA_UNAVAILABLE
+      });
+  }
+
+  function getTempCanvasAndContext(videoElement){
+    var tempCanvas = document.createElement('canvas');
+    var camera = getCurrentCamera();
+    tempCanvas.height = camera.height;
+    tempCanvas.width = camera.width;
+    var tempCanvasContext = tempCanvas.getContext('2d');
+    tempCanvasContext.drawImage(videoElement, 0, 0, camera.width, camera.height);
+    return {
+      canvas: tempCanvas,
+      context: tempCanvasContext
+    };
+  }
+
+  function getCurrentImageData(videoElement){
+    var snapshot = getTempCanvasAndContext(videoElement);
+    return snapshot.context.getImageData(0, 0, snapshot.canvas.width, snapshot.canvas.height);
+  }
+
+  // take a screenshot of the video preview with a temp canvas
+  function captureCurrentFrame(videoElement){
+    return getTempCanvasAndContext(videoElement).canvas.toDataURL('image/png');
+  }
+
+  function initialize(success, error){
+    if(scanWorker === null){
+      var workerBlob = new Blob([workerScript],{type: "text/javascript"});
+      scanWorker = new Worker(URL.createObjectURL(workerBlob));
+    }
+    if(!getVideoPreview()){
+      // prepare DOM (sync)
+      var videoPreview = document.createElement('video');
+      videoPreview.setAttribute('autoplay', 'autoplay');
+      videoPreview.setAttribute('id', ELEMENTS.preview);
+      videoPreview.setAttribute('style', 'display:block;position:fixed;top:50%;left:50%;' +
+      'width:auto;height:auto;min-width:100%;min-height:100%;z-index:' + ZINDEXES.preview +
+      ';-moz-transform: translateX(-50%) translateY(-50%);-webkit-transform: ' +
+      'translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);' +
+      'background-size:cover;background-position:50% 50%;background-color:#FFF;');
+      videoPreview.addEventListener('loadeddata', function(){
+        bringPreviewToFront();
+      });
+
+      var stillImg = document.createElement('div');
+      stillImg.setAttribute('id', ELEMENTS.still);
+      stillImg.setAttribute('style', 'display:block;position:fixed;top:50%;left:50%;visibility: hidden;' +
+      'width:auto;height:auto;min-width:100%;min-height:100%;z-index:' + ZINDEXES.still +
+      ';-moz-transform: translateX(-50%) translateY(-50%);-webkit-transform: ' +
+      'translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);' +
+      'background-size:cover;background-position:50% 50%;background-color:#FFF;');
+
+      document.body.appendChild(videoPreview);
+      document.body.appendChild(stillImg);
+    }
+    if(backCamera === null){
+      // set instance cameras
+      chooseCameras().then(function(cameras){
+        backCamera = cameras.backCamera;
+        frontCamera = cameras.frontCamera;
+        if(backCamera !== false){
+          success();
+        } else {
+          error(5); // CAMERA_UNAVAILABLE
+        }
+      }, function(err){
+        // something bad happened
+        err = null;
+        error(0); // UNEXPECTED_ERROR
+      });
+    } else if (backCamera === false){
+      error(5); // CAMERA_UNAVAILABLE
+    } else {
+      success();
+    }
+  }
+
+  /*
+   *  --- Begin Public API ---
+   */
+
+  function prepare(success, error){
+    initialize(function(){
+      // return status on success
+      success(calcStatus());
+    },
+    // pass errors through
+    error);
+  }
+
+  function show(success, error){
+    function showCamera(){
+      if(!mediaStreamIsActive()){
+        startCamera(success, error);
+      } else {
+        success(calcStatus());
+      }
+    }
+    if(!isInitialized()){
+      initialize(function(){
+        // on successful initialization, attempt to showCamera
+        showCamera();
+      },
+      // pass errors through
+      error);
+    } else {
+      showCamera();
+    }
+  }
+
+  function hide(success, error){
+    error = null; // should never error
+    if(mediaStreamIsActive()){
+      killActiveMediaStream();
+    }
+    var video = getVideoPreview();
+    if(video){
+      video.src = '';
+    }
+    success(calcStatus());
+  }
+
+  function scan(success, error) {
+    // initialize and start video preview if not already active
+    show(function(ignore){
+      // ignore success output – `scan` method callback should be passed the decoded data
+      ignore = null;
+      var video = getVideoPreview();
+      var returned = false;
+      scanning = true;
+      scanWorker.onmessage = function(event){
+        var obj = event.data;
+        if(obj.result && !returned){
+          returned = true;
+          thisScanCycle = null;
+          success(obj.result);
+        }
+      };
+      thisScanCycle = function(){
+        scanWorker.postMessage(getCurrentImageData(video));
+        if(cancelNextScan !== null){
+          // avoid race conditions, always clear before starting a cycle
+          cancelNextScan();
+        }
+        // interval in milliseconds at which to try decoding the QR code
+        var SCAN_INTERVAL = window.QRScanner_SCAN_INTERVAL || 130;
+        // this value can be adjusted on-the-fly (while a scan is active) to
+        // balance scan speed vs. CPU/power usage
+        nextScan = window.setTimeout(thisScanCycle, SCAN_INTERVAL);
+        cancelNextScan = function(sendError){
+          window.clearTimeout(nextScan);
+          nextScan = null;
+          cancelNextScan = null;
+          if(sendError){
+            error(6); // SCAN_CANCELED
+          }
+        };
+      };
+      thisScanCycle();
+    }, error);
+  }
+
+  function cancelScan(success, error){
+    error = null; // should never error
+    if(cancelNextScan !== null){
+      cancelNextScan(true);
+    }
+    scanning = false;
+    if(typeof success === "function"){
+      success(calcStatus());
+    }
+  }
+
+  function pausePreview(success, error){
+    error = null; // should never error
+    if(mediaStreamIsActive()){
+      // pause scanning too
+      if(cancelNextScan !== null){
+        cancelNextScan();
+      }
+      var video = getVideoPreview();
+      video.pause();
+      var img = new Image();
+      img.src = captureCurrentFrame(video);
+      getImg().style.backgroundImage = 'url(' + img.src + ')';
+      bringStillToFront();
+      // kill the active stream to turn off the privacy light (the screenshot
+      // in the stillImg will remain visible)
+      killActiveMediaStream();
+      success(calcStatus());
+    } else {
+      success(calcStatus());
+    }
+  }
+
+  function resumePreview(success, error){
+    // if a scan was happening, resume it
+    if(thisScanCycle !== null){
+      thisScanCycle();
+    }
+    show(success, error);
+  }
+
+  function enableLight(success, error){
+    error(7); //LIGHT_UNAVAILABLE
+  }
+
+  function disableLight(success, error){
+    error(7); //LIGHT_UNAVAILABLE
+  }
+
+  function useCamera(success, error, array){
+    var requestedCamera = array[0];
+    var initialized = isInitialized();
+    if(requestedCamera !== currentCamera){
+      if(initialized && requestedCamera === 1 && !canChangeCamera()){
+          error(4); //FRONT_CAMERA_UNAVAILABLE
+      } else {
+        currentCamera = requestedCamera;
+        if(initialized){
+          hide(function(status){
+            // Don't need this one
+            status = null;
+          });
+          show(success, error);
+        } else {
+          success(calcStatus());
+        }
+      }
+    } else {
+      success(calcStatus());
+    }
+  }
+
+  function openSettings(success, error){
+    error(8); //OPEN_SETTINGS_UNAVAILABLE
+  }
+
+  function getStatus(success, error){
+    error = null; // should never error
+    success(calcStatus());
+  }
+
+  // Reset all instance variables to their original state.
+  // This method might be useful in cases where a new camera is available, and
+  // the application needs to force the plugin to chooseCameras() again.
+  function destroy(success, error){
+    error = null; // should never error
+    cancelScan();
+    if(mediaStreamIsActive()){
+      killActiveMediaStream();
+    }
+    backCamera = null;
+    frontCamera = null;
+    var preview = getVideoPreview();
+    var still = getImg();
+    if(preview){
+      preview.remove();
+    }
+    if(still){
+      still.remove();
+    }
+    success(calcStatus());
+  }
+
+  return {
+      prepare: prepare,
+      show: show,
+      hide: hide,
+      scan: scan,
+      cancelScan: cancelScan,
+      pausePreview: pausePreview,
+      resumePreview: resumePreview,
+      enableLight: enableLight,
+      disableLight: disableLight,
+      useCamera: useCamera,
+      openSettings: openSettings,
+      getStatus: getStatus,
+      destroy: destroy
+  };
+};
+
+
+/***/ }),
+/* 3 */,
+/* 4 */,
+/* 5 */
+/***/ (function(module, exports) {
+
+module.exports = cordovaRequire;
+
+/***/ }),
+/* 6 */
+/***/ (function(module, exports) {
+
+module.exports = "!function(t){function e(i){if(n[i])return n[i].exports;var r=n[i]={i:i,l:!1,exports:{}};return t[i].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,i){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:i})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,\"a\",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p=\"\",e(e.s=19)}([function(t,e,n){\"use strict\";function i(){this.imagedata=null,this.width=0,this.height=0,this.qrCodeSymbol=null,this.debug=!1,this.callback=null}function r(t,e){return t>=0?t>>e:(t>>e)+(2<<~e)}n.d(e,\"b\",function(){return s}),e.a=i,e.c=r;var o=n(14),a=n(13),s={};s.sizeOfDataLengthInfo=[[10,9,8,8],[12,11,16,10],[14,13,16,12]],i.prototype.decode=function(t,e){var n=function(){try{this.error=void 0,this.result=this.process(this.imagedata)}catch(t){this.error=t,this.result=void 0}return null!=this.callback&&this.callback(this.error,this.result),this.result}.bind(this);if(void 0!=t&&void 0!=t.width)this.width=t.width,this.height=t.height,this.imagedata={data:e||t.data},this.imagedata.width=t.width,this.imagedata.height=t.height,n();else{if(\"undefined\"==typeof Image)throw new Error(\"This source format is not supported in your environment, you need to pass an image buffer with width and height (see https://github.com/edi9999/jsqrcode/blob/master/test/qrcode.js)\");var i=new Image;i.crossOrigin=\"Anonymous\",i.onload=function(){var t=document.createElement(\"canvas\"),e=t.getContext(\"2d\"),r=document.getElementById(\"out-canvas\");if(null!=r){var o=r.getContext(\"2d\");o.clearRect(0,0,320,240),o.drawImage(i,0,0,320,240)}t.width=i.width,t.height=i.height,e.drawImage(i,0,0),this.width=i.width,this.height=i.height;try{this.imagedata=e.getImageData(0,0,i.width,i.height)}catch(t){if(this.result=\"Cross domain image reading not supported in your browser! Save it to your computer then drag and drop the file!\",null!=this.callback)return this.callback(null,this.result)}n()}.bind(this),i.src=t}},i.prototype.decode_utf8=function(t){return decodeURIComponent(escape(t))},i.prototype.process=function(t){for(var e=(new Date).getTime(),n=this.grayScaleToBitmap(this.grayscale(t)),i=new o.a(n),r=i.detect(),s=a.a.decode(r.bits),h=s.DataByte,f=\"\",w=0;w<h.length;w++)for(var u=0;u<h[w].length;u++)f+=String.fromCharCode(h[w][u]);var l=(new Date).getTime(),c=l-e;return this.debug&&console.log(\"QR Code processing time (ms): \"+c),{result:this.decode_utf8(f),points:r.points}},i.prototype.getPixel=function(t,e,n){if(t.width<e)throw\"point error\";if(t.height<n)throw\"point error\";var i=4*e+n*t.width*4;return(33*t.data[i]+34*t.data[i+1]+33*t.data[i+2])/100},i.prototype.binarize=function(t){for(var e=new Array(this.width*this.height),n=0;n<this.height;n++)for(var i=0;i<this.width;i++){var r=this.getPixel(i,n);e[i+n*this.width]=r<=t}return e},i.prototype.getMiddleBrightnessPerArea=function(t){for(var e=Math.floor(t.width/4),n=Math.floor(t.height/4),i=new Array(4),r=0;r<4;r++){i[r]=new Array(4);for(var o=0;o<4;o++)i[r][o]=[0,0]}for(var a=0;a<4;a++)for(var s=0;s<4;s++){i[s][a][0]=255;for(var h=0;h<n;h++)for(var f=0;f<e;f++){var w=t.data[e*s+f+(n*a+h)*t.width];w<i[s][a][0]&&(i[s][a][0]=w),w>i[s][a][1]&&(i[s][a][1]=w)}}for(var u=new Array(4),l=0;l<4;l++)u[l]=new Array(4);for(var a=0;a<4;a++)for(var s=0;s<4;s++)u[s][a]=Math.floor((i[s][a][0]+i[s][a][1])/2);return u},i.prototype.grayScaleToBitmap=function(t){for(var e=this.getMiddleBrightnessPerArea(t),n=e.length,i=Math.floor(t.width/n),r=Math.floor(t.height/n),o=0;o<n;o++)for(var a=0;a<n;a++)for(var s=0;s<r;s++)for(var h=0;h<i;h++)t.data[i*a+h+(r*o+s)*t.width]=t.data[i*a+h+(r*o+s)*t.width]<e[a][o];return t},i.prototype.grayscale=function(t){for(var e=new Array(t.width*t.height),n=0;n<t.height;n++)for(var i=0;i<t.width;i++){var r=this.getPixel(t,i,n);e[i+n*t.width]=r}return{height:t.height,width:t.width,data:e}}},function(t,e,n){\"use strict\";function i(t,e){if(e||(e=t),t<1||e<1)throw\"Both dimensions must be greater than 0\";this.width=t,this.height=e;var n=t>>5;0!=(31&t)&&n++,this.rowSize=n,this.bits=new Array(n*e);for(var i=0;i<this.bits.length;i++)this.bits[i]=0}e.a=i;var r=n(0);Object.defineProperty(i.prototype,\"Dimension\",{get:function(){if(this.width!=this.height)throw\"Can't call getDimension() on a non-square matrix\";return this.width}}),i.prototype.get_Renamed=function(t,e){var i=e*this.rowSize+(t>>5);return 0!=(1&n.i(r.c)(this.bits[i],31&t))},i.prototype.set_Renamed=function(t,e){var n=e*this.rowSize+(t>>5);this.bits[n]|=1<<(31&t)},i.prototype.flip=function(t,e){var n=e*this.rowSize+(t>>5);this.bits[n]^=1<<(31&t)},i.prototype.clear=function(){for(var t=this.bits.length,e=0;e<t;e++)this.bits[e]=0},i.prototype.setRegion=function(t,e,n,i){if(e<0||t<0)throw\"Left and top must be nonnegative\";if(i<1||n<1)throw\"Height and width must be at least 1\";var r=t+n,o=e+i;if(o>this.height||r>this.width)throw\"The region must fit inside the matrix\";for(var a=e;a<o;a++)for(var s=a*this.rowSize,h=t;h<r;h++)this.bits[s+(h>>5)]|=1<<(31&h)}},function(t,e,n){\"use strict\";function i(t){this.errorCorrectionLevel=o.a.forBits(t>>3&3),this.dataMask=7&t}e.a=i;var r=n(0),o=n(15),a=[[21522,0],[20773,1],[24188,2],[23371,3],[17913,4],[16590,5],[20375,6],[19104,7],[30660,8],[29427,9],[32170,10],[30877,11],[26159,12],[25368,13],[27713,14],[26998,15],[5769,16],[5054,17],[7399,18],[6608,19],[1890,20],[597,21],[3340,22],[2107,23],[13663,24],[12392,25],[16177,26],[14854,27],[9396,28],[8579,29],[11994,30],[11245,31]],s=[0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4];i.prototype.GetHashCode=function(){return this.errorCorrectionLevel.ordinal()<<3|this.dataMask},i.prototype.Equals=function(t){var e=t;return this.errorCorrectionLevel==e.errorCorrectionLevel&&this.dataMask==e.dataMask},i.numBitsDiffering=function(t,e){return t^=e,s[15&t]+s[15&n.i(r.c)(t,4)]+s[15&n.i(r.c)(t,8)]+s[15&n.i(r.c)(t,12)]+s[15&n.i(r.c)(t,16)]+s[15&n.i(r.c)(t,20)]+s[15&n.i(r.c)(t,24)]+s[15&n.i(r.c)(t,28)]},i.decodeFormatInformation=function(t){var e=i.doDecodeFormatInformation(t);return null!=e?e:i.doDecodeFormatInformation(21522^t)},i.doDecodeFormatInformation=function(t){for(var e=4294967295,n=0,r=0;r<a.length;r++){var o=a[r],s=o[0];if(s==t)return new i(o[1]);var h=this.numBitsDiffering(t,s);h<e&&(n=o[1],e=h)}return e<=3?new i(n):null}},function(t,e,n){\"use strict\";function i(t){this.expTable=new Array(256),this.logTable=new Array(256);for(var e=1,n=0;n<256;n++)this.expTable[n]=e,(e<<=1)>=256&&(e^=t);for(var n=0;n<255;n++)this.logTable[this.expTable[n]]=n;var i=new Array(1);i[0]=0,this.zero=new r.a(this,new Array(i));var o=new Array(1);o[0]=1,this.one=new r.a(this,new Array(o))}e.a=i;var r=n(4);Object.defineProperty(i.prototype,\"Zero\",{get:function(){return this.zero}}),Object.defineProperty(i.prototype,\"One\",{get:function(){return this.one}}),i.prototype.buildMonomial=function(t,e){if(t<0)throw\"System.ArgumentException\";if(0==e)return this.zero;for(var n=new Array(t+1),i=0;i<n.length;i++)n[i]=0;return n[0]=e,new r.a(this,n)},i.prototype.exp=function(t){return this.expTable[t]},i.prototype.log=function(t){if(0==t)throw\"System.ArgumentException\";return this.logTable[t]},i.prototype.inverse=function(t){if(0==t)throw\"System.ArithmeticException\";return this.expTable[255-this.logTable[t]]},i.prototype.addOrSubtract=function(t,e){return t^e},i.prototype.multiply=function(t,e){return 0==t||0==e?0:1==t?e:1==e?t:this.expTable[(this.logTable[t]+this.logTable[e])%255]},i.QR_CODE_FIELD=new i(285),i.DATA_MATRIX_FIELD=new i(301)},function(t,e,n){\"use strict\";function i(t,e){if(null==e||0==e.length)throw\"System.ArgumentException\";this.field=t;var n=e.length;if(n>1&&0==e[0]){for(var i=1;i<n&&0==e[i];)i++;if(i==n)this.coefficients=t.Zero.coefficients;else{this.coefficients=new Array(n-i);for(var r=0;r<this.coefficients.length;r++)this.coefficients[r]=0;for(var o=0;o<this.coefficients.length;o++)this.coefficients[o]=e[i+o]}}else this.coefficients=e}e.a=i,Object.defineProperty(i.prototype,\"Zero\",{get:function(){return 0==this.coefficients[0]}}),Object.defineProperty(i.prototype,\"Degree\",{get:function(){return this.coefficients.length-1}}),i.prototype.getCoefficient=function(t){return this.coefficients[this.coefficients.length-1-t]},i.prototype.evaluateAt=function(t){if(0==t)return this.getCoefficient(0);var e=this.coefficients.length;if(1==t){for(var n=0,i=0;i<e;i++)n=this.field.addOrSubtract(n,this.coefficients[i]);return n}for(var r=this.coefficients[0],i=1;i<e;i++)r=this.field.addOrSubtract(this.field.multiply(t,r),this.coefficients[i]);return r},i.prototype.addOrSubtract=function(t){if(this.field!=t.field)throw\"GF256Polys do not have same GF256 field\";if(this.Zero)return t;if(t.Zero)return this;var e=this.coefficients,n=t.coefficients;if(e.length>n.length){var r=e;e=n,n=r}for(var o=new Array(n.length),a=n.length-e.length,s=0;s<a;s++)o[s]=n[s];for(var h=a;h<n.length;h++)o[h]=this.field.addOrSubtract(e[h-a],n[h]);return new i(this.field,o)},i.prototype.multiply1=function(t){if(this.field!=t.field)throw\"GF256Polys do not have same GF256 field\";if(this.Zero||t.Zero)return this.field.Zero;for(var e=this.coefficients,n=e.length,r=t.coefficients,o=r.length,a=new Array(n+o-1),s=0;s<n;s++)for(var h=e[s],f=0;f<o;f++)a[s+f]=this.field.addOrSubtract(a[s+f],this.field.multiply(h,r[f]));return new i(this.field,a)},i.prototype.multiply2=function(t){if(0==t)return this.field.Zero;if(1==t)return this;for(var e=this.coefficients.length,n=new Array(e),r=0;r<e;r++)n[r]=this.field.multiply(this.coefficients[r],t);return new i(this.field,n)},i.prototype.multiplyByMonomial=function(t,e){if(t<0)throw\"System.ArgumentException\";if(0==e)return this.field.Zero;for(var n=this.coefficients.length,r=new Array(n+t),o=0;o<r.length;o++)r[o]=0;for(var o=0;o<n;o++)r[o]=this.field.multiply(this.coefficients[o],e);return new i(this.field,r)},i.prototype.divide=function(t){if(this.field!=t.field)throw\"GF256Polys do not have same GF256 field\";if(t.Zero)throw\"Divide by 0\";for(var e=this.field.Zero,n=this,i=t.getCoefficient(t.Degree),r=this.field.inverse(i);n.Degree>=t.Degree&&!n.Zero;){var o=n.Degree-t.Degree,a=this.field.multiply(n.getCoefficient(n.Degree),r),s=t.multiplyByMonomial(o,a),h=this.field.buildMonomial(o,a);e=e.addOrSubtract(h),n=n.addOrSubtract(s)}return[e,n]}},function(t,e,n){\"use strict\";function i(t,e){this.count=t,this.dataCodewords=e}function r(t,e,n){this.ecCodewordsPerBlock=t,this.ecBlocks=n?[e,n]:[e]}function o(t,e,n,i,r,o){this.versionNumber=t,this.alignmentPatternCenters=e,this.ecBlocks=[n,i,r,o];for(var a=0,s=n.ecCodewordsPerBlock,h=n.getECBlocks(),f=0;f<h.length;f++){var w=h[f];a+=w.count*(w.dataCodewords+s)}this.totalCodewords=a}e.a=o;var a=n(2),s=n(1);Object.defineProperty(r.prototype,\"TotalECCodewords\",{get:function(){return this.ecCodewordsPerBlock*this.NumBlocks}}),Object.defineProperty(r.prototype,\"NumBlocks\",{get:function(){for(var t=0,e=0;e<this.ecBlocks.length;e++)t+=this.ecBlocks[e].length;return t}}),r.prototype.getECBlocks=function(){return this.ecBlocks},Object.defineProperty(o.prototype,\"DimensionForVersion\",{get:function(){return 17+4*this.versionNumber}}),o.prototype.buildFunctionPattern=function(){var t=this.DimensionForVersion,e=new s.a(t);e.setRegion(0,0,9,9),e.setRegion(t-8,0,8,9),e.setRegion(0,t-8,9,8);for(var n=this.alignmentPatternCenters.length,i=0;i<n;i++)for(var r=this.alignmentPatternCenters[i]-2,o=0;o<n;o++)0==i&&(0==o||o==n-1)||i==n-1&&0==o||e.setRegion(this.alignmentPatternCenters[o]-2,r,5,5);return e.setRegion(6,9,1,t-17),e.setRegion(9,6,t-17,1),this.versionNumber>6&&(e.setRegion(t-11,0,3,6),e.setRegion(0,t-11,6,3)),e},o.prototype.getECBlocksForLevel=function(t){return this.ecBlocks[t.ordinal()]},o.VERSION_DECODE_INFO=[31892,34236,39577,42195,48118,51042,55367,58893,63784,68472,70749,76311,79154,84390,87683,92361,96236,102084,102881,110507,110734,117786,119615,126325,127568,133589,136944,141498,145311,150283,152622,158308,161089,167017],o.VERSIONS=function(){return[new o(1,[],new r(7,new i(1,19)),new r(10,new i(1,16)),new r(13,new i(1,13)),new r(17,new i(1,9))),new o(2,[6,18],new r(10,new i(1,34)),new r(16,new i(1,28)),new r(22,new i(1,22)),new r(28,new i(1,16))),new o(3,[6,22],new r(15,new i(1,55)),new r(26,new i(1,44)),new r(18,new i(2,17)),new r(22,new i(2,13))),new o(4,[6,26],new r(20,new i(1,80)),new r(18,new i(2,32)),new r(26,new i(2,24)),new r(16,new i(4,9))),new o(5,[6,30],new r(26,new i(1,108)),new r(24,new i(2,43)),new r(18,new i(2,15),new i(2,16)),new r(22,new i(2,11),new i(2,12))),new o(6,[6,34],new r(18,new i(2,68)),new r(16,new i(4,27)),new r(24,new i(4,19)),new r(28,new i(4,15))),new o(7,[6,22,38],new r(20,new i(2,78)),new r(18,new i(4,31)),new r(18,new i(2,14),new i(4,15)),new r(26,new i(4,13),new i(1,14))),new o(8,[6,24,42],new r(24,new i(2,97)),new r(22,new i(2,38),new i(2,39)),new r(22,new i(4,18),new i(2,19)),new r(26,new i(4,14),new i(2,15))),new o(9,[6,26,46],new r(30,new i(2,116)),new r(22,new i(3,36),new i(2,37)),new r(20,new i(4,16),new i(4,17)),new r(24,new i(4,12),new i(4,13))),new o(10,[6,28,50],new r(18,new i(2,68),new i(2,69)),new r(26,new i(4,43),new i(1,44)),new r(24,new i(6,19),new i(2,20)),new r(28,new i(6,15),new i(2,16))),new o(11,[6,30,54],new r(20,new i(4,81)),new r(30,new i(1,50),new i(4,51)),new r(28,new i(4,22),new i(4,23)),new r(24,new i(3,12),new i(8,13))),new o(12,[6,32,58],new r(24,new i(2,92),new i(2,93)),new r(22,new i(6,36),new i(2,37)),new r(26,new i(4,20),new i(6,21)),new r(28,new i(7,14),new i(4,15))),new o(13,[6,34,62],new r(26,new i(4,107)),new r(22,new i(8,37),new i(1,38)),new r(24,new i(8,20),new i(4,21)),new r(22,new i(12,11),new i(4,12))),new o(14,[6,26,46,66],new r(30,new i(3,115),new i(1,116)),new r(24,new i(4,40),new i(5,41)),new r(20,new i(11,16),new i(5,17)),new r(24,new i(11,12),new i(5,13))),new o(15,[6,26,48,70],new r(22,new i(5,87),new i(1,88)),new r(24,new i(5,41),new i(5,42)),new r(30,new i(5,24),new i(7,25)),new r(24,new i(11,12),new i(7,13))),new o(16,[6,26,50,74],new r(24,new i(5,98),new i(1,99)),new r(28,new i(7,45),new i(3,46)),new r(24,new i(15,19),new i(2,20)),new r(30,new i(3,15),new i(13,16))),new o(17,[6,30,54,78],new r(28,new i(1,107),new i(5,108)),new r(28,new i(10,46),new i(1,47)),new r(28,new i(1,22),new i(15,23)),new r(28,new i(2,14),new i(17,15))),new o(18,[6,30,56,82],new r(30,new i(5,120),new i(1,121)),new r(26,new i(9,43),new i(4,44)),new r(28,new i(17,22),new i(1,23)),new r(28,new i(2,14),new i(19,15))),new o(19,[6,30,58,86],new r(28,new i(3,113),new i(4,114)),new r(26,new i(3,44),new i(11,45)),new r(26,new i(17,21),new i(4,22)),new r(26,new i(9,13),new i(16,14))),new o(20,[6,34,62,90],new r(28,new i(3,107),new i(5,108)),new r(26,new i(3,41),new i(13,42)),new r(30,new i(15,24),new i(5,25)),new r(28,new i(15,15),new i(10,16))),new o(21,[6,28,50,72,94],new r(28,new i(4,116),new i(4,117)),new r(26,new i(17,42)),new r(28,new i(17,22),new i(6,23)),new r(30,new i(19,16),new i(6,17))),new o(22,[6,26,50,74,98],new r(28,new i(2,111),new i(7,112)),new r(28,new i(17,46)),new r(30,new i(7,24),new i(16,25)),new r(24,new i(34,13))),new o(23,[6,30,54,74,102],new r(30,new i(4,121),new i(5,122)),new r(28,new i(4,47),new i(14,48)),new r(30,new i(11,24),new i(14,25)),new r(30,new i(16,15),new i(14,16))),new o(24,[6,28,54,80,106],new r(30,new i(6,117),new i(4,118)),new r(28,new i(6,45),new i(14,46)),new r(30,new i(11,24),new i(16,25)),new r(30,new i(30,16),new i(2,17))),new o(25,[6,32,58,84,110],new r(26,new i(8,106),new i(4,107)),new r(28,new i(8,47),new i(13,48)),new r(30,new i(7,24),new i(22,25)),new r(30,new i(22,15),new i(13,16))),new o(26,[6,30,58,86,114],new r(28,new i(10,114),new i(2,115)),new r(28,new i(19,46),new i(4,47)),new r(28,new i(28,22),new i(6,23)),new r(30,new i(33,16),new i(4,17))),new o(27,[6,34,62,90,118],new r(30,new i(8,122),new i(4,123)),new r(28,new i(22,45),new i(3,46)),new r(30,new i(8,23),new i(26,24)),new r(30,new i(12,15),new i(28,16))),new o(28,[6,26,50,74,98,122],new r(30,new i(3,117),new i(10,118)),new r(28,new i(3,45),new i(23,46)),new r(30,new i(4,24),new i(31,25)),new r(30,new i(11,15),new i(31,16))),new o(29,[6,30,54,78,102,126],new r(30,new i(7,116),new i(7,117)),new r(28,new i(21,45),new i(7,46)),new r(30,new i(1,23),new i(37,24)),new r(30,new i(19,15),new i(26,16))),new o(30,[6,26,52,78,104,130],new r(30,new i(5,115),new i(10,116)),new r(28,new i(19,47),new i(10,48)),new r(30,new i(15,24),new i(25,25)),new r(30,new i(23,15),new i(25,16))),new o(31,[6,30,56,82,108,134],new r(30,new i(13,115),new i(3,116)),new r(28,new i(2,46),new i(29,47)),new r(30,new i(42,24),new i(1,25)),new r(30,new i(23,15),new i(28,16))),new o(32,[6,34,60,86,112,138],new r(30,new i(17,115)),new r(28,new i(10,46),new i(23,47)),new r(30,new i(10,24),new i(35,25)),new r(30,new i(19,15),new i(35,16))),new o(33,[6,30,58,86,114,142],new r(30,new i(17,115),new i(1,116)),new r(28,new i(14,46),new i(21,47)),new r(30,new i(29,24),new i(19,25)),new r(30,new i(11,15),new i(46,16))),new o(34,[6,34,62,90,118,146],new r(30,new i(13,115),new i(6,116)),new r(28,new i(14,46),new i(23,47)),new r(30,new i(44,24),new i(7,25)),new r(30,new i(59,16),new i(1,17))),new o(35,[6,30,54,78,102,126,150],new r(30,new i(12,121),new i(7,122)),new r(28,new i(12,47),new i(26,48)),new r(30,new i(39,24),new i(14,25)),new r(30,new i(22,15),new i(41,16))),new o(36,[6,24,50,76,102,128,154],new r(30,new i(6,121),new i(14,122)),new r(28,new i(6,47),new i(34,48)),new r(30,new i(46,24),new i(10,25)),new r(30,new i(2,15),new i(64,16))),new o(37,[6,28,54,80,106,132,158],new r(30,new i(17,122),new i(4,123)),new r(28,new i(29,46),new i(14,47)),new r(30,new i(49,24),new i(10,25)),new r(30,new i(24,15),new i(46,16))),new o(38,[6,32,58,84,110,136,162],new r(30,new i(4,122),new i(18,123)),new r(28,new i(13,46),new i(32,47)),new r(30,new i(48,24),new i(14,25)),new r(30,new i(42,15),new i(32,16))),new o(39,[6,26,54,82,110,138,166],new r(30,new i(20,117),new i(4,118)),new r(28,new i(40,47),new i(7,48)),new r(30,new i(43,24),new i(22,25)),new r(30,new i(10,15),new i(67,16))),new o(40,[6,30,58,86,114,142,170],new r(30,new i(19,118),new i(6,119)),new r(28,new i(18,47),new i(31,48)),new r(30,new i(34,24),new i(34,25)),new r(30,new i(20,15),new i(61,16)))]}(),o.getVersionForNumber=function(t){if(t<1||t>40)throw\"ArgumentException\";return o.VERSIONS[t-1]},o.getProvisionalVersionForDimension=function(t){if(t%4!=1)throw\"Error getProvisionalVersionForDimension\";try{return o.getVersionForNumber(t-17>>2)}catch(t){throw\"Error getVersionForNumber\"}},o.decodeVersionInformation=function(t){for(var e=4294967295,n=0,i=0;i<o.VERSION_DECODE_INFO.length;i++){var r=o.VERSION_DECODE_INFO[i];if(r==t)return this.getVersionForNumber(i+7);var s=a.a.numBitsDiffering(t,r);s<e&&(n=i+7,e=s)}return e<=3?this.getVersionForNumber(n):null}},function(t,e,n){\"use strict\";Object.defineProperty(e,\"__esModule\",{value:!0});var i=n(0);e.default=i.a},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,\"loaded\",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,\"id\",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},function(t,e,n){\"use strict\";function i(t,e,n){this.x=t,this.y=e,this.count=1,this.estimatedModuleSize=n}function r(t,e,n,i,r,o,a){this.image=t,this.possibleCenters=[],this.startX=e,this.startY=n,this.width=i,this.height=r,this.moduleSize=o,this.crossCheckStateCount=[0,0,0],this.resultPointCallback=a}e.a=r,Object.defineProperty(i.prototype,\"X\",{get:function(){return Math.floor(this.x)}}),Object.defineProperty(i.prototype,\"Y\",{get:function(){return Math.floor(this.y)}}),i.prototype.incrementCount=function(){this.count++},i.prototype.aboutEquals=function(t,e,n){if(Math.abs(e-this.y)<=t&&Math.abs(n-this.x)<=t){var i=Math.abs(t-this.estimatedModuleSize);return i<=1||i/this.estimatedModuleSize<=1}return!1},r.prototype.centerFromEnd=function(t,e){return e-t[2]-t[1]/2},r.prototype.foundPatternCross=function(t){for(var e=this.moduleSize,n=e/2,i=0;i<3;i++)if(Math.abs(e-t[i])>=n)return!1;return!0},r.prototype.crossCheckVertical=function(t,e,n,i){var r=this.image,o=r.height,a=this.crossCheckStateCount;a[0]=0,a[1]=0,a[2]=0;for(var s=t;s>=0&&r.data[e+s*r.width]&&a[1]<=n;)a[1]++,s--;if(s<0||a[1]>n)return NaN;for(;s>=0&&!r.data[e+s*r.width]&&a[0]<=n;)a[0]++,s--;if(a[0]>n)return NaN;for(s=t+1;s<o&&r.data[e+s*r.width]&&a[1]<=n;)a[1]++,s++;if(s==o||a[1]>n)return NaN;for(;s<o&&!r.data[e+s*r.width]&&a[2]<=n;)a[2]++,s++;if(a[2]>n)return NaN;var h=a[0]+a[1]+a[2];return 5*Math.abs(h-i)>=2*i?NaN:this.foundPatternCross(a)?this.centerFromEnd(a,s):NaN},r.prototype.handlePossibleCenter=function(t,e,n){var r=t[0]+t[1]+t[2],o=this.centerFromEnd(t,n),a=this.crossCheckVertical(e,Math.floor(o),2*t[1],r);if(!isNaN(a)){for(var s=(t[0]+t[1]+t[2])/3,h=this.possibleCenters.length,f=0;f<h;f++){if(this.possibleCenters[f].aboutEquals(s,a,o))return new i(o,a,s)}var w=new i(o,a,s);this.possibleCenters.push(w),null!=this.resultPointCallback&&this.resultPointCallback.foundPossibleResultPoint(w)}return null},r.prototype.find=function(){for(var t=this.image,e=this.startX,n=this.height,i=e+this.width,r=this.startY+(n>>1),o=[0,0,0],a=0;a<n;a++){var s=r+(0==(1&a)?a+1>>1:-(a+1>>1));o[0]=0,o[1]=0,o[2]=0;for(var h=e;h<i&&!t.data[h+t.width*s];)h++;for(var f=0;h<i;){if(t.data[h+s*t.width])if(1==f)o[f]++;else if(2==f){if(this.foundPatternCross(o)){var w=this.handlePossibleCenter(o,s,h);if(null!=w)return w}o[0]=o[2],o[1]=1,o[2]=0,f=1}else o[++f]++;else 1==f&&f++,o[f]++;h++}if(this.foundPatternCross(o)){var w=this.handlePossibleCenter(o,s,i);if(null!=w)return w}}if(0!=this.possibleCenters.length)return this.possibleCenters[0];throw\"Couldn't find enough alignment patterns\"}},function(t,e,n){\"use strict\";function i(t){var e=t.Dimension;if(e<21||1!=(3&e))throw\"Error BitMatrixParser\";this.bitMatrix=t,this.parsedVersion=null,this.parsedFormatInfo=null}e.a=i;var r=n(2),o=n(5),a=n(12);i.prototype.copyBit=function(t,e,n){return this.bitMatrix.get_Renamed(t,e)?n<<1|1:n<<1},i.prototype.readFormatInformation=function(){if(null!=this.parsedFormatInfo)return this.parsedFormatInfo;for(var t=0,e=0;e<6;e++)t=this.copyBit(e,8,t);t=this.copyBit(7,8,t),t=this.copyBit(8,8,t),t=this.copyBit(8,7,t);for(var n=5;n>=0;n--)t=this.copyBit(8,n,t);if(this.parsedFormatInfo=r.a.decodeFormatInformation(t),null!=this.parsedFormatInfo)return this.parsedFormatInfo;var i=this.bitMatrix.Dimension;t=0;for(var o=i-8,e=i-1;e>=o;e--)t=this.copyBit(e,8,t);for(var n=i-7;n<i;n++)t=this.copyBit(8,n,t);if(this.parsedFormatInfo=r.a.decodeFormatInformation(t),null!=this.parsedFormatInfo)return this.parsedFormatInfo;throw\"Error readFormatInformation\"},i.prototype.readVersion=function(){if(null!=this.parsedVersion)return this.parsedVersion;var t=this.bitMatrix.Dimension,e=t-17>>2;if(e<=6)return o.a.getVersionForNumber(e);for(var n=0,i=t-11,r=5;r>=0;r--)for(var a=t-9;a>=i;a--)n=this.copyBit(a,r,n);if(this.parsedVersion=o.a.decodeVersionInformation(n),null!=this.parsedVersion&&this.parsedVersion.DimensionForVersion==t)return this.parsedVersion;n=0;for(var a=5;a>=0;a--)for(var r=t-9;r>=i;r--)n=this.copyBit(a,r,n);if(this.parsedVersion=o.a.decodeVersionInformation(n),null!=this.parsedVersion&&this.parsedVersion.DimensionForVersion==t)return this.parsedVersion;throw\"Error readVersion\"},i.prototype.readCodewords=function(){var t=this.readFormatInformation(),e=this.readVersion(),n=a.a.forReference(t.dataMask),i=this.bitMatrix.Dimension;n.unmaskBitMatrix(this.bitMatrix,i);for(var r=e.buildFunctionPattern(),o=!0,s=new Array(e.totalCodewords),h=0,f=0,w=0,u=i-1;u>0;u-=2){6==u&&u--;for(var l=0;l<i;l++)for(var c=o?i-1-l:l,d=0;d<2;d++)r.get_Renamed(u-d,c)||(w++,f<<=1,this.bitMatrix.get_Renamed(u-d,c)&&(f|=1),8==w&&(s[h++]=f,w=0,f=0));o^=!0}if(h!=e.totalCodewords)throw\"Error readCodewords\";return s}},function(t,e,n){\"use strict\";function i(t,e){this.numDataCodewords=t,this.codewords=e}e.a=i,i.getDataBlocks=function(t,e,n){if(t.length!=e.totalCodewords)throw\"ArgumentException\";for(var r=e.getECBlocksForLevel(n),o=0,a=r.getECBlocks(),s=0;s<a.length;s++)o+=a[s].count;for(var h=new Array(o),f=0,w=0;w<a.length;w++)for(var u=a[w],s=0;s<u.count;s++){var l=u.dataCodewords,c=r.ecCodewordsPerBlock+l;h[f++]=new i(l,new Array(c))}for(var d=h[0].codewords.length,p=h.length-1;p>=0;){if(h[p].codewords.length==d)break;p--}p++;for(var g=d-r.ecCodewordsPerBlock,v=0,s=0;s<g;s++)for(var w=0;w<f;w++)h[w].codewords[s]=t[v++];for(var w=p;w<f;w++)h[w].codewords[g]=t[v++];for(var m=h[0].codewords.length,s=g;s<m;s++)for(var w=0;w<f;w++){var b=w<p?s:s+1;h[w].codewords[b]=t[v++]}return h}},function(t,e,n){\"use strict\";function i(t,e,n){this.blockPointer=0,this.bitPointer=7,this.dataLength=0,this.blocks=t,this.numErrorCorrectionCode=n,e<=9?this.dataLengthMode=0:e>=10&&e<=26?this.dataLengthMode=1:e>=27&&e<=40&&(this.dataLengthMode=2)}e.a=i;var r=n(0);i.prototype.getNextBits=function(t){var e=0;if(t<this.bitPointer+1){for(var n=0,i=0;i<t;i++)n+=1<<i;return n<<=this.bitPointer-t+1,e=(this.blocks[this.blockPointer]&n)>>this.bitPointer-t+1,this.bitPointer-=t,e}if(t<this.bitPointer+1+8){for(var r=0,i=0;i<this.bitPointer+1;i++)r+=1<<i;return e=(this.blocks[this.blockPointer]&r)<<t-(this.bitPointer+1),this.blockPointer++,e+=this.blocks[this.blockPointer]>>8-(t-(this.bitPointer+1)),this.bitPointer=this.bitPointer-t%8,this.bitPointer<0&&(this.bitPointer=8+this.bitPointer),e}if(t<this.bitPointer+1+16){for(var r=0,o=0,i=0;i<this.bitPointer+1;i++)r+=1<<i;var a=(this.blocks[this.blockPointer]&r)<<t-(this.bitPointer+1);this.blockPointer++;var s=this.blocks[this.blockPointer]<<t-(this.bitPointer+1+8);this.blockPointer++;for(var i=0;i<t-(this.bitPointer+1+8);i++)o+=1<<i;o<<=8-(t-(this.bitPointer+1+8));return e=a+s+((this.blocks[this.blockPointer]&o)>>8-(t-(this.bitPointer+1+8))),this.bitPointer=this.bitPointer-(t-8)%8,this.bitPointer<0&&(this.bitPointer=8+this.bitPointer),e}return 0},i.prototype.NextMode=function(){return this.blockPointer>this.blocks.length-this.numErrorCorrectionCode-2?0:this.getNextBits(4)},i.prototype.getDataLength=function(t){for(var e=0;;){if(t>>e==1)break;e++}return this.getNextBits(r.b.sizeOfDataLengthInfo[this.dataLengthMode][e])},i.prototype.getRomanAndFigureString=function(t){var e=t,n=0,i=\"\",r=[\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"A\",\"B\",\"C\",\"D\",\"E\",\"F\",\"G\",\"H\",\"I\",\"J\",\"K\",\"L\",\"M\",\"N\",\"O\",\"P\",\"Q\",\"R\",\"S\",\"T\",\"U\",\"V\",\"W\",\"X\",\"Y\",\"Z\",\" \",\"$\",\"%\",\"*\",\"+\",\"-\",\".\",\"/\",\":\"];do{if(e>1){n=this.getNextBits(11);var o=Math.floor(n/45),a=n%45;i+=r[o],i+=r[a],e-=2}else 1==e&&(n=this.getNextBits(6),i+=r[n],e-=1)}while(e>0);return i},i.prototype.getFigureString=function(t){var e=t,n=0,i=\"\";do{e>=3?(n=this.getNextBits(10),n<100&&(i+=\"0\"),n<10&&(i+=\"0\"),e-=3):2==e?(n=this.getNextBits(7),n<10&&(i+=\"0\"),e-=2):1==e&&(n=this.getNextBits(4),e-=1),i+=n}while(e>0);return i},i.prototype.get8bitByteArray=function(t){var e=t,n=0,i=[];do{n=this.getNextBits(8),i.push(n),e--}while(e>0);return i},i.prototype.getKanjiString=function(t){var e=t,n=0,i=\"\";do{n=this.getNextBits(13);var r=n%192,o=n/192,a=(o<<8)+r,s=0;s=a+33088<=40956?a+33088:a+49472,i+=String.fromCharCode(s),e--}while(e>0);return i},Object.defineProperty(i.prototype,\"DataByte\",{get:function(){for(var t=[];;){var e=this.NextMode();if(0==e){if(t.length>0)break;throw\"Empty data block\"}if(1!=e&&2!=e&&4!=e&&8!=e&&7!=e)throw\"Invalid mode: \"+e+\" in (block:\"+this.blockPointer+\" bit:\"+this.bitPointer+\")\";var n=this.getDataLength(e);if(n<1)throw\"Invalid data length: \"+n;switch(e){case 1:for(var i=this.getFigureString(n),r=new Array(i.length),o=0;o<i.length;o++)r[o]=i.charCodeAt(o);t.push(r);break;case 2:for(var i=this.getRomanAndFigureString(n),r=new Array(i.length),o=0;o<i.length;o++)r[o]=i.charCodeAt(o);t.push(r);break;case 4:var a=this.get8bitByteArray(n);t.push(a);break;case 8:var i=this.getKanjiString(n);t.push(i)}}return t}})},function(t,e,n){\"use strict\";function i(){this.unmaskBitMatrix=function(t,e){for(var n=0;n<e;n++)for(var i=0;i<e;i++)this.isMasked(n,i)&&t.flip(i,n)},this.isMasked=function(t,e){return 0==(t+e&1)}}function r(){this.unmaskBitMatrix=function(t,e){for(var n=0;n<e;n++)for(var i=0;i<e;i++)this.isMasked(n,i)&&t.flip(i,n)},this.isMasked=function(t,e){return 0==(1&t)}}function o(){this.unmaskBitMatrix=function(t,e){for(var n=0;n<e;n++)for(var i=0;i<e;i++)this.isMasked(n,i)&&t.flip(i,n)},this.isMasked=function(t,e){return e%3==0}}function a(){this.unmaskBitMatrix=function(t,e){for(var n=0;n<e;n++)for(var i=0;i<e;i++)this.isMasked(n,i)&&t.flip(i,n)},this.isMasked=function(t,e){return(t+e)%3==0}}function s(){this.unmaskBitMatrix=function(t,e){for(var n=0;n<e;n++)for(var i=0;i<e;i++)this.isMasked(n,i)&&t.flip(i,n)},this.isMasked=function(t,e){return 0==(n.i(u.c)(t,1)+e/3&1)}}function h(){this.unmaskBitMatrix=function(t,e){for(var n=0;n<e;n++)for(var i=0;i<e;i++)this.isMasked(n,i)&&t.flip(i,n)},this.isMasked=function(t,e){var n=t*e;return(1&n)+n%3==0}}function f(){this.unmaskBitMatrix=function(t,e){for(var n=0;n<e;n++)for(var i=0;i<e;i++)this.isMasked(n,i)&&t.flip(i,n)},this.isMasked=function(t,e){var n=t*e;return 0==((1&n)+n%3&1)}}function w(){this.unmaskBitMatrix=function(t,e){for(var n=0;n<e;n++)for(var i=0;i<e;i++)this.isMasked(n,i)&&t.flip(i,n)},this.isMasked=function(t,e){return 0==((t+e&1)+t*e%3&1)}}var u=n(0),l={};l.forReference=function(t){if(t<0||t>7)throw\"System.ArgumentException\";return l.DATA_MASKS[t]},l.DATA_MASKS=[new i,new r,new o,new a,new s,new h,new f,new w],e.a=l},function(t,e,n){\"use strict\";var i=n(18),r=n(3),o=n(9),a=n(10),s=n(11),h={};h.rsDecoder=new i.a(r.a.QR_CODE_FIELD),h.correctErrors=function(t,e){for(var n=t.length,i=new Array(n),r=0;r<n;r++)i[r]=255&t[r];var o=t.length-e;try{h.rsDecoder.decode(i,o)}catch(t){throw t}for(var r=0;r<e;r++)t[r]=i[r]},h.decode=function(t){for(var e=new o.a(t),n=e.readVersion(),i=e.readFormatInformation().errorCorrectionLevel,r=e.readCodewords(),f=a.a.getDataBlocks(r,n,i),w=0,u=0;u<f.length;u++)w+=f[u].numDataCodewords;for(var l=new Array(w),c=0,d=0;d<f.length;d++){var p=f[d],g=p.codewords,v=p.numDataCodewords;h.correctErrors(g,v);for(var u=0;u<v;u++)l[c++]=g[u]}return new s.a(l,n.versionNumber,i.bits)},e.a=h},function(t,e,n){\"use strict\";function i(t,e,n,i,r,o,a,s,h){this.a11=t,this.a12=i,this.a13=a,this.a21=e,this.a22=r,this.a23=s,this.a31=n,this.a32=o,this.a33=h}function r(t,e){this.bits=t,this.points=e}function o(t){this.image=t,this.resultPointCallback=null}e.a=o;var a=n(5),s=n(8),h=n(17),f=n(16);i.prototype.transformPoints1=function(t){for(var e=t.length,n=this.a11,i=this.a12,r=this.a13,o=this.a21,a=this.a22,s=this.a23,h=this.a31,f=this.a32,w=this.a33,u=0;u<e;u+=2){var l=t[u],c=t[u+1],d=r*l+s*c+w;t[u]=(n*l+o*c+h)/d,t[u+1]=(i*l+a*c+f)/d}},i.prototype.transformPoints2=function(t,e){for(var n=t.length,i=0;i<n;i++){var r=t[i],o=e[i],a=this.a13*r+this.a23*o+this.a33;t[i]=(this.a11*r+this.a21*o+this.a31)/a,e[i]=(this.a12*r+this.a22*o+this.a32)/a}},i.prototype.buildAdjoint=function(){return new i(this.a22*this.a33-this.a23*this.a32,this.a23*this.a31-this.a21*this.a33,this.a21*this.a32-this.a22*this.a31,this.a13*this.a32-this.a12*this.a33,this.a11*this.a33-this.a13*this.a31,this.a12*this.a31-this.a11*this.a32,this.a12*this.a23-this.a13*this.a22,this.a13*this.a21-this.a11*this.a23,this.a11*this.a22-this.a12*this.a21)},i.prototype.times=function(t){return new i(this.a11*t.a11+this.a21*t.a12+this.a31*t.a13,this.a11*t.a21+this.a21*t.a22+this.a31*t.a23,this.a11*t.a31+this.a21*t.a32+this.a31*t.a33,this.a12*t.a11+this.a22*t.a12+this.a32*t.a13,this.a12*t.a21+this.a22*t.a22+this.a32*t.a23,this.a12*t.a31+this.a22*t.a32+this.a32*t.a33,this.a13*t.a11+this.a23*t.a12+this.a33*t.a13,this.a13*t.a21+this.a23*t.a22+this.a33*t.a23,this.a13*t.a31+this.a23*t.a32+this.a33*t.a33)},i.quadrilateralToQuadrilateral=function(t,e,n,i,r,o,a,s,h,f,w,u,l,c,d,p){var g=this.quadrilateralToSquare(t,e,n,i,r,o,a,s);return this.squareToQuadrilateral(h,f,w,u,l,c,d,p).times(g)},i.squareToQuadrilateral=function(t,e,n,r,o,a,s,h){var f=h-a,w=e-r+a-h;if(0==f&&0==w)return new i(n-t,o-n,t,r-e,a-r,e,0,0,1);var u=n-o,l=s-o,c=t-n+o-s,d=r-a,p=u*f-l*d,g=(c*f-l*w)/p,v=(u*w-c*d)/p;return new i(n-t+g*n,s-t+v*s,t,r-e+g*r,h-e+v*h,e,g,v,1)},i.quadrilateralToSquare=function(t,e,n,i,r,o,a,s){return this.squareToQuadrilateral(t,e,n,i,r,o,a,s).buildAdjoint()},o.prototype.sizeOfBlackWhiteBlackRun=function(t,e,n,i){var r=Math.abs(i-e)>Math.abs(n-t);if(r){var o=t;t=e,e=o,o=n,n=i,i=o}for(var a=Math.abs(n-t),s=Math.abs(i-e),h=-a>>1,f=e<i?1:-1,w=t<n?1:-1,u=0,l=t,c=e;l!=n;l+=w){var d=r?c:l,p=r?l:c;if(1==u?this.image.data[d+p*this.image.width]&&u++:this.image.data[d+p*this.image.width]||u++,3==u){var g=l-t,v=c-e;return Math.sqrt(g*g+v*v)}if((h+=s)>0){if(c==i)break;c+=f,h-=a}}var m=n-t,b=i-e;return Math.sqrt(m*m+b*b)},o.prototype.sizeOfBlackWhiteBlackRunBothWays=function(t,e,n,i){var r=this.sizeOfBlackWhiteBlackRun(t,e,n,i),o=1,a=t-(n-t);a<0?(o=t/(t-a),a=0):a>=this.image.width&&(o=(this.image.width-1-t)/(a-t),a=this.image.width-1);var s=Math.floor(e-(i-e)*o);return o=1,s<0?(o=e/(e-s),s=0):s>=this.image.height&&(o=(this.image.height-1-e)/(s-e),s=this.image.height-1),a=Math.floor(t+(a-t)*o),(r+=this.sizeOfBlackWhiteBlackRun(t,e,a,s))-1},o.prototype.calculateModuleSizeOneWay=function(t,e){var n=this.sizeOfBlackWhiteBlackRunBothWays(Math.floor(t.X),Math.floor(t.Y),Math.floor(e.X),Math.floor(e.Y)),i=this.sizeOfBlackWhiteBlackRunBothWays(Math.floor(e.X),Math.floor(e.Y),Math.floor(t.X),Math.floor(t.Y));return isNaN(n)?i/7:isNaN(i)?n/7:(n+i)/14},o.prototype.calculateModuleSize=function(t,e,n){return(this.calculateModuleSizeOneWay(t,e)+this.calculateModuleSizeOneWay(t,n))/2},o.prototype.distance=function(t,e){var n=t.X-e.X,i=t.Y-e.Y;return Math.sqrt(n*n+i*i)},o.prototype.computeDimension=function(t,e,n,i){var r=Math.round(this.distance(t,e)/i),o=Math.round(this.distance(t,n)/i),a=7+(r+o>>1);switch(3&a){case 0:a++;break;case 2:a--;break;case 3:throw\"Error\"}return a},o.prototype.findAlignmentInRegion=function(t,e,n,i){var r=Math.floor(i*t),o=Math.max(0,e-r),a=Math.min(this.image.width-1,e+r);if(a-o<3*t)throw\"Error\";var h=Math.max(0,n-r),f=Math.min(this.image.height-1,n+r);return new s.a(this.image,o,h,a-o,f-h,t,this.resultPointCallback).find()},o.prototype.createTransform=function(t,e,n,r,o){var a,s,h,f,w=o-3.5;return null!=r?(a=r.X,s=r.Y,h=f=w-3):(a=e.X-t.X+n.X,s=e.Y-t.Y+n.Y,h=f=w),i.quadrilateralToQuadrilateral(3.5,3.5,w,3.5,h,f,3.5,w,t.X,t.Y,e.X,e.Y,a,s,n.X,n.Y)},o.prototype.sampleGrid=function(t,e,n){return h.a.sampleGrid3(t,n,e)},o.prototype.processFinderPatternInfo=function(t){var e=t.topLeft,n=t.topRight,i=t.bottomLeft,o=this.calculateModuleSize(e,n,i);if(o<1)throw\"Error\";var s=this.computeDimension(e,n,i,o),h=a.a.getProvisionalVersionForDimension(s),f=h.DimensionForVersion-7,w=null;if(h.alignmentPatternCenters.length>0)for(var u=n.X-e.X+i.X,l=n.Y-e.Y+i.Y,c=1-3/f,d=Math.floor(e.X+c*(u-e.X)),p=Math.floor(e.Y+c*(l-e.Y)),g=4;g<=16;g<<=1){w=this.findAlignmentInRegion(o,d,p,g);break}var v,m=this.createTransform(e,n,i,w,s),b=this.sampleGrid(this.image,m,s);return v=null==w?[i,e,n]:[i,e,n,w],new r(b,v)},o.prototype.detect=function(){var t=(new f.a).findFinderPattern(this.image);return this.processFinderPatternInfo(t)}},function(t,e,n){\"use strict\";function i(t,e,n){this.ordinal_Renamed_Field=t,this.bits=e,this.name=n}e.a=i,i.prototype.ordinal=function(){return this.ordinal_Renamed_Field},i.forBits=function(t){if(t<0||t>=r.length)throw\"ArgumentException\";return r[t]};var r=[new i(1,0,\"M\"),new i(0,1,\"L\"),new i(3,2,\"H\"),new i(2,3,\"Q\")]},function(t,e,n){\"use strict\";function i(t){function e(t,e){var n=t.X-e.X,i=t.Y-e.Y;return Math.sqrt(n*n+i*i)}var n,i,r,o=e(t[0],t[1]),a=e(t[1],t[2]),s=e(t[0],t[2]);if(a>=o&&a>=s?(i=t[0],n=t[1],r=t[2]):s>=a&&s>=o?(i=t[1],n=t[0],r=t[2]):(i=t[2],n=t[0],r=t[1]),function(t,e,n){var i=e.x,r=e.y;return(n.x-i)*(t.y-r)-(n.y-r)*(t.x-i)}(n,i,r)<0){var h=n;n=r,r=h}t[0]=n,t[1]=i,t[2]=r}function r(t,e,n){this.x=t,this.y=e,this.count=1,this.estimatedModuleSize=n}function o(t){this.bottomLeft=t[0],this.topLeft=t[1],this.topRight=t[2]}function a(){this.image=null,this.possibleCenters=[],this.hasSkipped=!1,this.crossCheckStateCount=[0,0,0,0,0],this.resultPointCallback=null}e.a=a;Object.defineProperty(r.prototype,\"X\",{get:function(){return this.x}}),Object.defineProperty(r.prototype,\"Y\",{get:function(){return this.y}}),r.prototype.incrementCount=function(){this.count++},r.prototype.aboutEquals=function(t,e,n){if(Math.abs(e-this.y)<=t&&Math.abs(n-this.x)<=t){var i=Math.abs(t-this.estimatedModuleSize);return i<=1||i/this.estimatedModuleSize<=1}return!1},Object.defineProperty(a.prototype,\"CrossCheckStateCount\",{get:function(){return this.crossCheckStateCount[0]=0,this.crossCheckStateCount[1]=0,this.crossCheckStateCount[2]=0,this.crossCheckStateCount[3]=0,this.crossCheckStateCount[4]=0,this.crossCheckStateCount}}),a.prototype.foundPatternCross=function(t){for(var e=0,n=0;n<5;n++){var i=t[n];if(0==i)return!1;e+=i}if(e<7)return!1;var r=Math.floor((e<<8)/7),o=Math.floor(r/2);return Math.abs(r-(t[0]<<8))<o&&Math.abs(r-(t[1]<<8))<o&&Math.abs(3*r-(t[2]<<8))<3*o&&Math.abs(r-(t[3]<<8))<o&&Math.abs(r-(t[4]<<8))<o},a.prototype.centerFromEnd=function(t,e){return e-t[4]-t[3]-t[2]/2},a.prototype.crossCheckVertical=function(t,e,n,i){for(var r=this.image,o=r.height,a=this.CrossCheckStateCount,s=t;s>=0&&r.data[e+s*r.width];)a[2]++,s--;if(s<0)return NaN;for(;s>=0&&!r.data[e+s*r.width]&&a[1]<=n;)a[1]++,s--;if(s<0||a[1]>n)return NaN;for(;s>=0&&r.data[e+s*r.width]&&a[0]<=n;)a[0]++,s--;if(a[0]>n)return NaN;for(s=t+1;s<o&&r.data[e+s*r.width];)a[2]++,s++;if(s==o)return NaN;for(;s<o&&!r.data[e+s*r.width]&&a[3]<n;)a[3]++,s++;if(s==o||a[3]>=n)return NaN;for(;s<o&&r.data[e+s*r.width]&&a[4]<n;)a[4]++,s++;if(a[4]>=n)return NaN;var h=a[0]+a[1]+a[2]+a[3]+a[4];return 5*Math.abs(h-i)>=2*i?NaN:this.foundPatternCross(a)?this.centerFromEnd(a,s):NaN},a.prototype.crossCheckHorizontal=function(t,e,n,i){for(var r=this.image,o=r.width,a=this.CrossCheckStateCount,s=t;s>=0&&r.data[s+e*r.width];)a[2]++,s--;if(s<0)return NaN;for(;s>=0&&!r.data[s+e*r.width]&&a[1]<=n;)a[1]++,s--;if(s<0||a[1]>n)return NaN;for(;s>=0&&r.data[s+e*r.width]&&a[0]<=n;)a[0]++,s--;if(a[0]>n)return NaN;for(s=t+1;s<o&&r.data[s+e*r.width];)a[2]++,s++;if(s==o)return NaN;for(;s<o&&!r.data[s+e*r.width]&&a[3]<n;)a[3]++,s++;if(s==o||a[3]>=n)return NaN;for(;s<o&&r.data[s+e*r.width]&&a[4]<n;)a[4]++,s++;if(a[4]>=n)return NaN;var h=a[0]+a[1]+a[2]+a[3]+a[4];return 5*Math.abs(h-i)>=i?NaN:this.foundPatternCross(a)?this.centerFromEnd(a,s):NaN},a.prototype.handlePossibleCenter=function(t,e,n){var i=t[0]+t[1]+t[2]+t[3]+t[4],o=this.centerFromEnd(t,n),a=this.crossCheckVertical(e,Math.floor(o),t[2],i);if(!isNaN(a)&&(o=this.crossCheckHorizontal(Math.floor(o),Math.floor(a),t[2],i),!isNaN(o))){for(var s=i/7,h=!1,f=this.possibleCenters.length,w=0;w<f;w++){var u=this.possibleCenters[w];if(u.aboutEquals(s,a,o)){u.incrementCount(),h=!0;break}}if(!h){var l=new r(o,a,s);this.possibleCenters.push(l),null!=this.resultPointCallback&&this.resultPointCallback.foundPossibleResultPoint(l)}return!0}return!1},a.prototype.selectBestPatterns=function(){var t=this.possibleCenters.length;if(t<3)throw\"Couldn't find enough finder patterns:\"+t+\" patterns found\";if(t>3){for(var e=0,n=0,i=0;i<t;i++){var r=this.possibleCenters[i].estimatedModuleSize;e+=r,n+=r*r}var o=e/t;this.possibleCenters.sort(function(t,e){var n=Math.abs(e.estimatedModuleSize-o),i=Math.abs(t.estimatedModuleSize-o);return n<i?-1:n==i?0:1});for(var a=Math.sqrt(n/t-o*o),s=Math.max(.2*o,a),i=this.possibleCenters-1;i>=0;i--){var h=this.possibleCenters[i];Math.abs(h.estimatedModuleSize-o)>s&&this.possibleCenters.splice(i,1)}}return this.possibleCenters.length>3&&this.possibleCenters.sort(function(t,e){return t.count>e.count?-1:t.count<e.count?1:0}),[this.possibleCenters[0],this.possibleCenters[1],this.possibleCenters[2]]},a.prototype.findRowSkip=function(){var t=this.possibleCenters.length;if(t<=1)return 0;for(var e=null,n=0;n<t;n++){var i=this.possibleCenters[n];if(i.count>=2){if(null!=e)return this.hasSkipped=!0,Math.floor((Math.abs(e.X-i.X)-Math.abs(e.Y-i.Y))/2);e=i}}return 0},a.prototype.haveMultiplyConfirmedCenters=function(){for(var t=0,e=0,n=this.possibleCenters.length,i=0;i<n;i++){var r=this.possibleCenters[i];r.count>=2&&(t++,e+=r.estimatedModuleSize)}if(t<3)return!1;for(var o=e/n,a=0,i=0;i<n;i++)r=this.possibleCenters[i],a+=Math.abs(r.estimatedModuleSize-o);return a<=.05*e},a.prototype.findFinderPattern=function(t){this.image=t;var e=t.height,n=t.width,r=Math.floor(3*e/228);r<3&&(r=3);for(var a=!1,s=new Array(5),h=r-1;h<e&&!a;h+=r){s[0]=0,s[1]=0,s[2]=0,s[3]=0,s[4]=0;for(var f=0,w=0;w<n;w++)if(t.data[w+h*t.width])1==(1&f)&&f++,s[f]++;else if(0==(1&f))if(4==f)if(this.foundPatternCross(s)){var u=this.handlePossibleCenter(s,h,w);if(u)if(r=2,this.hasSkipped)a=this.haveMultiplyConfirmedCenters();else{var l=this.findRowSkip();l>s[2]&&(h+=l-s[2]-r,w=n-1)}else{do{w++}while(w<n&&!t.data[w+h*t.width]);w--}f=0,s[0]=0,s[1]=0,s[2]=0,s[3]=0,s[4]=0}else s[0]=s[2],s[1]=s[3],s[2]=s[4],s[3]=1,s[4]=0,f=3;else s[++f]++;else s[f]++;if(this.foundPatternCross(s)){var u=this.handlePossibleCenter(s,h,n);u&&(r=s[0],this.hasSkipped&&(a=this.haveMultiplyConfirmedCenters()))}}var c=this.selectBestPatterns();return i(c),new o(c)}},function(t,e,n){\"use strict\";var i=n(1),r={};r.checkAndNudgePoints=function(t,e){for(var n=t.width,i=t.height,r=!0,o=0;o<e.length&&r;o+=2){var a=Math.floor(e[o]),s=Math.floor(e[o+1]);if(a<-1||a>n||s<-1||s>i)throw\"Error.checkAndNudgePoints \";r=!1,-1==a?(e[o]=0,r=!0):a==n&&(e[o]=n-1,r=!0),-1==s?(e[o+1]=0,r=!0):s==i&&(e[o+1]=i-1,r=!0)}r=!0;for(var o=e.length-2;o>=0&&r;o-=2){var a=Math.floor(e[o]),s=Math.floor(e[o+1]);if(a<-1||a>n||s<-1||s>i)throw\"Error.checkAndNudgePoints \";r=!1,-1==a?(e[o]=0,r=!0):a==n&&(e[o]=n-1,r=!0),-1==s?(e[o+1]=0,r=!0):s==i&&(e[o+1]=i-1,r=!0)}},r.sampleGrid3=function(t,e,n){for(var o=new i.a(e),a=new Array(e<<1),s=0;s<e;s++){for(var h=a.length,f=s+.5,w=0;w<h;w+=2)a[w]=.5+(w>>1),a[w+1]=f;n.transformPoints1(a),r.checkAndNudgePoints(t,a);try{for(var w=0;w<h;w+=2){t.data[Math.floor(a[w])+t.width*Math.floor(a[w+1])]&&o.set_Renamed(w>>1,s)}}catch(t){throw\"Error.checkAndNudgePoints\"}}return o},e.a=r},function(t,e,n){\"use strict\";function i(t){this.field=t}e.a=i;var r=n(3),o=n(4);i.prototype.decode=function(t,e){for(var n=new o.a(this.field,t),i=new Array(e),a=0;a<i.length;a++)i[a]=0;for(var s=!0,a=0;a<e;a++){var h=n.evaluateAt(this.field.exp(a));i[i.length-1-a]=h,0!=h&&(s=!1)}if(!s)for(var f=new o.a(this.field,i),w=this.runEuclideanAlgorithm(this.field.buildMonomial(e,1),f,e),u=w[0],l=w[1],c=this.findErrorLocations(u),d=this.findErrorMagnitudes(l,c,!1),a=0;a<c.length;a++){var p=t.length-1-this.field.log(c[a]);if(p<0)throw\"ReedSolomonException Bad error location\";t[p]=r.a.prototype.addOrSubtract(t[p],d[a])}},i.prototype.runEuclideanAlgorithm=function(t,e,n){if(t.Degree<e.Degree){var i=t;t=e,e=i}for(var r=t,o=e,a=this.field.One,s=this.field.Zero,h=this.field.Zero,f=this.field.One;o.Degree>=Math.floor(n/2);){var w=r,u=a,l=h;if(r=o,a=s,h=f,r.Zero)throw\"r_{i-1} was zero\";o=w;for(var c=this.field.Zero,d=r.getCoefficient(r.Degree),p=this.field.inverse(d);o.Degree>=r.Degree&&!o.Zero;){var g=o.Degree-r.Degree,v=this.field.multiply(o.getCoefficient(o.Degree),p);c=c.addOrSubtract(this.field.buildMonomial(g,v)),o=o.addOrSubtract(r.multiplyByMonomial(g,v))}s=c.multiply1(a).addOrSubtract(u),f=c.multiply1(h).addOrSubtract(l)}var m=f.getCoefficient(0);if(0==m)throw\"ReedSolomonException sigmaTilde(0) was zero\";var b=this.field.inverse(m);return[f.multiply2(b),o.multiply2(b)]},i.prototype.findErrorLocations=function(t){var e=t.Degree;if(1==e)return new Array(t.getCoefficient(1));for(var n=new Array(e),i=0,r=1;r<256&&i<e;r++)0==t.evaluateAt(r)&&(n[i]=this.field.inverse(r),i++);if(i!=e)throw\"Error locator degree does not match number of roots\";return n},i.prototype.findErrorMagnitudes=function(t,e,n){for(var i=e.length,o=new Array(i),a=0;a<i;a++){for(var s=this.field.inverse(e[a]),h=1,f=0;f<i;f++)a!=f&&(h=this.field.multiply(h,r.a.prototype.addOrSubtract(1,this.field.multiply(e[f],s))));o[a]=this.field.multiply(t.evaluateAt(s),this.field.inverse(h)),n&&(o[a]=this.field.multiply(o[a],s))}return o}},function(t,e,n){(function(t){var e=n(6).default,i=new e;i.callback=function(t,e){postMessage({result:e,err:t})},onmessage=function(t){var e=t.data;i.decode(e)}}).call(e,n(7)(t))}]);"
+
+/***/ }),
+/* 7 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+ /* eslint-env node */
+
+
+// SDP helpers.
+var SDPUtils = {};
+
+// Generate an alphanumeric identifier for cname or mids.
+// TODO: use UUIDs instead? https://gist.github.com/jed/982883
+SDPUtils.generateIdentifier = function() {
+  return Math.random().toString(36).substr(2, 10);
+};
+
+// The RTCP CNAME used by all peerconnections from the same JS.
+SDPUtils.localCName = SDPUtils.generateIdentifier();
+
+// Splits SDP into lines, dealing with both CRLF and LF.
+SDPUtils.splitLines = function(blob) {
+  return blob.trim().split('\n').map(function(line) {
+    return line.trim();
+  });
+};
+// Splits SDP into sessionpart and mediasections. Ensures CRLF.
+SDPUtils.splitSections = function(blob) {
+  var parts = blob.split('\nm=');
+  return parts.map(function(part, index) {
+    return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
+  });
+};
+
+// Returns lines that start with a certain prefix.
+SDPUtils.matchPrefix = function(blob, prefix) {
+  return SDPUtils.splitLines(blob).filter(function(line) {
+    return line.indexOf(prefix) === 0;
+  });
+};
+
+// Parses an ICE candidate line. Sample input:
+// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
+// rport 55996"
+SDPUtils.parseCandidate = function(line) {
+  var parts;
+  // Parse both variants.
+  if (line.indexOf('a=candidate:') === 0) {
+    parts = line.substring(12).split(' ');
+  } else {
+    parts = line.substring(10).split(' ');
+  }
+
+  var candidate = {
+    foundation: parts[0],
+    component: parts[1],
+    protocol: parts[2].toLowerCase(),
+    priority: parseInt(parts[3], 10),
+    ip: parts[4],
+    port: parseInt(parts[5], 10),
+    // skip parts[6] == 'typ'
+    type: parts[7]
+  };
+
+  for (var i = 8; i < parts.length; i += 2) {
+    switch (parts[i]) {
+      case 'raddr':
+        candidate.relatedAddress = parts[i + 1];
+        break;
+      case 'rport':
+        candidate.relatedPort = parseInt(parts[i + 1], 10);
+        break;
+      case 'tcptype':
+        candidate.tcpType = parts[i + 1];
+        break;
+      default: // extension handling, in particular ufrag
+        candidate[parts[i]] = parts[i + 1];
+        break;
+    }
+  }
+  return candidate;
+};
+
+// Translates a candidate object into SDP candidate attribute.
+SDPUtils.writeCandidate = function(candidate) {
+  var sdp = [];
+  sdp.push(candidate.foundation);
+  sdp.push(candidate.component);
+  sdp.push(candidate.protocol.toUpperCase());
+  sdp.push(candidate.priority);
+  sdp.push(candidate.ip);
+  sdp.push(candidate.port);
+
+  var type = candidate.type;
+  sdp.push('typ');
+  sdp.push(type);
+  if (type !== 'host' && candidate.relatedAddress &&
+      candidate.relatedPort) {
+    sdp.push('raddr');
+    sdp.push(candidate.relatedAddress); // was: relAddr
+    sdp.push('rport');
+    sdp.push(candidate.relatedPort); // was: relPort
+  }
+  if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
+    sdp.push('tcptype');
+    sdp.push(candidate.tcpType);
+  }
+  return 'candidate:' + sdp.join(' ');
+};
+
+// Parses an ice-options line, returns an array of option tags.
+// a=ice-options:foo bar
+SDPUtils.parseIceOptions = function(line) {
+  return line.substr(14).split(' ');
+}
+
+// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
+// a=rtpmap:111 opus/48000/2
+SDPUtils.parseRtpMap = function(line) {
+  var parts = line.substr(9).split(' ');
+  var parsed = {
+    payloadType: parseInt(parts.shift(), 10) // was: id
+  };
+
+  parts = parts[0].split('/');
+
+  parsed.name = parts[0];
+  parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
+  // was: channels
+  parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
+  return parsed;
+};
+
+// Generate an a=rtpmap line from RTCRtpCodecCapability or
+// RTCRtpCodecParameters.
+SDPUtils.writeRtpMap = function(codec) {
+  var pt = codec.payloadType;
+  if (codec.preferredPayloadType !== undefined) {
+    pt = codec.preferredPayloadType;
+  }
+  return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +
+      (codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\r\n';
+};
+
+// Parses an a=extmap line (headerextension from RFC 5285). Sample input:
+// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
+// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
+SDPUtils.parseExtmap = function(line) {
+  var parts = line.substr(9).split(' ');
+  return {
+    id: parseInt(parts[0], 10),
+    direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
+    uri: parts[1]
+  };
+};
+
+// Generates a=extmap line from RTCRtpHeaderExtensionParameters or
+// RTCRtpHeaderExtension.
+SDPUtils.writeExtmap = function(headerExtension) {
+  return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +
+      (headerExtension.direction && headerExtension.direction !== 'sendrecv'
+          ? '/' + headerExtension.direction
+          : '') +
+      ' ' + headerExtension.uri + '\r\n';
+};
+
+// Parses an ftmp line, returns dictionary. Sample input:
+// a=fmtp:96 vbr=on;cng=on
+// Also deals with vbr=on; cng=on
+SDPUtils.parseFmtp = function(line) {
+  var parsed = {};
+  var kv;
+  var parts = line.substr(line.indexOf(' ') + 1).split(';');
+  for (var j = 0; j < parts.length; j++) {
+    kv = parts[j].trim().split('=');
+    parsed[kv[0].trim()] = kv[1];
+  }
+  return parsed;
+};
+
+// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
+SDPUtils.writeFmtp = function(codec) {
+  var line = '';
+  var pt = codec.payloadType;
+  if (codec.preferredPayloadType !== undefined) {
+    pt = codec.preferredPayloadType;
+  }
+  if (codec.parameters && Object.keys(codec.parameters).length) {
+    var params = [];
+    Object.keys(codec.parameters).forEach(function(param) {
+      params.push(param + '=' + codec.parameters[param]);
+    });
+    line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
+  }
+  return line;
+};
+
+// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
+// a=rtcp-fb:98 nack rpsi
+SDPUtils.parseRtcpFb = function(line) {
+  var parts = line.substr(line.indexOf(' ') + 1).split(' ');
+  return {
+    type: parts.shift(),
+    parameter: parts.join(' ')
+  };
+};
+// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
+SDPUtils.writeRtcpFb = function(codec) {
+  var lines = '';
+  var pt = codec.payloadType;
+  if (codec.preferredPayloadType !== undefined) {
+    pt = codec.preferredPayloadType;
+  }
+  if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
+    // FIXME: special handling for trr-int?
+    codec.rtcpFeedback.forEach(function(fb) {
+      lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +
+      (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +
+          '\r\n';
+    });
+  }
+  return lines;
+};
+
+// Parses an RFC 5576 ssrc media attribute. Sample input:
+// a=ssrc:3735928559 cname:something
+SDPUtils.parseSsrcMedia = function(line) {
+  var sp = line.indexOf(' ');
+  var parts = {
+    ssrc: parseInt(line.substr(7, sp - 7), 10)
+  };
+  var colon = line.indexOf(':', sp);
+  if (colon > -1) {
+    parts.attribute = line.substr(sp + 1, colon - sp - 1);
+    parts.value = line.substr(colon + 1);
+  } else {
+    parts.attribute = line.substr(sp + 1);
+  }
+  return parts;
+};
+
+// Extracts the MID (RFC 5888) from a media section.
+// returns the MID or undefined if no mid line was found.
+SDPUtils.getMid = function(mediaSection) {
+  var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
+  if (mid) {
+    return mid.substr(6);
+  }
+}
+
+SDPUtils.parseFingerprint = function(line) {
+  var parts = line.substr(14).split(' ');
+  return {
+    algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
+    value: parts[1]
+  };
+};
+
+// Extracts DTLS parameters from SDP media section or sessionpart.
+// FIXME: for consistency with other functions this should only
+//   get the fingerprint line as input. See also getIceParameters.
+SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
+  var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
+      'a=fingerprint:');
+  // Note: a=setup line is ignored since we use the 'auto' role.
+  // Note2: 'algorithm' is not case sensitive except in Edge.
+  return {
+    role: 'auto',
+    fingerprints: lines.map(SDPUtils.parseFingerprint)
+  };
+};
+
+// Serializes DTLS parameters to SDP.
+SDPUtils.writeDtlsParameters = function(params, setupType) {
+  var sdp = 'a=setup:' + setupType + '\r\n';
+  params.fingerprints.forEach(function(fp) {
+    sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
+  });
+  return sdp;
+};
+// Parses ICE information from SDP media section or sessionpart.
+// FIXME: for consistency with other functions this should only
+//   get the ice-ufrag and ice-pwd lines as input.
+SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
+  var lines = SDPUtils.splitLines(mediaSection);
+  // Search in session part, too.
+  lines = lines.concat(SDPUtils.splitLines(sessionpart));
+  var iceParameters = {
+    usernameFragment: lines.filter(function(line) {
+      return line.indexOf('a=ice-ufrag:') === 0;
+    })[0].substr(12),
+    password: lines.filter(function(line) {
+      return line.indexOf('a=ice-pwd:') === 0;
+    })[0].substr(10)
+  };
+  return iceParameters;
+};
+
+// Serializes ICE parameters to SDP.
+SDPUtils.writeIceParameters = function(params) {
+  return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
+      'a=ice-pwd:' + params.password + '\r\n';
+};
+
+// Parses the SDP media section and returns RTCRtpParameters.
+SDPUtils.parseRtpParameters = function(mediaSection) {
+  var description = {
+    codecs: [],
+    headerExtensions: [],
+    fecMechanisms: [],
+    rtcp: []
+  };
+  var lines = SDPUtils.splitLines(mediaSection);
+  var mline = lines[0].split(' ');
+  for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
+    var pt = mline[i];
+    var rtpmapline = SDPUtils.matchPrefix(
+        mediaSection, 'a=rtpmap:' + pt + ' ')[0];
+    if (rtpmapline) {
+      var codec = SDPUtils.parseRtpMap(rtpmapline);
+      var fmtps = SDPUtils.matchPrefix(
+          mediaSection, 'a=fmtp:' + pt + ' ');
+      // Only the first a=fmtp:<pt> is considered.
+      codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
+      codec.rtcpFeedback = SDPUtils.matchPrefix(
+          mediaSection, 'a=rtcp-fb:' + pt + ' ')
+        .map(SDPUtils.parseRtcpFb);
+      description.codecs.push(codec);
+      // parse FEC mechanisms from rtpmap lines.
+      switch (codec.name.toUpperCase()) {
+        case 'RED':
+        case 'ULPFEC':
+          description.fecMechanisms.push(codec.name.toUpperCase());
+          break;
+        default: // only RED and ULPFEC are recognized as FEC mechanisms.
+          break;
+      }
+    }
+  }
+  SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {
+    description.headerExtensions.push(SDPUtils.parseExtmap(line));
+  });
+  // FIXME: parse rtcp.
+  return description;
+};
+
+// Generates parts of the SDP media section describing the capabilities /
+// parameters.
+SDPUtils.writeRtpDescription = function(kind, caps) {
+  var sdp = '';
+
+  // Build the mline.
+  sdp += 'm=' + kind + ' ';
+  sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
+  sdp += ' UDP/TLS/RTP/SAVPF ';
+  sdp += caps.codecs.map(function(codec) {
+    if (codec.preferredPayloadType !== undefined) {
+      return codec.preferredPayloadType;
+    }
+    return codec.payloadType;
+  }).join(' ') + '\r\n';
+
+  sdp += 'c=IN IP4 0.0.0.0\r\n';
+  sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
+
+  // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
+  caps.codecs.forEach(function(codec) {
+    sdp += SDPUtils.writeRtpMap(codec);
+    sdp += SDPUtils.writeFmtp(codec);
+    sdp += SDPUtils.writeRtcpFb(codec);
+  });
+  var maxptime = 0;
+  caps.codecs.forEach(function(codec) {
+    if (codec.maxptime > maxptime) {
+      maxptime = codec.maxptime;
+    }
+  });
+  if (maxptime > 0) {
+    sdp += 'a=maxptime:' + maxptime + '\r\n';
+  }
+  sdp += 'a=rtcp-mux\r\n';
+
+  caps.headerExtensions.forEach(function(extension) {
+    sdp += SDPUtils.writeExtmap(extension);
+  });
+  // FIXME: write fecMechanisms.
+  return sdp;
+};
+
+// Parses the SDP media section and returns an array of
+// RTCRtpEncodingParameters.
+SDPUtils.parseRtpEncodingParameters = function(mediaSection) {
+  var encodingParameters = [];
+  var description = SDPUtils.parseRtpParameters(mediaSection);
+  var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
+  var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
+
+  // filter a=ssrc:... cname:, ignore PlanB-msid
+  var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
+  .map(function(line) {
+    return SDPUtils.parseSsrcMedia(line);
+  })
+  .filter(function(parts) {
+    return parts.attribute === 'cname';
+  });
+  var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
+  var secondarySsrc;
+
+  var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')
+  .map(function(line) {
+    var parts = line.split(' ');
+    parts.shift();
+    return parts.map(function(part) {
+      return parseInt(part, 10);
+    });
+  });
+  if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
+    secondarySsrc = flows[0][1];
+  }
+
+  description.codecs.forEach(function(codec) {
+    if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
+      var encParam = {
+        ssrc: primarySsrc,
+        codecPayloadType: parseInt(codec.parameters.apt, 10),
+        rtx: {
+          ssrc: secondarySsrc
+        }
+      };
+      encodingParameters.push(encParam);
+      if (hasRed) {
+        encParam = JSON.parse(JSON.stringify(encParam));
+        encParam.fec = {
+          ssrc: secondarySsrc,
+          mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
+        };
+        encodingParameters.push(encParam);
+      }
+    }
+  });
+  if (encodingParameters.length === 0 && primarySsrc) {
+    encodingParameters.push({
+      ssrc: primarySsrc
+    });
+  }
+
+  // we support both b=AS and b=TIAS but interpret AS as TIAS.
+  var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
+  if (bandwidth.length) {
+    if (bandwidth[0].indexOf('b=TIAS:') === 0) {
+      bandwidth = parseInt(bandwidth[0].substr(7), 10);
+    } else if (bandwidth[0].indexOf('b=AS:') === 0) {
+      bandwidth = parseInt(bandwidth[0].substr(5), 10);
+    }
+    encodingParameters.forEach(function(params) {
+      params.maxBitrate = bandwidth;
+    });
+  }
+  return encodingParameters;
+};
+
+// parses http://draft.ortc.org/#rtcrtcpparameters*
+SDPUtils.parseRtcpParameters = function(mediaSection) {
+  var rtcpParameters = {};
+
+  var cname;
+  // Gets the first SSRC. Note that with RTX there might be multiple
+  // SSRCs.
+  var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
+      .map(function(line) {
+        return SDPUtils.parseSsrcMedia(line);
+      })
+      .filter(function(obj) {
+        return obj.attribute === 'cname';
+      })[0];
+  if (remoteSsrc) {
+    rtcpParameters.cname = remoteSsrc.value;
+    rtcpParameters.ssrc = remoteSsrc.ssrc;
+  }
+
+  // Edge uses the compound attribute instead of reducedSize
+  // compound is !reducedSize
+  var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');
+  rtcpParameters.reducedSize = rsize.length > 0;
+  rtcpParameters.compound = rsize.length === 0;
+
+  // parses the rtcp-mux attrіbute.
+  // Note that Edge does not support unmuxed RTCP.
+  var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');
+  rtcpParameters.mux = mux.length > 0;
+
+  return rtcpParameters;
+};
+
+// parses either a=msid: or a=ssrc:... msid lines and returns
+// the id of the MediaStream and MediaStreamTrack.
+SDPUtils.parseMsid = function(mediaSection) {
+  var parts;
+  var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
+  if (spec.length === 1) {
+    parts = spec[0].substr(7).split(' ');
+    return {stream: parts[0], track: parts[1]};
+  }
+  var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
+  .map(function(line) {
+    return SDPUtils.parseSsrcMedia(line);
+  })
+  .filter(function(parts) {
+    return parts.attribute === 'msid';
+  });
+  if (planB.length > 0) {
+    parts = planB[0].value.split(' ');
+    return {stream: parts[0], track: parts[1]};
+  }
+};
+
+SDPUtils.writeSessionBoilerplate = function() {
+  // FIXME: sess-id should be an NTP timestamp.
+  return 'v=0\r\n' +
+      'o=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\r\n' +
+      's=-\r\n' +
+      't=0 0\r\n';
+};
+
+SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
+  var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
+
+  // Map ICE parameters (ufrag, pwd) to SDP.
+  sdp += SDPUtils.writeIceParameters(
+      transceiver.iceGatherer.getLocalParameters());
+
+  // Map DTLS parameters to SDP.
+  sdp += SDPUtils.writeDtlsParameters(
+      transceiver.dtlsTransport.getLocalParameters(),
+      type === 'offer' ? 'actpass' : 'active');
+
+  sdp += 'a=mid:' + transceiver.mid + '\r\n';
+
+  if (transceiver.direction) {
+    sdp += 'a=' + transceiver.direction + '\r\n';
+  } else if (transceiver.rtpSender && transceiver.rtpReceiver) {
+    sdp += 'a=sendrecv\r\n';
+  } else if (transceiver.rtpSender) {
+    sdp += 'a=sendonly\r\n';
+  } else if (transceiver.rtpReceiver) {
+    sdp += 'a=recvonly\r\n';
+  } else {
+    sdp += 'a=inactive\r\n';
+  }
+
+  if (transceiver.rtpSender) {
+    // spec.
+    var msid = 'msid:' + stream.id + ' ' +
+        transceiver.rtpSender.track.id + '\r\n';
+    sdp += 'a=' + msid;
+
+    // for Chrome.
+    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
+        ' ' + msid;
+    if (transceiver.sendEncodingParameters[0].rtx) {
+      sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
+          ' ' + msid;
+      sdp += 'a=ssrc-group:FID ' +
+          transceiver.sendEncodingParameters[0].ssrc + ' ' +
+          transceiver.sendEncodingParameters[0].rtx.ssrc +
+          '\r\n';
+    }
+  }
+  // FIXME: this should be written by writeRtpDescription.
+  sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
+      ' cname:' + SDPUtils.localCName + '\r\n';
+  if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
+    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
+        ' cname:' + SDPUtils.localCName + '\r\n';
+  }
+  return sdp;
+};
+
+// Gets the direction from the mediaSection or the sessionpart.
+SDPUtils.getDirection = function(mediaSection, sessionpart) {
+  // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
+  var lines = SDPUtils.splitLines(mediaSection);
+  for (var i = 0; i < lines.length; i++) {
+    switch (lines[i]) {
+      case 'a=sendrecv':
+      case 'a=sendonly':
+      case 'a=recvonly':
+      case 'a=inactive':
+        return lines[i].substr(2);
+      default:
+        // FIXME: What should happen here?
+    }
+  }
+  if (sessionpart) {
+    return SDPUtils.getDirection(sessionpart);
+  }
+  return 'sendrecv';
+};
+
+SDPUtils.getKind = function(mediaSection) {
+  var lines = SDPUtils.splitLines(mediaSection);
+  var mline = lines[0].split(' ');
+  return mline[0].substr(2);
+};
+
+SDPUtils.isRejected = function(mediaSection) {
+  return mediaSection.split(' ', 2)[1] === '0';
+};
+
+// Expose public methods.
+module.exports = SDPUtils;
+
+
+/***/ }),
+/* 8 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+ /* eslint-env node */
+
+
+
+// Shimming starts here.
+(function() {
+  // Utils.
+  var utils = __webpack_require__(0);
+  var logging = utils.log;
+  var browserDetails = utils.browserDetails;
+  // Export to the adapter global object visible in the browser.
+  module.exports.browserDetails = browserDetails;
+  module.exports.extractVersion = utils.extractVersion;
+  module.exports.disableLog = utils.disableLog;
+
+  // Uncomment the line below if you want logging to occur, including logging
+  // for the switch statement below. Can also be turned on in the browser via
+  // adapter.disableLog(false), but then logging from the switch statement below
+  // will not appear.
+  // require('./utils').disableLog(false);
+
+  // Browser shims.
+  var chromeShim = __webpack_require__(9) || null;
+  var edgeShim = __webpack_require__(11) || null;
+  var firefoxShim = __webpack_require__(14) || null;
+  var safariShim = __webpack_require__(16) || null;
+
+  // Shim browser if found.
+  switch (browserDetails.browser) {
+    case 'chrome':
+      if (!chromeShim || !chromeShim.shimPeerConnection) {
+        logging('Chrome shim is not included in this adapter release.');
+        return;
+      }
+      logging('adapter.js shimming chrome.');
+      // Export to the adapter global object visible in the browser.
+      module.exports.browserShim = chromeShim;
+
+      chromeShim.shimGetUserMedia();
+      chromeShim.shimMediaStream();
+      utils.shimCreateObjectURL();
+      chromeShim.shimSourceObject();
+      chromeShim.shimPeerConnection();
+      chromeShim.shimOnTrack();
+      chromeShim.shimGetSendersWithDtmf();
+      break;
+    case 'firefox':
+      if (!firefoxShim || !firefoxShim.shimPeerConnection) {
+        logging('Firefox shim is not included in this adapter release.');
+        return;
+      }
+      logging('adapter.js shimming firefox.');
+      // Export to the adapter global object visible in the browser.
+      module.exports.browserShim = firefoxShim;
+
+      firefoxShim.shimGetUserMedia();
+      utils.shimCreateObjectURL();
+      firefoxShim.shimSourceObject();
+      firefoxShim.shimPeerConnection();
+      firefoxShim.shimOnTrack();
+      break;
+    case 'edge':
+      if (!edgeShim || !edgeShim.shimPeerConnection) {
+        logging('MS edge shim is not included in this adapter release.');
+        return;
+      }
+      logging('adapter.js shimming edge.');
+      // Export to the adapter global object visible in the browser.
+      module.exports.browserShim = edgeShim;
+
+      edgeShim.shimGetUserMedia();
+      utils.shimCreateObjectURL();
+      edgeShim.shimPeerConnection();
+      edgeShim.shimReplaceTrack();
+      break;
+    case 'safari':
+      if (!safariShim) {
+        logging('Safari shim is not included in this adapter release.');
+        return;
+      }
+      logging('adapter.js shimming safari.');
+      // Export to the adapter global object visible in the browser.
+      module.exports.browserShim = safariShim;
+
+      safariShim.shimCallbacksAPI();
+      safariShim.shimAddStream();
+      safariShim.shimOnAddStream();
+      safariShim.shimGetUserMedia();
+      break;
+    default:
+      logging('Unsupported browser!');
+  }
+})();
+
+
+/***/ }),
+/* 9 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+ /* eslint-env node */
+
+var logging = __webpack_require__(0).log;
+var browserDetails = __webpack_require__(0).browserDetails;
+
+var chromeShim = {
+  shimMediaStream: function() {
+    window.MediaStream = window.MediaStream || window.webkitMediaStream;
+  },
+
+  shimOnTrack: function() {
+    if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
+        window.RTCPeerConnection.prototype)) {
+      Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
+        get: function() {
+          return this._ontrack;
+        },
+        set: function(f) {
+          var self = this;
+          if (this._ontrack) {
+            this.removeEventListener('track', this._ontrack);
+            this.removeEventListener('addstream', this._ontrackpoly);
+          }
+          this.addEventListener('track', this._ontrack = f);
+          this.addEventListener('addstream', this._ontrackpoly = function(e) {
+            // onaddstream does not fire when a track is added to an existing
+            // stream. But stream.onaddtrack is implemented so we use that.
+            e.stream.addEventListener('addtrack', function(te) {
+              var receiver;
+              if (RTCPeerConnection.prototype.getReceivers) {
+                receiver = self.getReceivers().find(function(r) {
+                  return r.track.id === te.track.id;
+                });
+              } else {
+                receiver = {track: te.track};
+              }
+
+              var event = new Event('track');
+              event.track = te.track;
+              event.receiver = receiver;
+              event.streams = [e.stream];
+              self.dispatchEvent(event);
+            });
+            e.stream.getTracks().forEach(function(track) {
+              var receiver;
+              if (RTCPeerConnection.prototype.getReceivers) {
+                receiver = self.getReceivers().find(function(r) {
+                  return r.track.id === track.id;
+                });
+              } else {
+                receiver = {track: track};
+              }
+              var event = new Event('track');
+              event.track = track;
+              event.receiver = receiver;
+              event.streams = [e.stream];
+              this.dispatchEvent(event);
+            }.bind(this));
+          }.bind(this));
+        }
+      });
+    }
+  },
+
+  shimGetSendersWithDtmf: function() {
+    if (typeof window === 'object' && window.RTCPeerConnection &&
+        !('getSenders' in RTCPeerConnection.prototype) &&
+        'createDTMFSender' in RTCPeerConnection.prototype) {
+      RTCPeerConnection.prototype.getSenders = function() {
+        return this._senders || [];
+      };
+      var origAddStream = RTCPeerConnection.prototype.addStream;
+      var origRemoveStream = RTCPeerConnection.prototype.removeStream;
+
+      if (!RTCPeerConnection.prototype.addTrack) {
+        RTCPeerConnection.prototype.addTrack = function(track, stream) {
+          var pc = this;
+          if (pc.signalingState === 'closed') {
+            throw new DOMException(
+              'The RTCPeerConnection\'s signalingState is \'closed\'.',
+              'InvalidStateError');
+          }
+          var streams = [].slice.call(arguments, 1);
+          if (streams.length !== 1 ||
+              !streams[0].getTracks().find(function(t) {
+                return t === track;
+              })) {
+            // this is not fully correct but all we can manage without
+            // [[associated MediaStreams]] internal slot.
+            throw new DOMException(
+              'The adapter.js addTrack polyfill only supports a single ' +
+              ' stream which is associated with the specified track.',
+              'NotSupportedError');
+          }
+
+          pc._senders = pc._senders || [];
+          var alreadyExists = pc._senders.find(function(t) {
+            return t.track === track;
+          });
+          if (alreadyExists) {
+            throw new DOMException('Track already exists.',
+                'InvalidAccessError');
+          }
+
+          pc._streams = pc._streams || {};
+          var oldStream = pc._streams[stream.id];
+          if (oldStream) {
+            oldStream.addTrack(track);
+            pc.removeStream(oldStream);
+            pc.addStream(oldStream);
+          } else {
+            var newStream = new MediaStream([track]);
+            pc._streams[stream.id] = newStream;
+            pc.addStream(newStream);
+          }
+
+          var sender = {
+            track: track,
+            get dtmf() {
+              if (this._dtmf === undefined) {
+                if (track.kind === 'audio') {
+                  this._dtmf = pc.createDTMFSender(track);
+                } else {
+                  this._dtmf = null;
+                }
+              }
+              return this._dtmf;
+            }
+          };
+          pc._senders.push(sender);
+          return sender;
+        };
+      }
+      RTCPeerConnection.prototype.addStream = function(stream) {
+        var pc = this;
+        pc._senders = pc._senders || [];
+        origAddStream.apply(pc, [stream]);
+        stream.getTracks().forEach(function(track) {
+          pc._senders.push({
+            track: track,
+            get dtmf() {
+              if (this._dtmf === undefined) {
+                if (track.kind === 'audio') {
+                  this._dtmf = pc.createDTMFSender(track);
+                } else {
+                  this._dtmf = null;
+                }
+              }
+              return this._dtmf;
+            }
+          });
+        });
+      };
+
+      RTCPeerConnection.prototype.removeStream = function(stream) {
+        var pc = this;
+        pc._senders = pc._senders || [];
+        origRemoveStream.apply(pc, [stream]);
+        stream.getTracks().forEach(function(track) {
+          var sender = pc._senders.find(function(s) {
+            return s.track === track;
+          });
+          if (sender) {
+            pc._senders.splice(pc._senders.indexOf(sender), 1); // remove sender
+          }
+        });
+      };
+    }
+  },
+
+  shimSourceObject: function() {
+    if (typeof window === 'object') {
+      if (window.HTMLMediaElement &&
+        !('srcObject' in window.HTMLMediaElement.prototype)) {
+        // Shim the srcObject property, once, when HTMLMediaElement is found.
+        Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
+          get: function() {
+            return this._srcObject;
+          },
+          set: function(stream) {
+            var self = this;
+            // Use _srcObject as a private property for this shim
+            this._srcObject = stream;
+            if (this.src) {
+              URL.revokeObjectURL(this.src);
+            }
+
+            if (!stream) {
+              this.src = '';
+              return undefined;
+            }
+            this.src = URL.createObjectURL(stream);
+            // We need to recreate the blob url when a track is added or
+            // removed. Doing it manually since we want to avoid a recursion.
+            stream.addEventListener('addtrack', function() {
+              if (self.src) {
+                URL.revokeObjectURL(self.src);
+              }
+              self.src = URL.createObjectURL(stream);
+            });
+            stream.addEventListener('removetrack', function() {
+              if (self.src) {
+                URL.revokeObjectURL(self.src);
+              }
+              self.src = URL.createObjectURL(stream);
+            });
+          }
+        });
+      }
+    }
+  },
+
+  shimPeerConnection: function() {
+    // The RTCPeerConnection object.
+    if (!window.RTCPeerConnection) {
+      window.RTCPeerConnection = function(pcConfig, pcConstraints) {
+        // Translate iceTransportPolicy to iceTransports,
+        // see https://code.google.com/p/webrtc/issues/detail?id=4869
+        // this was fixed in M56 along with unprefixing RTCPeerConnection.
+        logging('PeerConnection');
+        if (pcConfig && pcConfig.iceTransportPolicy) {
+          pcConfig.iceTransports = pcConfig.iceTransportPolicy;
+        }
+
+        return new webkitRTCPeerConnection(pcConfig, pcConstraints);
+      };
+      window.RTCPeerConnection.prototype = webkitRTCPeerConnection.prototype;
+      // wrap static methods. Currently just generateCertificate.
+      if (webkitRTCPeerConnection.generateCertificate) {
+        Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
+          get: function() {
+            return webkitRTCPeerConnection.generateCertificate;
+          }
+        });
+      }
+    } else {
+      // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
+      var OrigPeerConnection = RTCPeerConnection;
+      window.RTCPeerConnection = function(pcConfig, pcConstraints) {
+        if (pcConfig && pcConfig.iceServers) {
+          var newIceServers = [];
+          for (var i = 0; i < pcConfig.iceServers.length; i++) {
+            var server = pcConfig.iceServers[i];
+            if (!server.hasOwnProperty('urls') &&
+                server.hasOwnProperty('url')) {
+              console.warn('RTCIceServer.url is deprecated! Use urls instead.');
+              server = JSON.parse(JSON.stringify(server));
+              server.urls = server.url;
+              newIceServers.push(server);
+            } else {
+              newIceServers.push(pcConfig.iceServers[i]);
+            }
+          }
+          pcConfig.iceServers = newIceServers;
+        }
+        return new OrigPeerConnection(pcConfig, pcConstraints);
+      };
+      window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
+      // wrap static methods. Currently just generateCertificate.
+      Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
+        get: function() {
+          return OrigPeerConnection.generateCertificate;
+        }
+      });
+    }
+
+    var origGetStats = RTCPeerConnection.prototype.getStats;
+    RTCPeerConnection.prototype.getStats = function(selector,
+        successCallback, errorCallback) {
+      var self = this;
+      var args = arguments;
+
+      // If selector is a function then we are in the old style stats so just
+      // pass back the original getStats format to avoid breaking old users.
+      if (arguments.length > 0 && typeof selector === 'function') {
+        return origGetStats.apply(this, arguments);
+      }
+
+      // When spec-style getStats is supported, return those when called with
+      // either no arguments or the selector argument is null.
+      if (origGetStats.length === 0 && (arguments.length === 0 ||
+          typeof arguments[0] !== 'function')) {
+        return origGetStats.apply(this, []);
+      }
+
+      var fixChromeStats_ = function(response) {
+        var standardReport = {};
+        var reports = response.result();
+        reports.forEach(function(report) {
+          var standardStats = {
+            id: report.id,
+            timestamp: report.timestamp,
+            type: {
+              localcandidate: 'local-candidate',
+              remotecandidate: 'remote-candidate'
+            }[report.type] || report.type
+          };
+          report.names().forEach(function(name) {
+            standardStats[name] = report.stat(name);
+          });
+          standardReport[standardStats.id] = standardStats;
+        });
+
+        return standardReport;
+      };
+
+      // shim getStats with maplike support
+      var makeMapStats = function(stats) {
+        return new Map(Object.keys(stats).map(function(key) {
+          return [key, stats[key]];
+        }));
+      };
+
+      if (arguments.length >= 2) {
+        var successCallbackWrapper_ = function(response) {
+          args[1](makeMapStats(fixChromeStats_(response)));
+        };
+
+        return origGetStats.apply(this, [successCallbackWrapper_,
+          arguments[0]]);
+      }
+
+      // promise-support
+      return new Promise(function(resolve, reject) {
+        origGetStats.apply(self, [
+          function(response) {
+            resolve(makeMapStats(fixChromeStats_(response)));
+          }, reject]);
+      }).then(successCallback, errorCallback);
+    };
+
+    // add promise support -- natively available in Chrome 51
+    if (browserDetails.version < 51) {
+      ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
+          .forEach(function(method) {
+            var nativeMethod = RTCPeerConnection.prototype[method];
+            RTCPeerConnection.prototype[method] = function() {
+              var args = arguments;
+              var self = this;
+              var promise = new Promise(function(resolve, reject) {
+                nativeMethod.apply(self, [args[0], resolve, reject]);
+              });
+              if (args.length < 2) {
+                return promise;
+              }
+              return promise.then(function() {
+                args[1].apply(null, []);
+              },
+              function(err) {
+                if (args.length >= 3) {
+                  args[2].apply(null, [err]);
+                }
+              });
+            };
+          });
+    }
+
+    // promise support for createOffer and createAnswer. Available (without
+    // bugs) since M52: crbug/619289
+    if (browserDetails.version < 52) {
+      ['createOffer', 'createAnswer'].forEach(function(method) {
+        var nativeMethod = RTCPeerConnection.prototype[method];
+        RTCPeerConnection.prototype[method] = function() {
+          var self = this;
+          if (arguments.length < 1 || (arguments.length === 1 &&
+              typeof arguments[0] === 'object')) {
+            var opts = arguments.length === 1 ? arguments[0] : undefined;
+            return new Promise(function(resolve, reject) {
+              nativeMethod.apply(self, [resolve, reject, opts]);
+            });
+          }
+          return nativeMethod.apply(this, arguments);
+        };
+      });
+    }
+
+    // shim implicit creation of RTCSessionDescription/RTCIceCandidate
+    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
+        .forEach(function(method) {
+          var nativeMethod = RTCPeerConnection.prototype[method];
+          RTCPeerConnection.prototype[method] = function() {
+            arguments[0] = new ((method === 'addIceCandidate') ?
+                RTCIceCandidate : RTCSessionDescription)(arguments[0]);
+            return nativeMethod.apply(this, arguments);
+          };
+        });
+
+    // support for addIceCandidate(null or undefined)
+    var nativeAddIceCandidate =
+        RTCPeerConnection.prototype.addIceCandidate;
+    RTCPeerConnection.prototype.addIceCandidate = function() {
+      if (!arguments[0]) {
+        if (arguments[1]) {
+          arguments[1].apply(null);
+        }
+        return Promise.resolve();
+      }
+      return nativeAddIceCandidate.apply(this, arguments);
+    };
+  }
+};
+
+
+// Expose public methods.
+module.exports = {
+  shimMediaStream: chromeShim.shimMediaStream,
+  shimOnTrack: chromeShim.shimOnTrack,
+  shimGetSendersWithDtmf: chromeShim.shimGetSendersWithDtmf,
+  shimSourceObject: chromeShim.shimSourceObject,
+  shimPeerConnection: chromeShim.shimPeerConnection,
+  shimGetUserMedia: __webpack_require__(10)
+};
+
+
+/***/ }),
+/* 10 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+ /* eslint-env node */
+
+var logging = __webpack_require__(0).log;
+var browserDetails = __webpack_require__(0).browserDetails;
+
+// Expose public methods.
+module.exports = function() {
+  var constraintsToChrome_ = function(c) {
+    if (typeof c !== 'object' || c.mandatory || c.optional) {
+      return c;
+    }
+    var cc = {};
+    Object.keys(c).forEach(function(key) {
+      if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
+        return;
+      }
+      var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
+      if (r.exact !== undefined && typeof r.exact === 'number') {
+        r.min = r.max = r.exact;
+      }
+      var oldname_ = function(prefix, name) {
+        if (prefix) {
+          return prefix + name.charAt(0).toUpperCase() + name.slice(1);
+        }
+        return (name === 'deviceId') ? 'sourceId' : name;
+      };
+      if (r.ideal !== undefined) {
+        cc.optional = cc.optional || [];
+        var oc = {};
+        if (typeof r.ideal === 'number') {
+          oc[oldname_('min', key)] = r.ideal;
+          cc.optional.push(oc);
+          oc = {};
+          oc[oldname_('max', key)] = r.ideal;
+          cc.optional.push(oc);
+        } else {
+          oc[oldname_('', key)] = r.ideal;
+          cc.optional.push(oc);
+        }
+      }
+      if (r.exact !== undefined && typeof r.exact !== 'number') {
+        cc.mandatory = cc.mandatory || {};
+        cc.mandatory[oldname_('', key)] = r.exact;
+      } else {
+        ['min', 'max'].forEach(function(mix) {
+          if (r[mix] !== undefined) {
+            cc.mandatory = cc.mandatory || {};
+            cc.mandatory[oldname_(mix, key)] = r[mix];
+          }
+        });
+      }
+    });
+    if (c.advanced) {
+      cc.optional = (cc.optional || []).concat(c.advanced);
+    }
+    return cc;
+  };
+
+  var shimConstraints_ = function(constraints, func) {
+    constraints = JSON.parse(JSON.stringify(constraints));
+    if (constraints && constraints.audio) {
+      constraints.audio = constraintsToChrome_(constraints.audio);
+    }
+    if (constraints && typeof constraints.video === 'object') {
+      // Shim facingMode for mobile & surface pro.
+      var face = constraints.video.facingMode;
+      face = face && ((typeof face === 'object') ? face : {ideal: face});
+      var getSupportedFacingModeLies = browserDetails.version < 61;
+
+      if ((face && (face.exact === 'user' || face.exact === 'environment' ||
+                    face.ideal === 'user' || face.ideal === 'environment')) &&
+          !(navigator.mediaDevices.getSupportedConstraints &&
+            navigator.mediaDevices.getSupportedConstraints().facingMode &&
+            !getSupportedFacingModeLies)) {
+        delete constraints.video.facingMode;
+        var matches;
+        if (face.exact === 'environment' || face.ideal === 'environment') {
+          matches = ['back', 'rear'];
+        } else if (face.exact === 'user' || face.ideal === 'user') {
+          matches = ['front'];
+        }
+        if (matches) {
+          // Look for matches in label, or use last cam for back (typical).
+          return navigator.mediaDevices.enumerateDevices()
+          .then(function(devices) {
+            devices = devices.filter(function(d) {
+              return d.kind === 'videoinput';
+            });
+            var dev = devices.find(function(d) {
+              return matches.some(function(match) {
+                return d.label.toLowerCase().indexOf(match) !== -1;
+              });
+            });
+            if (!dev && devices.length && matches.indexOf('back') !== -1) {
+              dev = devices[devices.length - 1]; // more likely the back cam
+            }
+            if (dev) {
+              constraints.video.deviceId = face.exact ? {exact: dev.deviceId} :
+                                                        {ideal: dev.deviceId};
+            }
+            constraints.video = constraintsToChrome_(constraints.video);
+            logging('chrome: ' + JSON.stringify(constraints));
+            return func(constraints);
+          });
+        }
+      }
+      constraints.video = constraintsToChrome_(constraints.video);
+    }
+    logging('chrome: ' + JSON.stringify(constraints));
+    return func(constraints);
+  };
+
+  var shimError_ = function(e) {
+    return {
+      name: {
+        ConstraintNotSatisfiedError: 'OverconstrainedError',
+        PermissionDeniedError: 'NotAllowedError',
+        TrackStartError: 'NotReadableError'
+      }[e.name] || e.name,
+      message: e.message,
+      constraint: e.constraintName,
+      toString: function() {
+        return this.name + (this.message && ': ') + this.message;
+      }
+    };
+  };
+
+  var getUserMedia_ = function(constraints, onSuccess, onError) {
+    shimConstraints_(constraints, function(c) {
+      navigator.webkitGetUserMedia(c, onSuccess, function(e) {
+        onError(shimError_(e));
+      });
+    });
+  };
+
+  navigator.getUserMedia = getUserMedia_;
+
+  // Returns the result of getUserMedia as a Promise.
+  var getUserMediaPromise_ = function(constraints) {
+    return new Promise(function(resolve, reject) {
+      navigator.getUserMedia(constraints, resolve, reject);
+    });
+  };
+
+  if (!navigator.mediaDevices) {
+    navigator.mediaDevices = {
+      getUserMedia: getUserMediaPromise_,
+      enumerateDevices: function() {
+        return new Promise(function(resolve) {
+          var kinds = {audio: 'audioinput', video: 'videoinput'};
+          return MediaStreamTrack.getSources(function(devices) {
+            resolve(devices.map(function(device) {
+              return {label: device.label,
+                kind: kinds[device.kind],
+                deviceId: device.id,
+                groupId: ''};
+            }));
+          });
+        });
+      },
+      getSupportedConstraints: function() {
+        return {
+          deviceId: true, echoCancellation: true, facingMode: true,
+          frameRate: true, height: true, width: true
+        };
+      }
+    };
+  }
+
+  // A shim for getUserMedia method on the mediaDevices object.
+  // TODO(KaptenJansson) remove once implemented in Chrome stable.
+  if (!navigator.mediaDevices.getUserMedia) {
+    navigator.mediaDevices.getUserMedia = function(constraints) {
+      return getUserMediaPromise_(constraints);
+    };
+  } else {
+    // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
+    // function which returns a Promise, it does not accept spec-style
+    // constraints.
+    var origGetUserMedia = navigator.mediaDevices.getUserMedia.
+        bind(navigator.mediaDevices);
+    navigator.mediaDevices.getUserMedia = function(cs) {
+      return shimConstraints_(cs, function(c) {
+        return origGetUserMedia(c).then(function(stream) {
+          if (c.audio && !stream.getAudioTracks().length ||
+              c.video && !stream.getVideoTracks().length) {
+            stream.getTracks().forEach(function(track) {
+              track.stop();
+            });
+            throw new DOMException('', 'NotFoundError');
+          }
+          return stream;
+        }, function(e) {
+          return Promise.reject(shimError_(e));
+        });
+      });
+    };
+  }
+
+  // Dummy devicechange event methods.
+  // TODO(KaptenJansson) remove once implemented in Chrome stable.
+  if (typeof navigator.mediaDevices.addEventListener === 'undefined') {
+    navigator.mediaDevices.addEventListener = function() {
+      logging('Dummy mediaDevices.addEventListener called.');
+    };
+  }
+  if (typeof navigator.mediaDevices.removeEventListener === 'undefined') {
+    navigator.mediaDevices.removeEventListener = function() {
+      logging('Dummy mediaDevices.removeEventListener called.');
+    };
+  }
+};
+
+
+/***/ }),
+/* 11 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+ /* eslint-env node */
+
+
+var browserDetails = __webpack_require__(0).browserDetails;
+var shimRTCPeerConnection = __webpack_require__(13);
+
+module.exports = {
+  shimGetUserMedia: __webpack_require__(12),
+  shimPeerConnection: function() {
+    if (window.RTCIceGatherer) {
+      // ORTC defines an RTCIceCandidate object but no constructor.
+      // Not implemented in Edge.
+      if (!window.RTCIceCandidate) {
+        window.RTCIceCandidate = function(args) {
+          return args;
+        };
+      }
+      // ORTC does not have a session description object but
+      // other browsers (i.e. Chrome) that will support both PC and ORTC
+      // in the future might have this defined already.
+      if (!window.RTCSessionDescription) {
+        window.RTCSessionDescription = function(args) {
+          return args;
+        };
+      }
+      // this adds an additional event listener to MediaStrackTrack that signals
+      // when a tracks enabled property was changed. Workaround for a bug in
+      // addStream, see below. No longer required in 15025+
+      if (browserDetails.version < 15025) {
+        var origMSTEnabled = Object.getOwnPropertyDescriptor(
+            MediaStreamTrack.prototype, 'enabled');
+        Object.defineProperty(MediaStreamTrack.prototype, 'enabled', {
+          set: function(value) {
+            origMSTEnabled.set.call(this, value);
+            var ev = new Event('enabled');
+            ev.enabled = value;
+            this.dispatchEvent(ev);
+          }
+        });
+      }
+    }
+    window.RTCPeerConnection = shimRTCPeerConnection(browserDetails.version);
+  },
+  shimReplaceTrack: function() {
+    // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614
+    if (window.RTCRtpSender && !('replaceTrack' in RTCRtpSender.prototype)) {
+      RTCRtpSender.prototype.replaceTrack = RTCRtpSender.prototype.setTrack;
+    }
+  }
+};
+
+
+/***/ }),
+/* 12 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+ /* eslint-env node */
+
+
+// Expose public methods.
+module.exports = function() {
+  var shimError_ = function(e) {
+    return {
+      name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name,
+      message: e.message,
+      constraint: e.constraint,
+      toString: function() {
+        return this.name;
+      }
+    };
+  };
+
+  // getUserMedia error shim.
+  var origGetUserMedia = navigator.mediaDevices.getUserMedia.
+      bind(navigator.mediaDevices);
+  navigator.mediaDevices.getUserMedia = function(c) {
+    return origGetUserMedia(c).catch(function(e) {
+      return Promise.reject(shimError_(e));
+    });
+  };
+};
+
+
+/***/ }),
+/* 13 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+ /* eslint-env node */
+
+
+var SDPUtils = __webpack_require__(7);
+
+// sort tracks such that they follow an a-v-a-v...
+// pattern.
+function sortTracks(tracks) {
+  var audioTracks = tracks.filter(function(track) {
+    return track.kind === 'audio';
+  });
+  var videoTracks = tracks.filter(function(track) {
+    return track.kind === 'video';
+  });
+  tracks = [];
+  while (audioTracks.length || videoTracks.length) {
+    if (audioTracks.length) {
+      tracks.push(audioTracks.shift());
+    }
+    if (videoTracks.length) {
+      tracks.push(videoTracks.shift());
+    }
+  }
+  return tracks;
+}
+
+// Edge does not like
+// 1) stun:
+// 2) turn: that does not have all of turn:host:port?transport=udp
+// 3) turn: with ipv6 addresses
+// 4) turn: occurring muliple times
+function filterIceServers(iceServers, edgeVersion) {
+  var hasTurn = false;
+  iceServers = JSON.parse(JSON.stringify(iceServers));
+  return iceServers.filter(function(server) {
+    if (server && (server.urls || server.url)) {
+      var urls = server.urls || server.url;
+      if (server.url && !server.urls) {
+        console.warn('RTCIceServer.url is deprecated! Use urls instead.');
+      }
+      var isString = typeof urls === 'string';
+      if (isString) {
+        urls = [urls];
+      }
+      urls = urls.filter(function(url) {
+        var validTurn = url.indexOf('turn:') === 0 &&
+            url.indexOf('transport=udp') !== -1 &&
+            url.indexOf('turn:[') === -1 &&
+            !hasTurn;
+
+        if (validTurn) {
+          hasTurn = true;
+          return true;
+        }
+        return url.indexOf('stun:') === 0 && edgeVersion >= 14393;
+      });
+
+      delete server.url;
+      server.urls = isString ? urls[0] : urls;
+      return !!urls.length;
+    }
+    return false;
+  });
+}
+
+// Determines the intersection of local and remote capabilities.
+function getCommonCapabilities(localCapabilities, remoteCapabilities) {
+  var commonCapabilities = {
+    codecs: [],
+    headerExtensions: [],
+    fecMechanisms: []
+  };
+
+  var findCodecByPayloadType = function(pt, codecs) {
+    pt = parseInt(pt, 10);
+    for (var i = 0; i < codecs.length; i++) {
+      if (codecs[i].payloadType === pt ||
+          codecs[i].preferredPayloadType === pt) {
+        return codecs[i];
+      }
+    }
+  };
+
+  var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) {
+    var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);
+    var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);
+    return lCodec && rCodec &&
+        lCodec.name.toLowerCase() === rCodec.name.toLowerCase();
+  };
+
+  localCapabilities.codecs.forEach(function(lCodec) {
+    for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
+      var rCodec = remoteCapabilities.codecs[i];
+      if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
+          lCodec.clockRate === rCodec.clockRate) {
+        if (lCodec.name.toLowerCase() === 'rtx' &&
+            lCodec.parameters && rCodec.parameters.apt) {
+          // for RTX we need to find the local rtx that has a apt
+          // which points to the same local codec as the remote one.
+          if (!rtxCapabilityMatches(lCodec, rCodec,
+              localCapabilities.codecs, remoteCapabilities.codecs)) {
+            continue;
+          }
+        }
+        rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy
+        // number of channels is the highest common number of channels
+        rCodec.numChannels = Math.min(lCodec.numChannels,
+            rCodec.numChannels);
+        // push rCodec so we reply with offerer payload type
+        commonCapabilities.codecs.push(rCodec);
+
+        // determine common feedback mechanisms
+        rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {
+          for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {
+            if (lCodec.rtcpFeedback[j].type === fb.type &&
+                lCodec.rtcpFeedback[j].parameter === fb.parameter) {
+              return true;
+            }
+          }
+          return false;
+        });
+        // FIXME: also need to determine .parameters
+        //  see https://github.com/openpeer/ortc/issues/569
+        break;
+      }
+    }
+  });
+
+  localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
+    for (var i = 0; i < remoteCapabilities.headerExtensions.length;
+         i++) {
+      var rHeaderExtension = remoteCapabilities.headerExtensions[i];
+      if (lHeaderExtension.uri === rHeaderExtension.uri) {
+        commonCapabilities.headerExtensions.push(rHeaderExtension);
+        break;
+      }
+    }
+  });
+
+  // FIXME: fecMechanisms
+  return commonCapabilities;
+}
+
+// is action=setLocalDescription with type allowed in signalingState
+function isActionAllowedInSignalingState(action, type, signalingState) {
+  return {
+    offer: {
+      setLocalDescription: ['stable', 'have-local-offer'],
+      setRemoteDescription: ['stable', 'have-remote-offer']
+    },
+    answer: {
+      setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],
+      setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']
+    }
+  }[type][action].indexOf(signalingState) !== -1;
+}
+
+module.exports = function(edgeVersion) {
+  var RTCPeerConnection = function(config) {
+    var self = this;
+
+    var _eventTarget = document.createDocumentFragment();
+    ['addEventListener', 'removeEventListener', 'dispatchEvent']
+        .forEach(function(method) {
+          self[method] = _eventTarget[method].bind(_eventTarget);
+        });
+
+    this.needNegotiation = false;
+
+    this.onicecandidate = null;
+    this.onaddstream = null;
+    this.ontrack = null;
+    this.onremovestream = null;
+    this.onsignalingstatechange = null;
+    this.oniceconnectionstatechange = null;
+    this.onicegatheringstatechange = null;
+    this.onnegotiationneeded = null;
+    this.ondatachannel = null;
+    this.canTrickleIceCandidates = null;
+
+    this.localStreams = [];
+    this.remoteStreams = [];
+    this.getLocalStreams = function() {
+      return self.localStreams;
+    };
+    this.getRemoteStreams = function() {
+      return self.remoteStreams;
+    };
+
+    this.localDescription = new RTCSessionDescription({
+      type: '',
+      sdp: ''
+    });
+    this.remoteDescription = new RTCSessionDescription({
+      type: '',
+      sdp: ''
+    });
+    this.signalingState = 'stable';
+    this.iceConnectionState = 'new';
+    this.iceGatheringState = 'new';
+
+    this.iceOptions = {
+      gatherPolicy: 'all',
+      iceServers: []
+    };
+    if (config && config.iceTransportPolicy) {
+      switch (config.iceTransportPolicy) {
+        case 'all':
+        case 'relay':
+          this.iceOptions.gatherPolicy = config.iceTransportPolicy;
+          break;
+        default:
+          // don't set iceTransportPolicy.
+          break;
+      }
+    }
+    this.usingBundle = config && config.bundlePolicy === 'max-bundle';
+
+    if (config && config.iceServers) {
+      this.iceOptions.iceServers = filterIceServers(config.iceServers,
+          edgeVersion);
+    }
+    this._config = config || {};
+
+    // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
+    // everything that is needed to describe a SDP m-line.
+    this.transceivers = [];
+
+    // since the iceGatherer is currently created in createOffer but we
+    // must not emit candidates until after setLocalDescription we buffer
+    // them in this array.
+    this._localIceCandidatesBuffer = [];
+  };
+
+  RTCPeerConnection.prototype._emitGatheringStateChange = function() {
+    var event = new Event('icegatheringstatechange');
+    this.dispatchEvent(event);
+    if (this.onicegatheringstatechange !== null) {
+      this.onicegatheringstatechange(event);
+    }
+  };
+
+  RTCPeerConnection.prototype._emitBufferedCandidates = function() {
+    var self = this;
+    var sections = SDPUtils.splitSections(self.localDescription.sdp);
+    // FIXME: need to apply ice candidates in a way which is async but
+    // in-order
+    this._localIceCandidatesBuffer.forEach(function(event) {
+      var end = !event.candidate || Object.keys(event.candidate).length === 0;
+      if (end) {
+        for (var j = 1; j < sections.length; j++) {
+          if (sections[j].indexOf('\r\na=end-of-candidates\r\n') === -1) {
+            sections[j] += 'a=end-of-candidates\r\n';
+          }
+        }
+      } else {
+        sections[event.candidate.sdpMLineIndex + 1] +=
+            'a=' + event.candidate.candidate + '\r\n';
+      }
+      self.localDescription.sdp = sections.join('');
+      self.dispatchEvent(event);
+      if (self.onicecandidate !== null) {
+        self.onicecandidate(event);
+      }
+      if (!event.candidate && self.iceGatheringState !== 'complete') {
+        var complete = self.transceivers.every(function(transceiver) {
+          return transceiver.iceGatherer &&
+              transceiver.iceGatherer.state === 'completed';
+        });
+        if (complete && self.iceGatheringStateChange !== 'complete') {
+          self.iceGatheringState = 'complete';
+          self._emitGatheringStateChange();
+        }
+      }
+    });
+    this._localIceCandidatesBuffer = [];
+  };
+
+  RTCPeerConnection.prototype.getConfiguration = function() {
+    return this._config;
+  };
+
+  // internal helper to create a transceiver object.
+  // (whih is not yet the same as the WebRTC 1.0 transceiver)
+  RTCPeerConnection.prototype._createTransceiver = function(kind) {
+    var hasBundleTransport = this.transceivers.length > 0;
+    var transceiver = {
+      track: null,
+      iceGatherer: null,
+      iceTransport: null,
+      dtlsTransport: null,
+      localCapabilities: null,
+      remoteCapabilities: null,
+      rtpSender: null,
+      rtpReceiver: null,
+      kind: kind,
+      mid: null,
+      sendEncodingParameters: null,
+      recvEncodingParameters: null,
+      stream: null,
+      wantReceive: true
+    };
+    if (this.usingBundle && hasBundleTransport) {
+      transceiver.iceTransport = this.transceivers[0].iceTransport;
+      transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;
+    } else {
+      var transports = this._createIceAndDtlsTransports();
+      transceiver.iceTransport = transports.iceTransport;
+      transceiver.dtlsTransport = transports.dtlsTransport;
+    }
+    this.transceivers.push(transceiver);
+    return transceiver;
+  };
+
+  RTCPeerConnection.prototype.addTrack = function(track, stream) {
+    var transceiver;
+    for (var i = 0; i < this.transceivers.length; i++) {
+      if (!this.transceivers[i].track &&
+          this.transceivers[i].kind === track.kind) {
+        transceiver = this.transceivers[i];
+      }
+    }
+    if (!transceiver) {
+      transceiver = this._createTransceiver(track.kind);
+    }
+
+    transceiver.track = track;
+    transceiver.stream = stream;
+    transceiver.rtpSender = new RTCRtpSender(track,
+        transceiver.dtlsTransport);
+
+    this._maybeFireNegotiationNeeded();
+    return transceiver.rtpSender;
+  };
+
+  RTCPeerConnection.prototype.addStream = function(stream) {
+    var self = this;
+    if (edgeVersion >= 15025) {
+      this.localStreams.push(stream);
+      stream.getTracks().forEach(function(track) {
+        self.addTrack(track, stream);
+      });
+    } else {
+      // Clone is necessary for local demos mostly, attaching directly
+      // to two different senders does not work (build 10547).
+      // Fixed in 15025 (or earlier)
+      var clonedStream = stream.clone();
+      stream.getTracks().forEach(function(track, idx) {
+        var clonedTrack = clonedStream.getTracks()[idx];
+        track.addEventListener('enabled', function(event) {
+          clonedTrack.enabled = event.enabled;
+        });
+      });
+      clonedStream.getTracks().forEach(function(track) {
+        self.addTrack(track, clonedStream);
+      });
+      this.localStreams.push(clonedStream);
+    }
+    this._maybeFireNegotiationNeeded();
+  };
+
+  RTCPeerConnection.prototype.removeStream = function(stream) {
+    var idx = this.localStreams.indexOf(stream);
+    if (idx > -1) {
+      this.localStreams.splice(idx, 1);
+      this._maybeFireNegotiationNeeded();
+    }
+  };
+
+  RTCPeerConnection.prototype.getSenders = function() {
+    return this.transceivers.filter(function(transceiver) {
+      return !!transceiver.rtpSender;
+    })
+    .map(function(transceiver) {
+      return transceiver.rtpSender;
+    });
+  };
+
+  RTCPeerConnection.prototype.getReceivers = function() {
+    return this.transceivers.filter(function(transceiver) {
+      return !!transceiver.rtpReceiver;
+    })
+    .map(function(transceiver) {
+      return transceiver.rtpReceiver;
+    });
+  };
+
+  // Create ICE gatherer and hook it up.
+  RTCPeerConnection.prototype._createIceGatherer = function(mid,
+      sdpMLineIndex) {
+    var self = this;
+    var iceGatherer = new RTCIceGatherer(self.iceOptions);
+    iceGatherer.onlocalcandidate = function(evt) {
+      var event = new Event('icecandidate');
+      event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
+
+      var cand = evt.candidate;
+      var end = !cand || Object.keys(cand).length === 0;
+      // Edge emits an empty object for RTCIceCandidateComplete‥
+      if (end) {
+        // polyfill since RTCIceGatherer.state is not implemented in
+        // Edge 10547 yet.
+        if (iceGatherer.state === undefined) {
+          iceGatherer.state = 'completed';
+        }
+      } else {
+        // RTCIceCandidate doesn't have a component, needs to be added
+        cand.component = 1;
+        event.candidate.candidate = SDPUtils.writeCandidate(cand);
+      }
+
+      // update local description.
+      var sections = SDPUtils.splitSections(self.localDescription.sdp);
+      if (!end) {
+        sections[event.candidate.sdpMLineIndex + 1] +=
+            'a=' + event.candidate.candidate + '\r\n';
+      } else {
+        sections[event.candidate.sdpMLineIndex + 1] +=
+            'a=end-of-candidates\r\n';
+      }
+      self.localDescription.sdp = sections.join('');
+      var transceivers = self._pendingOffer ? self._pendingOffer :
+          self.transceivers;
+      var complete = transceivers.every(function(transceiver) {
+        return transceiver.iceGatherer &&
+            transceiver.iceGatherer.state === 'completed';
+      });
+
+      // Emit candidate if localDescription is set.
+      // Also emits null candidate when all gatherers are complete.
+      switch (self.iceGatheringState) {
+        case 'new':
+          if (!end) {
+            self._localIceCandidatesBuffer.push(event);
+          }
+          if (end && complete) {
+            self._localIceCandidatesBuffer.push(
+                new Event('icecandidate'));
+          }
+          break;
+        case 'gathering':
+          self._emitBufferedCandidates();
+          if (!end) {
+            self.dispatchEvent(event);
+            if (self.onicecandidate !== null) {
+              self.onicecandidate(event);
+            }
+          }
+          if (complete) {
+            self.dispatchEvent(new Event('icecandidate'));
+            if (self.onicecandidate !== null) {
+              self.onicecandidate(new Event('icecandidate'));
+            }
+            self.iceGatheringState = 'complete';
+            self._emitGatheringStateChange();
+          }
+          break;
+        case 'complete':
+          // should not happen... currently!
+          break;
+        default: // no-op.
+          break;
+      }
+    };
+    return iceGatherer;
+  };
+
+  // Create ICE transport and DTLS transport.
+  RTCPeerConnection.prototype._createIceAndDtlsTransports = function() {
+    var self = this;
+    var iceTransport = new RTCIceTransport(null);
+    iceTransport.onicestatechange = function() {
+      self._updateConnectionState();
+    };
+
+    var dtlsTransport = new RTCDtlsTransport(iceTransport);
+    dtlsTransport.ondtlsstatechange = function() {
+      self._updateConnectionState();
+    };
+    dtlsTransport.onerror = function() {
+      // onerror does not set state to failed by itself.
+      Object.defineProperty(dtlsTransport, 'state',
+          {value: 'failed', writable: true});
+      self._updateConnectionState();
+    };
+
+    return {
+      iceTransport: iceTransport,
+      dtlsTransport: dtlsTransport
+    };
+  };
+
+  // Destroy ICE gatherer, ICE transport and DTLS transport.
+  // Without triggering the callbacks.
+  RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function(
+      sdpMLineIndex) {
+    var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
+    if (iceGatherer) {
+      delete iceGatherer.onlocalcandidate;
+      delete this.transceivers[sdpMLineIndex].iceGatherer;
+    }
+    var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;
+    if (iceTransport) {
+      delete iceTransport.onicestatechange;
+      delete this.transceivers[sdpMLineIndex].iceTransport;
+    }
+    var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;
+    if (dtlsTransport) {
+      delete dtlsTransport.ondtlssttatechange;
+      delete dtlsTransport.onerror;
+      delete this.transceivers[sdpMLineIndex].dtlsTransport;
+    }
+  };
+
+  // Start the RTP Sender and Receiver for a transceiver.
+  RTCPeerConnection.prototype._transceive = function(transceiver,
+      send, recv) {
+    var params = getCommonCapabilities(transceiver.localCapabilities,
+        transceiver.remoteCapabilities);
+    if (send && transceiver.rtpSender) {
+      params.encodings = transceiver.sendEncodingParameters;
+      params.rtcp = {
+        cname: SDPUtils.localCName,
+        compound: transceiver.rtcpParameters.compound
+      };
+      if (transceiver.recvEncodingParameters.length) {
+        params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
+      }
+      transceiver.rtpSender.send(params);
+    }
+    if (recv && transceiver.rtpReceiver) {
+      // remove RTX field in Edge 14942
+      if (transceiver.kind === 'video'
+          && transceiver.recvEncodingParameters
+          && edgeVersion < 15019) {
+        transceiver.recvEncodingParameters.forEach(function(p) {
+          delete p.rtx;
+        });
+      }
+      params.encodings = transceiver.recvEncodingParameters;
+      params.rtcp = {
+        cname: transceiver.rtcpParameters.cname,
+        compound: transceiver.rtcpParameters.compound
+      };
+      if (transceiver.sendEncodingParameters.length) {
+        params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
+      }
+      transceiver.rtpReceiver.receive(params);
+    }
+  };
+
+  RTCPeerConnection.prototype.setLocalDescription = function(description) {
+    var self = this;
+
+    if (!isActionAllowedInSignalingState('setLocalDescription',
+        description.type, this.signalingState)) {
+      var e = new Error('Can not set local ' + description.type +
+          ' in state ' + this.signalingState);
+      e.name = 'InvalidStateError';
+      if (arguments.length > 2 && typeof arguments[2] === 'function') {
+        window.setTimeout(arguments[2], 0, e);
+      }
+      return Promise.reject(e);
+    }
+
+    var sections;
+    var sessionpart;
+    if (description.type === 'offer') {
+      // FIXME: What was the purpose of this empty if statement?
+      // if (!this._pendingOffer) {
+      // } else {
+      if (this._pendingOffer) {
+        // VERY limited support for SDP munging. Limited to:
+        // * changing the order of codecs
+        sections = SDPUtils.splitSections(description.sdp);
+        sessionpart = sections.shift();
+        sections.forEach(function(mediaSection, sdpMLineIndex) {
+          var caps = SDPUtils.parseRtpParameters(mediaSection);
+          self._pendingOffer[sdpMLineIndex].localCapabilities = caps;
+        });
+        this.transceivers = this._pendingOffer;
+        delete this._pendingOffer;
+      }
+    } else if (description.type === 'answer') {
+      sections = SDPUtils.splitSections(self.remoteDescription.sdp);
+      sessionpart = sections.shift();
+      var isIceLite = SDPUtils.matchPrefix(sessionpart,
+          'a=ice-lite').length > 0;
+      sections.forEach(function(mediaSection, sdpMLineIndex) {
+        var transceiver = self.transceivers[sdpMLineIndex];
+        var iceGatherer = transceiver.iceGatherer;
+        var iceTransport = transceiver.iceTransport;
+        var dtlsTransport = transceiver.dtlsTransport;
+        var localCapabilities = transceiver.localCapabilities;
+        var remoteCapabilities = transceiver.remoteCapabilities;
+
+        var rejected = SDPUtils.isRejected(mediaSection);
+
+        if (!rejected && !transceiver.isDatachannel) {
+          var remoteIceParameters = SDPUtils.getIceParameters(
+              mediaSection, sessionpart);
+          var remoteDtlsParameters = SDPUtils.getDtlsParameters(
+              mediaSection, sessionpart);
+          if (isIceLite) {
+            remoteDtlsParameters.role = 'server';
+          }
+
+          if (!self.usingBundle || sdpMLineIndex === 0) {
+            iceTransport.start(iceGatherer, remoteIceParameters,
+                isIceLite ? 'controlling' : 'controlled');
+            dtlsTransport.start(remoteDtlsParameters);
+          }
+
+          // Calculate intersection of capabilities.
+          var params = getCommonCapabilities(localCapabilities,
+              remoteCapabilities);
+
+          // Start the RTCRtpSender. The RTCRtpReceiver for this
+          // transceiver has already been started in setRemoteDescription.
+          self._transceive(transceiver,
+              params.codecs.length > 0,
+              false);
+        }
+      });
+    }
+
+    this.localDescription = {
+      type: description.type,
+      sdp: description.sdp
+    };
+    switch (description.type) {
+      case 'offer':
+        this._updateSignalingState('have-local-offer');
+        break;
+      case 'answer':
+        this._updateSignalingState('stable');
+        break;
+      default:
+        throw new TypeError('unsupported type "' + description.type +
+            '"');
+    }
+
+    // If a success callback was provided, emit ICE candidates after it
+    // has been executed. Otherwise, emit callback after the Promise is
+    // resolved.
+    var hasCallback = arguments.length > 1 &&
+      typeof arguments[1] === 'function';
+    if (hasCallback) {
+      var cb = arguments[1];
+      window.setTimeout(function() {
+        cb();
+        if (self.iceGatheringState === 'new') {
+          self.iceGatheringState = 'gathering';
+          self._emitGatheringStateChange();
+        }
+        self._emitBufferedCandidates();
+      }, 0);
+    }
+    var p = Promise.resolve();
+    p.then(function() {
+      if (!hasCallback) {
+        if (self.iceGatheringState === 'new') {
+          self.iceGatheringState = 'gathering';
+          self._emitGatheringStateChange();
+        }
+        // Usually candidates will be emitted earlier.
+        window.setTimeout(self._emitBufferedCandidates.bind(self), 500);
+      }
+    });
+    return p;
+  };
+
+  RTCPeerConnection.prototype.setRemoteDescription = function(description) {
+    var self = this;
+
+    if (!isActionAllowedInSignalingState('setRemoteDescription',
+        description.type, this.signalingState)) {
+      var e = new Error('Can not set remote ' + description.type +
+          ' in state ' + this.signalingState);
+      e.name = 'InvalidStateError';
+      if (arguments.length > 2 && typeof arguments[2] === 'function') {
+        window.setTimeout(arguments[2], 0, e);
+      }
+      return Promise.reject(e);
+    }
+
+    var streams = {};
+    var receiverList = [];
+    var sections = SDPUtils.splitSections(description.sdp);
+    var sessionpart = sections.shift();
+    var isIceLite = SDPUtils.matchPrefix(sessionpart,
+        'a=ice-lite').length > 0;
+    var usingBundle = SDPUtils.matchPrefix(sessionpart,
+        'a=group:BUNDLE ').length > 0;
+    this.usingBundle = usingBundle;
+    var iceOptions = SDPUtils.matchPrefix(sessionpart,
+        'a=ice-options:')[0];
+    if (iceOptions) {
+      this.canTrickleIceCandidates = iceOptions.substr(14).split(' ')
+          .indexOf('trickle') >= 0;
+    } else {
+      this.canTrickleIceCandidates = false;
+    }
+
+    sections.forEach(function(mediaSection, sdpMLineIndex) {
+      var lines = SDPUtils.splitLines(mediaSection);
+      var kind = SDPUtils.getKind(mediaSection);
+      var rejected = SDPUtils.isRejected(mediaSection);
+      var protocol = lines[0].substr(2).split(' ')[2];
+
+      var direction = SDPUtils.getDirection(mediaSection, sessionpart);
+      var remoteMsid = SDPUtils.parseMsid(mediaSection);
+
+      var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier();
+
+      // Reject datachannels which are not implemented yet.
+      if (kind === 'application' && protocol === 'DTLS/SCTP') {
+        self.transceivers[sdpMLineIndex] = {
+          mid: mid,
+          isDatachannel: true
+        };
+        return;
+      }
+
+      var transceiver;
+      var iceGatherer;
+      var iceTransport;
+      var dtlsTransport;
+      var rtpReceiver;
+      var sendEncodingParameters;
+      var recvEncodingParameters;
+      var localCapabilities;
+
+      var track;
+      // FIXME: ensure the mediaSection has rtcp-mux set.
+      var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
+      var remoteIceParameters;
+      var remoteDtlsParameters;
+      if (!rejected) {
+        remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
+            sessionpart);
+        remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
+            sessionpart);
+        remoteDtlsParameters.role = 'client';
+      }
+      recvEncodingParameters =
+          SDPUtils.parseRtpEncodingParameters(mediaSection);
+
+      var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection);
+
+      var isComplete = SDPUtils.matchPrefix(mediaSection,
+          'a=end-of-candidates', sessionpart).length > 0;
+      var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')
+          .map(function(cand) {
+            return SDPUtils.parseCandidate(cand);
+          })
+          .filter(function(cand) {
+            return cand.component === '1' || cand.component === 1;
+          });
+      if (description.type === 'offer' && !rejected) {
+        transceiver = self.transceivers[sdpMLineIndex] ||
+            self._createTransceiver(kind);
+        transceiver.mid = mid;
+
+        if (!transceiver.iceGatherer) {
+          transceiver.iceGatherer = usingBundle && sdpMLineIndex > 0 ?
+              self.transceivers[0].iceGatherer :
+              self._createIceGatherer(mid, sdpMLineIndex);
+        }
+
+        if (isComplete && (!usingBundle || sdpMLineIndex === 0)) {
+          transceiver.iceTransport.setRemoteCandidates(cands);
+        }
+
+        localCapabilities = RTCRtpReceiver.getCapabilities(kind);
+
+        // filter RTX until additional stuff needed for RTX is implemented
+        // in adapter.js
+        if (edgeVersion < 15019) {
+          localCapabilities.codecs = localCapabilities.codecs.filter(
+              function(codec) {
+                return codec.name !== 'rtx';
+              });
+        }
+
+        sendEncodingParameters = [{
+          ssrc: (2 * sdpMLineIndex + 2) * 1001
+        }];
+
+        if (direction === 'sendrecv' || direction === 'sendonly') {
+          rtpReceiver = new RTCRtpReceiver(transceiver.dtlsTransport,
+              kind);
+
+          track = rtpReceiver.track;
+          // FIXME: does not work with Plan B.
+          if (remoteMsid) {
+            if (!streams[remoteMsid.stream]) {
+              streams[remoteMsid.stream] = new MediaStream();
+              Object.defineProperty(streams[remoteMsid.stream], 'id', {
+                get: function() {
+                  return remoteMsid.stream;
+                }
+              });
+            }
+            Object.defineProperty(track, 'id', {
+              get: function() {
+                return remoteMsid.track;
+              }
+            });
+            streams[remoteMsid.stream].addTrack(track);
+            receiverList.push([track, rtpReceiver,
+              streams[remoteMsid.stream]]);
+          } else {
+            if (!streams.default) {
+              streams.default = new MediaStream();
+            }
+            streams.default.addTrack(track);
+            receiverList.push([track, rtpReceiver, streams.default]);
+          }
+        }
+
+        transceiver.localCapabilities = localCapabilities;
+        transceiver.remoteCapabilities = remoteCapabilities;
+        transceiver.rtpReceiver = rtpReceiver;
+        transceiver.rtcpParameters = rtcpParameters;
+        transceiver.sendEncodingParameters = sendEncodingParameters;
+        transceiver.recvEncodingParameters = recvEncodingParameters;
+
+        // Start the RTCRtpReceiver now. The RTPSender is started in
+        // setLocalDescription.
+        self._transceive(self.transceivers[sdpMLineIndex],
+            false,
+            direction === 'sendrecv' || direction === 'sendonly');
+      } else if (description.type === 'answer' && !rejected) {
+        if (usingBundle && sdpMLineIndex > 0) {
+          self._disposeIceAndDtlsTransports(sdpMLineIndex);
+          self.transceivers[sdpMLineIndex].iceGatherer =
+              self.transceivers[0].iceGatherer;
+          self.transceivers[sdpMLineIndex].iceTransport =
+              self.transceivers[0].iceTransport;
+          self.transceivers[sdpMLineIndex].dtlsTransport =
+              self.transceivers[0].dtlsTransport;
+          if (self.transceivers[sdpMLineIndex].rtpSender) {
+            self.transceivers[sdpMLineIndex].rtpSender.setTransport(
+                self.transceivers[0].dtlsTransport);
+          }
+          if (self.transceivers[sdpMLineIndex].rtpReceiver) {
+            self.transceivers[sdpMLineIndex].rtpReceiver.setTransport(
+                self.transceivers[0].dtlsTransport);
+          }
+        }
+        transceiver = self.transceivers[sdpMLineIndex];
+        iceGatherer = transceiver.iceGatherer;
+        iceTransport = transceiver.iceTransport;
+        dtlsTransport = transceiver.dtlsTransport;
+        rtpReceiver = transceiver.rtpReceiver;
+        sendEncodingParameters = transceiver.sendEncodingParameters;
+        localCapabilities = transceiver.localCapabilities;
+
+        self.transceivers[sdpMLineIndex].recvEncodingParameters =
+            recvEncodingParameters;
+        self.transceivers[sdpMLineIndex].remoteCapabilities =
+            remoteCapabilities;
+        self.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;
+
+        if ((isIceLite || isComplete) && cands.length) {
+          iceTransport.setRemoteCandidates(cands);
+        }
+        if (!usingBundle || sdpMLineIndex === 0) {
+          iceTransport.start(iceGatherer, remoteIceParameters,
+              'controlling');
+          dtlsTransport.start(remoteDtlsParameters);
+        }
+
+        self._transceive(transceiver,
+            direction === 'sendrecv' || direction === 'recvonly',
+            direction === 'sendrecv' || direction === 'sendonly');
+
+        if (rtpReceiver &&
+            (direction === 'sendrecv' || direction === 'sendonly')) {
+          track = rtpReceiver.track;
+          if (remoteMsid) {
+            if (!streams[remoteMsid.stream]) {
+              streams[remoteMsid.stream] = new MediaStream();
+            }
+            streams[remoteMsid.stream].addTrack(track);
+            receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);
+          } else {
+            if (!streams.default) {
+              streams.default = new MediaStream();
+            }
+            streams.default.addTrack(track);
+            receiverList.push([track, rtpReceiver, streams.default]);
+          }
+        } else {
+          // FIXME: actually the receiver should be created later.
+          delete transceiver.rtpReceiver;
+        }
+      }
+    });
+
+    this.remoteDescription = {
+      type: description.type,
+      sdp: description.sdp
+    };
+    switch (description.type) {
+      case 'offer':
+        this._updateSignalingState('have-remote-offer');
+        break;
+      case 'answer':
+        this._updateSignalingState('stable');
+        break;
+      default:
+        throw new TypeError('unsupported type "' + description.type +
+            '"');
+    }
+    Object.keys(streams).forEach(function(sid) {
+      var stream = streams[sid];
+      if (stream.getTracks().length) {
+        self.remoteStreams.push(stream);
+        var event = new Event('addstream');
+        event.stream = stream;
+        self.dispatchEvent(event);
+        if (self.onaddstream !== null) {
+          window.setTimeout(function() {
+            self.onaddstream(event);
+          }, 0);
+        }
+
+        receiverList.forEach(function(item) {
+          var track = item[0];
+          var receiver = item[1];
+          if (stream.id !== item[2].id) {
+            return;
+          }
+          var trackEvent = new Event('track');
+          trackEvent.track = track;
+          trackEvent.receiver = receiver;
+          trackEvent.streams = [stream];
+          self.dispatchEvent(trackEvent);
+          if (self.ontrack !== null) {
+            window.setTimeout(function() {
+              self.ontrack(trackEvent);
+            }, 0);
+          }
+        });
+      }
+    });
+
+    // check whether addIceCandidate({}) was called within four seconds after
+    // setRemoteDescription.
+    window.setTimeout(function() {
+      if (!(self && self.transceivers)) {
+        return;
+      }
+      self.transceivers.forEach(function(transceiver) {
+        if (transceiver.iceTransport &&
+            transceiver.iceTransport.state === 'new' &&
+            transceiver.iceTransport.getRemoteCandidates().length > 0) {
+          console.warn('Timeout for addRemoteCandidate. Consider sending ' +
+              'an end-of-candidates notification');
+          transceiver.iceTransport.addRemoteCandidate({});
+        }
+      });
+    }, 4000);
+
+    if (arguments.length > 1 && typeof arguments[1] === 'function') {
+      window.setTimeout(arguments[1], 0);
+    }
+    return Promise.resolve();
+  };
+
+  RTCPeerConnection.prototype.close = function() {
+    this.transceivers.forEach(function(transceiver) {
+      /* not yet
+      if (transceiver.iceGatherer) {
+        transceiver.iceGatherer.close();
+      }
+      */
+      if (transceiver.iceTransport) {
+        transceiver.iceTransport.stop();
+      }
+      if (transceiver.dtlsTransport) {
+        transceiver.dtlsTransport.stop();
+      }
+      if (transceiver.rtpSender) {
+        transceiver.rtpSender.stop();
+      }
+      if (transceiver.rtpReceiver) {
+        transceiver.rtpReceiver.stop();
+      }
+    });
+    // FIXME: clean up tracks, local streams, remote streams, etc
+    this._updateSignalingState('closed');
+  };
+
+  // Update the signaling state.
+  RTCPeerConnection.prototype._updateSignalingState = function(newState) {
+    this.signalingState = newState;
+    var event = new Event('signalingstatechange');
+    this.dispatchEvent(event);
+    if (this.onsignalingstatechange !== null) {
+      this.onsignalingstatechange(event);
+    }
+  };
+
+  // Determine whether to fire the negotiationneeded event.
+  RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {
+    var self = this;
+    if (this.signalingState !== 'stable' || this.needNegotiation === true) {
+      return;
+    }
+    this.needNegotiation = true;
+    window.setTimeout(function() {
+      if (self.needNegotiation === false) {
+        return;
+      }
+      self.needNegotiation = false;
+      var event = new Event('negotiationneeded');
+      self.dispatchEvent(event);
+      if (self.onnegotiationneeded !== null) {
+        self.onnegotiationneeded(event);
+      }
+    }, 0);
+  };
+
+  // Update the connection state.
+  RTCPeerConnection.prototype._updateConnectionState = function() {
+    var self = this;
+    var newState;
+    var states = {
+      'new': 0,
+      closed: 0,
+      connecting: 0,
+      checking: 0,
+      connected: 0,
+      completed: 0,
+      failed: 0
+    };
+    this.transceivers.forEach(function(transceiver) {
+      states[transceiver.iceTransport.state]++;
+      states[transceiver.dtlsTransport.state]++;
+    });
+    // ICETransport.completed and connected are the same for this purpose.
+    states.connected += states.completed;
+
+    newState = 'new';
+    if (states.failed > 0) {
+      newState = 'failed';
+    } else if (states.connecting > 0 || states.checking > 0) {
+      newState = 'connecting';
+    } else if (states.disconnected > 0) {
+      newState = 'disconnected';
+    } else if (states.new > 0) {
+      newState = 'new';
+    } else if (states.connected > 0 || states.completed > 0) {
+      newState = 'connected';
+    }
+
+    if (newState !== self.iceConnectionState) {
+      self.iceConnectionState = newState;
+      var event = new Event('iceconnectionstatechange');
+      this.dispatchEvent(event);
+      if (this.oniceconnectionstatechange !== null) {
+        this.oniceconnectionstatechange(event);
+      }
+    }
+  };
+
+  RTCPeerConnection.prototype.createOffer = function() {
+    var self = this;
+    if (this._pendingOffer) {
+      throw new Error('createOffer called while there is a pending offer.');
+    }
+    var offerOptions;
+    if (arguments.length === 1 && typeof arguments[0] !== 'function') {
+      offerOptions = arguments[0];
+    } else if (arguments.length === 3) {
+      offerOptions = arguments[2];
+    }
+
+    var numAudioTracks = this.transceivers.filter(function(t) {
+      return t.kind === 'audio';
+    }).length;
+    var numVideoTracks = this.transceivers.filter(function(t) {
+      return t.kind === 'video';
+    }).length;
+
+    // Determine number of audio and video tracks we need to send/recv.
+    if (offerOptions) {
+      // Reject Chrome legacy constraints.
+      if (offerOptions.mandatory || offerOptions.optional) {
+        throw new TypeError(
+            'Legacy mandatory/optional constraints not supported.');
+      }
+      if (offerOptions.offerToReceiveAudio !== undefined) {
+        if (offerOptions.offerToReceiveAudio === true) {
+          numAudioTracks = 1;
+        } else if (offerOptions.offerToReceiveAudio === false) {
+          numAudioTracks = 0;
+        } else {
+          numAudioTracks = offerOptions.offerToReceiveAudio;
+        }
+      }
+      if (offerOptions.offerToReceiveVideo !== undefined) {
+        if (offerOptions.offerToReceiveVideo === true) {
+          numVideoTracks = 1;
+        } else if (offerOptions.offerToReceiveVideo === false) {
+          numVideoTracks = 0;
+        } else {
+          numVideoTracks = offerOptions.offerToReceiveVideo;
+        }
+      }
+    }
+
+    this.transceivers.forEach(function(transceiver) {
+      if (transceiver.kind === 'audio') {
+        numAudioTracks--;
+        if (numAudioTracks < 0) {
+          transceiver.wantReceive = false;
+        }
+      } else if (transceiver.kind === 'video') {
+        numVideoTracks--;
+        if (numVideoTracks < 0) {
+          transceiver.wantReceive = false;
+        }
+      }
+    });
+
+    // Create M-lines for recvonly streams.
+    while (numAudioTracks > 0 || numVideoTracks > 0) {
+      if (numAudioTracks > 0) {
+        this._createTransceiver('audio');
+        numAudioTracks--;
+      }
+      if (numVideoTracks > 0) {
+        this._createTransceiver('video');
+        numVideoTracks--;
+      }
+    }
+    // reorder tracks
+    var transceivers = sortTracks(this.transceivers);
+
+    var sdp = SDPUtils.writeSessionBoilerplate();
+    transceivers.forEach(function(transceiver, sdpMLineIndex) {
+      // For each track, create an ice gatherer, ice transport,
+      // dtls transport, potentially rtpsender and rtpreceiver.
+      var track = transceiver.track;
+      var kind = transceiver.kind;
+      var mid = SDPUtils.generateIdentifier();
+      transceiver.mid = mid;
+
+      if (!transceiver.iceGatherer) {
+        transceiver.iceGatherer = self.usingBundle && sdpMLineIndex > 0 ?
+            transceivers[0].iceGatherer :
+            self._createIceGatherer(mid, sdpMLineIndex);
+      }
+
+      var localCapabilities = RTCRtpSender.getCapabilities(kind);
+      // filter RTX until additional stuff needed for RTX is implemented
+      // in adapter.js
+      if (edgeVersion < 15019) {
+        localCapabilities.codecs = localCapabilities.codecs.filter(
+            function(codec) {
+              return codec.name !== 'rtx';
+            });
+      }
+      localCapabilities.codecs.forEach(function(codec) {
+        // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
+        // by adding level-asymmetry-allowed=1
+        if (codec.name === 'H264' &&
+            codec.parameters['level-asymmetry-allowed'] === undefined) {
+          codec.parameters['level-asymmetry-allowed'] = '1';
+        }
+      });
+
+      // generate an ssrc now, to be used later in rtpSender.send
+      var sendEncodingParameters = [{
+        ssrc: (2 * sdpMLineIndex + 1) * 1001
+      }];
+      if (track) {
+        // add RTX
+        if (edgeVersion >= 15019 && kind === 'video') {
+          sendEncodingParameters[0].rtx = {
+            ssrc: (2 * sdpMLineIndex + 1) * 1001 + 1
+          };
+        }
+      }
+
+      if (transceiver.wantReceive) {
+        transceiver.rtpReceiver = new RTCRtpReceiver(transceiver.dtlsTransport,
+            kind);
+      }
+
+      transceiver.localCapabilities = localCapabilities;
+      transceiver.sendEncodingParameters = sendEncodingParameters;
+    });
+
+    // always offer BUNDLE and dispose on return if not supported.
+    if (this._config.bundlePolicy !== 'max-compat') {
+      sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {
+        return t.mid;
+      }).join(' ') + '\r\n';
+    }
+    sdp += 'a=ice-options:trickle\r\n';
+
+    transceivers.forEach(function(transceiver, sdpMLineIndex) {
+      sdp += SDPUtils.writeMediaSection(transceiver,
+          transceiver.localCapabilities, 'offer', transceiver.stream);
+      sdp += 'a=rtcp-rsize\r\n';
+    });
+
+    this._pendingOffer = transceivers;
+    var desc = new RTCSessionDescription({
+      type: 'offer',
+      sdp: sdp
+    });
+    if (arguments.length && typeof arguments[0] === 'function') {
+      window.setTimeout(arguments[0], 0, desc);
+    }
+    return Promise.resolve(desc);
+  };
+
+  RTCPeerConnection.prototype.createAnswer = function() {
+    var sdp = SDPUtils.writeSessionBoilerplate();
+    if (this.usingBundle) {
+      sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {
+        return t.mid;
+      }).join(' ') + '\r\n';
+    }
+    this.transceivers.forEach(function(transceiver, sdpMLineIndex) {
+      if (transceiver.isDatachannel) {
+        sdp += 'm=application 0 DTLS/SCTP 5000\r\n' +
+            'c=IN IP4 0.0.0.0\r\n' +
+            'a=mid:' + transceiver.mid + '\r\n';
+        return;
+      }
+
+      // FIXME: look at direction.
+      if (transceiver.stream) {
+        var localTrack;
+        if (transceiver.kind === 'audio') {
+          localTrack = transceiver.stream.getAudioTracks()[0];
+        } else if (transceiver.kind === 'video') {
+          localTrack = transceiver.stream.getVideoTracks()[0];
+        }
+        if (localTrack) {
+          // add RTX
+          if (edgeVersion >= 15019 && transceiver.kind === 'video') {
+            transceiver.sendEncodingParameters[0].rtx = {
+              ssrc: (2 * sdpMLineIndex + 2) * 1001 + 1
+            };
+          }
+        }
+      }
+
+      // Calculate intersection of capabilities.
+      var commonCapabilities = getCommonCapabilities(
+          transceiver.localCapabilities,
+          transceiver.remoteCapabilities);
+
+      var hasRtx = commonCapabilities.codecs.filter(function(c) {
+        return c.name.toLowerCase() === 'rtx';
+      }).length;
+      if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
+        delete transceiver.sendEncodingParameters[0].rtx;
+      }
+
+      sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,
+          'answer', transceiver.stream);
+      if (transceiver.rtcpParameters &&
+          transceiver.rtcpParameters.reducedSize) {
+        sdp += 'a=rtcp-rsize\r\n';
+      }
+    });
+
+    var desc = new RTCSessionDescription({
+      type: 'answer',
+      sdp: sdp
+    });
+    if (arguments.length && typeof arguments[0] === 'function') {
+      window.setTimeout(arguments[0], 0, desc);
+    }
+    return Promise.resolve(desc);
+  };
+
+  RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
+    if (!candidate) {
+      for (var j = 0; j < this.transceivers.length; j++) {
+        this.transceivers[j].iceTransport.addRemoteCandidate({});
+        if (this.usingBundle) {
+          return Promise.resolve();
+        }
+      }
+    } else {
+      var mLineIndex = candidate.sdpMLineIndex;
+      if (candidate.sdpMid) {
+        for (var i = 0; i < this.transceivers.length; i++) {
+          if (this.transceivers[i].mid === candidate.sdpMid) {
+            mLineIndex = i;
+            break;
+          }
+        }
+      }
+      var transceiver = this.transceivers[mLineIndex];
+      if (transceiver) {
+        var cand = Object.keys(candidate.candidate).length > 0 ?
+            SDPUtils.parseCandidate(candidate.candidate) : {};
+        // Ignore Chrome's invalid candidates since Edge does not like them.
+        if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {
+          return Promise.resolve();
+        }
+        // Ignore RTCP candidates, we assume RTCP-MUX.
+        if (cand.component &&
+            !(cand.component === '1' || cand.component === 1)) {
+          return Promise.resolve();
+        }
+        transceiver.iceTransport.addRemoteCandidate(cand);
+
+        // update the remoteDescription.
+        var sections = SDPUtils.splitSections(this.remoteDescription.sdp);
+        sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()
+            : 'a=end-of-candidates') + '\r\n';
+        this.remoteDescription.sdp = sections.join('');
+      }
+    }
+    if (arguments.length > 1 && typeof arguments[1] === 'function') {
+      window.setTimeout(arguments[1], 0);
+    }
+    return Promise.resolve();
+  };
+
+  RTCPeerConnection.prototype.getStats = function() {
+    var promises = [];
+    this.transceivers.forEach(function(transceiver) {
+      ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
+        'dtlsTransport'].forEach(function(method) {
+          if (transceiver[method]) {
+            promises.push(transceiver[method].getStats());
+          }
+        });
+    });
+    var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&
+        arguments[1];
+    var fixStatsType = function(stat) {
+      return {
+        inboundrtp: 'inbound-rtp',
+        outboundrtp: 'outbound-rtp',
+        candidatepair: 'candidate-pair',
+        localcandidate: 'local-candidate',
+        remotecandidate: 'remote-candidate'
+      }[stat.type] || stat.type;
+    };
+    return new Promise(function(resolve) {
+      // shim getStats with maplike support
+      var results = new Map();
+      Promise.all(promises).then(function(res) {
+        res.forEach(function(result) {
+          Object.keys(result).forEach(function(id) {
+            result[id].type = fixStatsType(result[id]);
+            results.set(id, result[id]);
+          });
+        });
+        if (cb) {
+          window.setTimeout(cb, 0, results);
+        }
+        resolve(results);
+      });
+    });
+  };
+  return RTCPeerConnection;
+};
+
+
+/***/ }),
+/* 14 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+ /* eslint-env node */
+
+
+var browserDetails = __webpack_require__(0).browserDetails;
+
+var firefoxShim = {
+  shimOnTrack: function() {
+    if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
+        window.RTCPeerConnection.prototype)) {
+      Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
+        get: function() {
+          return this._ontrack;
+        },
+        set: function(f) {
+          if (this._ontrack) {
+            this.removeEventListener('track', this._ontrack);
+            this.removeEventListener('addstream', this._ontrackpoly);
+          }
+          this.addEventListener('track', this._ontrack = f);
+          this.addEventListener('addstream', this._ontrackpoly = function(e) {
+            e.stream.getTracks().forEach(function(track) {
+              var event = new Event('track');
+              event.track = track;
+              event.receiver = {track: track};
+              event.streams = [e.stream];
+              this.dispatchEvent(event);
+            }.bind(this));
+          }.bind(this));
+        }
+      });
+    }
+  },
+
+  shimSourceObject: function() {
+    // Firefox has supported mozSrcObject since FF22, unprefixed in 42.
+    if (typeof window === 'object') {
+      if (window.HTMLMediaElement &&
+        !('srcObject' in window.HTMLMediaElement.prototype)) {
+        // Shim the srcObject property, once, when HTMLMediaElement is found.
+        Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
+          get: function() {
+            return this.mozSrcObject;
+          },
+          set: function(stream) {
+            this.mozSrcObject = stream;
+          }
+        });
+      }
+    }
+  },
+
+  shimPeerConnection: function() {
+    if (typeof window !== 'object' || !(window.RTCPeerConnection ||
+        window.mozRTCPeerConnection)) {
+      return; // probably media.peerconnection.enabled=false in about:config
+    }
+    // The RTCPeerConnection object.
+    if (!window.RTCPeerConnection) {
+      window.RTCPeerConnection = function(pcConfig, pcConstraints) {
+        if (browserDetails.version < 38) {
+          // .urls is not supported in FF < 38.
+          // create RTCIceServers with a single url.
+          if (pcConfig && pcConfig.iceServers) {
+            var newIceServers = [];
+            for (var i = 0; i < pcConfig.iceServers.length; i++) {
+              var server = pcConfig.iceServers[i];
+              if (server.hasOwnProperty('urls')) {
+                for (var j = 0; j < server.urls.length; j++) {
+                  var newServer = {
+                    url: server.urls[j]
+                  };
+                  if (server.urls[j].indexOf('turn') === 0) {
+                    newServer.username = server.username;
+                    newServer.credential = server.credential;
+                  }
+                  newIceServers.push(newServer);
+                }
+              } else {
+                newIceServers.push(pcConfig.iceServers[i]);
+              }
+            }
+            pcConfig.iceServers = newIceServers;
+          }
+        }
+        return new mozRTCPeerConnection(pcConfig, pcConstraints);
+      };
+      window.RTCPeerConnection.prototype = mozRTCPeerConnection.prototype;
+
+      // wrap static methods. Currently just generateCertificate.
+      if (mozRTCPeerConnection.generateCertificate) {
+        Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
+          get: function() {
+            return mozRTCPeerConnection.generateCertificate;
+          }
+        });
+      }
+
+      window.RTCSessionDescription = mozRTCSessionDescription;
+      window.RTCIceCandidate = mozRTCIceCandidate;
+    }
+
+    // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
+    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
+        .forEach(function(method) {
+          var nativeMethod = RTCPeerConnection.prototype[method];
+          RTCPeerConnection.prototype[method] = function() {
+            arguments[0] = new ((method === 'addIceCandidate') ?
+                RTCIceCandidate : RTCSessionDescription)(arguments[0]);
+            return nativeMethod.apply(this, arguments);
+          };
+        });
+
+    // support for addIceCandidate(null or undefined)
+    var nativeAddIceCandidate =
+        RTCPeerConnection.prototype.addIceCandidate;
+    RTCPeerConnection.prototype.addIceCandidate = function() {
+      if (!arguments[0]) {
+        if (arguments[1]) {
+          arguments[1].apply(null);
+        }
+        return Promise.resolve();
+      }
+      return nativeAddIceCandidate.apply(this, arguments);
+    };
+
+    // shim getStats with maplike support
+    var makeMapStats = function(stats) {
+      var map = new Map();
+      Object.keys(stats).forEach(function(key) {
+        map.set(key, stats[key]);
+        map[key] = stats[key];
+      });
+      return map;
+    };
+
+    var modernStatsTypes = {
+      inboundrtp: 'inbound-rtp',
+      outboundrtp: 'outbound-rtp',
+      candidatepair: 'candidate-pair',
+      localcandidate: 'local-candidate',
+      remotecandidate: 'remote-candidate'
+    };
+
+    var nativeGetStats = RTCPeerConnection.prototype.getStats;
+    RTCPeerConnection.prototype.getStats = function(selector, onSucc, onErr) {
+      return nativeGetStats.apply(this, [selector || null])
+        .then(function(stats) {
+          if (browserDetails.version < 48) {
+            stats = makeMapStats(stats);
+          }
+          if (browserDetails.version < 53 && !onSucc) {
+            // Shim only promise getStats with spec-hyphens in type names
+            // Leave callback version alone; misc old uses of forEach before Map
+            try {
+              stats.forEach(function(stat) {
+                stat.type = modernStatsTypes[stat.type] || stat.type;
+              });
+            } catch (e) {
+              if (e.name !== 'TypeError') {
+                throw e;
+              }
+              // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
+              stats.forEach(function(stat, i) {
+                stats.set(i, Object.assign({}, stat, {
+                  type: modernStatsTypes[stat.type] || stat.type
+                }));
+              });
+            }
+          }
+          return stats;
+        })
+        .then(onSucc, onErr);
+    };
+  }
+};
+
+// Expose public methods.
+module.exports = {
+  shimOnTrack: firefoxShim.shimOnTrack,
+  shimSourceObject: firefoxShim.shimSourceObject,
+  shimPeerConnection: firefoxShim.shimPeerConnection,
+  shimGetUserMedia: __webpack_require__(15)
+};
+
+
+/***/ }),
+/* 15 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+ /* eslint-env node */
+
+
+var logging = __webpack_require__(0).log;
+var browserDetails = __webpack_require__(0).browserDetails;
+
+// Expose public methods.
+module.exports = function() {
+  var shimError_ = function(e) {
+    return {
+      name: {
+        InternalError: 'NotReadableError',
+        NotSupportedError: 'TypeError',
+        PermissionDeniedError: 'NotAllowedError',
+        SecurityError: 'NotAllowedError'
+      }[e.name] || e.name,
+      message: {
+        'The operation is insecure.': 'The request is not allowed by the ' +
+        'user agent or the platform in the current context.'
+      }[e.message] || e.message,
+      constraint: e.constraint,
+      toString: function() {
+        return this.name + (this.message && ': ') + this.message;
+      }
+    };
+  };
+
+  // getUserMedia constraints shim.
+  var getUserMedia_ = function(constraints, onSuccess, onError) {
+    var constraintsToFF37_ = function(c) {
+      if (typeof c !== 'object' || c.require) {
+        return c;
+      }
+      var require = [];
+      Object.keys(c).forEach(function(key) {
+        if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
+          return;
+        }
+        var r = c[key] = (typeof c[key] === 'object') ?
+            c[key] : {ideal: c[key]};
+        if (r.min !== undefined ||
+            r.max !== undefined || r.exact !== undefined) {
+          require.push(key);
+        }
+        if (r.exact !== undefined) {
+          if (typeof r.exact === 'number') {
+            r. min = r.max = r.exact;
+          } else {
+            c[key] = r.exact;
+          }
+          delete r.exact;
+        }
+        if (r.ideal !== undefined) {
+          c.advanced = c.advanced || [];
+          var oc = {};
+          if (typeof r.ideal === 'number') {
+            oc[key] = {min: r.ideal, max: r.ideal};
+          } else {
+            oc[key] = r.ideal;
+          }
+          c.advanced.push(oc);
+          delete r.ideal;
+          if (!Object.keys(r).length) {
+            delete c[key];
+          }
+        }
+      });
+      if (require.length) {
+        c.require = require;
+      }
+      return c;
+    };
+    constraints = JSON.parse(JSON.stringify(constraints));
+    if (browserDetails.version < 38) {
+      logging('spec: ' + JSON.stringify(constraints));
+      if (constraints.audio) {
+        constraints.audio = constraintsToFF37_(constraints.audio);
+      }
+      if (constraints.video) {
+        constraints.video = constraintsToFF37_(constraints.video);
+      }
+      logging('ff37: ' + JSON.stringify(constraints));
+    }
+    return navigator.mozGetUserMedia(constraints, onSuccess, function(e) {
+      onError(shimError_(e));
+    });
+  };
+
+  // Returns the result of getUserMedia as a Promise.
+  var getUserMediaPromise_ = function(constraints) {
+    return new Promise(function(resolve, reject) {
+      getUserMedia_(constraints, resolve, reject);
+    });
+  };
+
+  // Shim for mediaDevices on older versions.
+  if (!navigator.mediaDevices) {
+    navigator.mediaDevices = {getUserMedia: getUserMediaPromise_,
+      addEventListener: function() { },
+      removeEventListener: function() { }
+    };
+  }
+  navigator.mediaDevices.enumerateDevices =
+      navigator.mediaDevices.enumerateDevices || function() {
+        return new Promise(function(resolve) {
+          var infos = [
+            {kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
+            {kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
+          ];
+          resolve(infos);
+        });
+      };
+
+  if (browserDetails.version < 41) {
+    // Work around http://bugzil.la/1169665
+    var orgEnumerateDevices =
+        navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
+    navigator.mediaDevices.enumerateDevices = function() {
+      return orgEnumerateDevices().then(undefined, function(e) {
+        if (e.name === 'NotFoundError') {
+          return [];
+        }
+        throw e;
+      });
+    };
+  }
+  if (browserDetails.version < 49) {
+    var origGetUserMedia = navigator.mediaDevices.getUserMedia.
+        bind(navigator.mediaDevices);
+    navigator.mediaDevices.getUserMedia = function(c) {
+      return origGetUserMedia(c).then(function(stream) {
+        // Work around https://bugzil.la/802326
+        if (c.audio && !stream.getAudioTracks().length ||
+            c.video && !stream.getVideoTracks().length) {
+          stream.getTracks().forEach(function(track) {
+            track.stop();
+          });
+          throw new DOMException('The object can not be found here.',
+                                 'NotFoundError');
+        }
+        return stream;
+      }, function(e) {
+        return Promise.reject(shimError_(e));
+      });
+    };
+  }
+  navigator.getUserMedia = function(constraints, onSuccess, onError) {
+    if (browserDetails.version < 44) {
+      return getUserMedia_(constraints, onSuccess, onError);
+    }
+    // Replace Firefox 44+'s deprecation warning with unprefixed version.
+    console.warn('navigator.getUserMedia has been replaced by ' +
+                 'navigator.mediaDevices.getUserMedia');
+    navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
+  };
+};
+
+
+/***/ }),
+/* 16 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+
+var safariShim = {
+  // TODO: DrAlex, should be here, double check against LayoutTests
+
+  // TODO: once the back-end for the mac port is done, add.
+  // TODO: check for webkitGTK+
+  // shimPeerConnection: function() { },
+
+  shimAddStream: function() {
+    if (typeof window === 'object' && window.RTCPeerConnection &&
+        !('addStream' in window.RTCPeerConnection.prototype)) {
+      RTCPeerConnection.prototype.addStream = function(stream) {
+        var self = this;
+        stream.getTracks().forEach(function(track) {
+          self.addTrack(track, stream);
+        });
+      };
+    }
+  },
+  shimOnAddStream: function() {
+    if (typeof window === 'object' && window.RTCPeerConnection &&
+        !('onaddstream' in window.RTCPeerConnection.prototype)) {
+      Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
+        get: function() {
+          return this._onaddstream;
+        },
+        set: function(f) {
+          if (this._onaddstream) {
+            this.removeEventListener('addstream', this._onaddstream);
+            this.removeEventListener('track', this._onaddstreampoly);
+          }
+          this.addEventListener('addstream', this._onaddstream = f);
+          this.addEventListener('track', this._onaddstreampoly = function(e) {
+            var stream = e.streams[0];
+            if (!this._streams) {
+              this._streams = [];
+            }
+            if (this._streams.indexOf(stream) >= 0) {
+              return;
+            }
+            this._streams.push(stream);
+            var event = new Event('addstream');
+            event.stream = e.streams[0];
+            this.dispatchEvent(event);
+          }.bind(this));
+        }
+      });
+    }
+  },
+  shimCallbacksAPI: function() {
+    if (typeof window !== 'object' || !window.RTCPeerConnection) {
+      return;
+    }
+    var prototype = RTCPeerConnection.prototype;
+    var createOffer = prototype.createOffer;
+    var createAnswer = prototype.createAnswer;
+    var setLocalDescription = prototype.setLocalDescription;
+    var setRemoteDescription = prototype.setRemoteDescription;
+    var addIceCandidate = prototype.addIceCandidate;
+
+    prototype.createOffer = function(successCallback, failureCallback) {
+      var options = (arguments.length >= 2) ? arguments[2] : arguments[0];
+      var promise = createOffer.apply(this, [options]);
+      if (!failureCallback) {
+        return promise;
+      }
+      promise.then(successCallback, failureCallback);
+      return Promise.resolve();
+    };
+
+    prototype.createAnswer = function(successCallback, failureCallback) {
+      var options = (arguments.length >= 2) ? arguments[2] : arguments[0];
+      var promise = createAnswer.apply(this, [options]);
+      if (!failureCallback) {
+        return promise;
+      }
+      promise.then(successCallback, failureCallback);
+      return Promise.resolve();
+    };
+
+    var withCallback = function(description, successCallback, failureCallback) {
+      var promise = setLocalDescription.apply(this, [description]);
+      if (!failureCallback) {
+        return promise;
+      }
+      promise.then(successCallback, failureCallback);
+      return Promise.resolve();
+    };
+    prototype.setLocalDescription = withCallback;
+
+    withCallback = function(description, successCallback, failureCallback) {
+      var promise = setRemoteDescription.apply(this, [description]);
+      if (!failureCallback) {
+        return promise;
+      }
+      promise.then(successCallback, failureCallback);
+      return Promise.resolve();
+    };
+    prototype.setRemoteDescription = withCallback;
+
+    withCallback = function(candidate, successCallback, failureCallback) {
+      var promise = addIceCandidate.apply(this, [candidate]);
+      if (!failureCallback) {
+        return promise;
+      }
+      promise.then(successCallback, failureCallback);
+      return Promise.resolve();
+    };
+    prototype.addIceCandidate = withCallback;
+  },
+  shimGetUserMedia: function() {
+    if (!navigator.getUserMedia) {
+      if (navigator.webkitGetUserMedia) {
+        navigator.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
+      } else if (navigator.mediaDevices &&
+          navigator.mediaDevices.getUserMedia) {
+        navigator.getUserMedia = function(constraints, cb, errcb) {
+          navigator.mediaDevices.getUserMedia(constraints)
+          .then(cb, errcb);
+        }.bind(navigator);
+      }
+    }
+  }
+};
+
+// Expose public methods.
+module.exports = {
+  shimCallbacksAPI: safariShim.shimCallbacksAPI,
+  shimAddStream: safariShim.shimAddStream,
+  shimOnAddStream: safariShim.shimOnAddStream,
+  shimGetUserMedia: safariShim.shimGetUserMedia
+  // TODO
+  // shimPeerConnection: safariShim.shimPeerConnection
+};
+
+
+/***/ }),
+/* 17 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var cordovaRequire = __webpack_require__(5);
+var cordovaModule = __webpack_require__(1);
+
+var createQRScannerInternal = __webpack_require__(2);
+
+cordovaModule.exports = createQRScannerInternal();
+cordovaRequire('cordova/exec/proxy').add('QRScanner', cordovaModule.exports);
+
+
+/***/ })
+/******/ ]);
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-qrscanner/www/www.min.js b/platforms/browser/platform_www/plugins/cordova-plugin-qrscanner/www/www.min.js
new file mode 100644
index 0000000..5d95c0a
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-qrscanner/www/www.min.js
@@ -0,0 +1,343 @@
+cordova.define("cordova-plugin-qrscanner.QRScanner", function(require, exports, module) { // This file is generated by `npm run build`.
+
+/*global exports:false*/
+/*jshint unused:false */
+// remap parameter names from cordova.define
+// see `externals` in webpack.cordova.config.js
+var cordovaRequire = require;
+var cordovaExports = exports;
+var cordovaModule = module;
+/******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+/******/
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+/******/
+/******/ 	// identity function for calling harmony imports with the correct context
+/******/ 	__webpack_require__.i = function(value) { return value; };
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, {
+/******/ 				configurable: false,
+/******/ 				enumerable: true,
+/******/ 				get: getter
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 18);
+/******/ })
+/************************************************************************/
+/******/ ({
+
+/***/ 1:
+/***/ (function(module, exports) {
+
+module.exports = cordovaModule;
+
+/***/ }),
+
+/***/ 18:
+/***/ (function(module, exports, __webpack_require__) {
+
+var globalCordova = __webpack_require__(4);
+var cordovaModule = __webpack_require__(1);
+
+var createQRScannerAdapter = __webpack_require__(3);
+
+// pass in global cordova object to expose cordova.exec
+var QRScannerAdapter = createQRScannerAdapter(globalCordova);
+cordovaModule.exports = QRScannerAdapter;
+
+
+/***/ }),
+
+/***/ 3:
+/***/ (function(module, exports) {
+
+module.exports = function createQRScanner(cordova){
+// The native implementations should return their status as ['string':'string']
+// dictionaries. Boolean values are encoded to '0' and '1', respectively.
+function stringToBool(string) {
+  switch (string) {
+    case '1':
+      return true;
+    case '0':
+      return false;
+    default:
+    throw new Error('QRScanner plugin returned an invalid boolean number-string: ' + string);
+  }
+}
+
+// Converts the returned ['string':'string'] dictionary to a status object.
+function convertStatus(statusDictionary) {
+  return {
+    authorized: stringToBool(statusDictionary.authorized),
+    denied: stringToBool(statusDictionary.denied),
+    restricted: stringToBool(statusDictionary.restricted),
+    prepared: stringToBool(statusDictionary.prepared),
+    scanning: stringToBool(statusDictionary.scanning),
+    previewing: stringToBool(statusDictionary.previewing),
+    showing: stringToBool(statusDictionary.showing),
+    lightEnabled: stringToBool(statusDictionary.lightEnabled),
+    canOpenSettings: stringToBool(statusDictionary.canOpenSettings),
+    canEnableLight: stringToBool(statusDictionary.canEnableLight),
+    canChangeCamera: stringToBool(statusDictionary.canChangeCamera),
+    currentCamera: parseInt(statusDictionary.currentCamera)
+  };
+}
+
+// Simple utility method to ensure the background is transparent. Used by the
+// plugin to force re-rendering immediately after the native webview background
+// is made transparent.
+function clearBackground() {
+  var body = document.body;
+  if (body.style) {
+    body.style.backgroundColor = 'rgba(0,0,0,0.01)';
+    body.style.backgroundImage = '';
+    setTimeout(function() {
+      body.style.backgroundColor = 'transparent';
+    }, 1);
+    if (body.parentNode && body.parentNode.style) {
+      body.parentNode.style.backgroundColor = 'transparent';
+      body.parentNode.style.backgroundImage = '';
+    }
+  }
+}
+
+function errorCallback(callback) {
+  if (!callback) {
+    return null;
+  }
+  return function(error) {
+    var errorCode = parseInt(error);
+    var QRScannerError = {};
+    switch (errorCode) {
+      case 0:
+        QRScannerError = {
+          name: 'UNEXPECTED_ERROR',
+          code: 0,
+          _message: 'QRScanner experienced an unexpected error.'
+        };
+        break;
+      case 1:
+        QRScannerError = {
+          name: 'CAMERA_ACCESS_DENIED',
+          code: 1,
+          _message: 'The user denied camera access.'
+        };
+        break;
+      case 2:
+        QRScannerError = {
+          name: 'CAMERA_ACCESS_RESTRICTED',
+          code: 2,
+          _message: 'Camera access is restricted.'
+        };
+        break;
+      case 3:
+        QRScannerError = {
+          name: 'BACK_CAMERA_UNAVAILABLE',
+          code: 3,
+          _message: 'The back camera is unavailable.'
+        };
+        break;
+      case 4:
+        QRScannerError = {
+          name: 'FRONT_CAMERA_UNAVAILABLE',
+          code: 4,
+          _message: 'The front camera is unavailable.'
+        };
+        break;
+      case 5:
+        QRScannerError = {
+          name: 'CAMERA_UNAVAILABLE',
+          code: 5,
+          _message: 'The camera is unavailable.'
+        };
+        break;
+      case 6:
+        QRScannerError = {
+          name: 'SCAN_CANCELED',
+          code: 6,
+          _message: 'Scan was canceled.'
+        };
+        break;
+      case 7:
+        QRScannerError = {
+          name: 'LIGHT_UNAVAILABLE',
+          code: 7,
+          _message: 'The device light is unavailable.'
+        };
+        break;
+      case 8:
+        // Open settings is only available on iOS 8.0+.
+        QRScannerError = {
+          name: 'OPEN_SETTINGS_UNAVAILABLE',
+          code: 8,
+          _message: 'The device is unable to open settings.'
+        };
+        break;
+      default:
+        QRScannerError = {
+          name: 'UNEXPECTED_ERROR',
+          code: 0,
+          _message: 'QRScanner returned an invalid error code.'
+        };
+        break;
+    }
+    callback(QRScannerError);
+  };
+}
+
+function successCallback(callback) {
+  if (!callback) {
+    return null;
+  }
+  return function(statusDict) {
+    callback(null, convertStatus(statusDict));
+  };
+}
+
+function doneCallback(callback, clear) {
+  if (!callback) {
+    return null;
+  }
+  return function(statusDict) {
+    if (clear) {
+      clearBackground();
+    }
+    callback(convertStatus(statusDict));
+  };
+}
+
+return {
+  prepare: function(callback) {
+    cordova.exec(successCallback(callback), errorCallback(callback), 'QRScanner', 'prepare', []);
+  },
+  destroy: function(callback) {
+    cordova.exec(doneCallback(callback, true), null, 'QRScanner', 'destroy', []);
+  },
+  scan: function(callback) {
+    if (!callback) {
+      throw new Error('No callback provided to scan method.');
+    }
+    var success = function(result) {
+      callback(null, result);
+    };
+    cordova.exec(success, errorCallback(callback), 'QRScanner', 'scan', []);
+  },
+  cancelScan: function(callback) {
+    cordova.exec(doneCallback(callback), null, 'QRScanner', 'cancelScan', []);
+  },
+  show: function(callback) {
+    cordova.exec(doneCallback(callback, true), null, 'QRScanner', 'show', []);
+  },
+  hide: function(callback) {
+    cordova.exec(doneCallback(callback, true), null, 'QRScanner', 'hide', []);
+  },
+  pausePreview: function(callback) {
+    cordova.exec(doneCallback(callback), null, 'QRScanner', 'pausePreview', []);
+  },
+  resumePreview: function(callback) {
+    cordova.exec(doneCallback(callback), null, 'QRScanner', 'resumePreview', []);
+  },
+  enableLight: function(callback) {
+    cordova.exec(successCallback(callback), errorCallback(callback), 'QRScanner', 'enableLight', []);
+  },
+  disableLight: function(callback) {
+    cordova.exec(successCallback(callback), errorCallback(callback), 'QRScanner', 'disableLight', []);
+  },
+  useCamera: function(index, callback) {
+    cordova.exec(successCallback(callback), errorCallback(callback), 'QRScanner', 'useCamera', [index]);
+  },
+  useFrontCamera: function(callback) {
+    var frontCamera = 1;
+    if (callback) {
+      this.useCamera(frontCamera, callback);
+    } else {
+      cordova.exec(null, null, 'QRScanner', 'useCamera', [frontCamera]);
+    }
+  },
+  useBackCamera: function(callback) {
+    var backCamera = 0;
+    if (callback) {
+      this.useCamera(backCamera, callback);
+    } else {
+      cordova.exec(null, null, 'QRScanner', 'useCamera', [backCamera]);
+    }
+  },
+  openSettings: function(callback) {
+    if (callback) {
+      cordova.exec(successCallback(callback), errorCallback(callback), 'QRScanner', 'openSettings', []);
+    } else {
+      cordova.exec(null, null, 'QRScanner', 'openSettings', []);
+    }
+  },
+  getStatus: function(callback) {
+    if (!callback) {
+      throw new Error('No callback provided to getStatus method.');
+    }
+    cordova.exec(doneCallback(callback), null, 'QRScanner', 'getStatus', []);
+  }
+};
+};
+
+
+/***/ }),
+
+/***/ 4:
+/***/ (function(module, exports) {
+
+module.exports = cordova;
+
+/***/ })
+
+/******/ });
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-splashscreen/src/browser/SplashScreenProxy.js b/platforms/browser/platform_www/plugins/cordova-plugin-splashscreen/src/browser/SplashScreenProxy.js
new file mode 100644
index 0000000..6f04cf5
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-splashscreen/src/browser/SplashScreenProxy.js
@@ -0,0 +1,172 @@
+cordova.define("cordova-plugin-splashscreen.SplashScreenProxy", function(require, exports, module) { /*

+ *

+ * 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.

+ *

+*/

+

+// Default parameter values including image size can be changed in `config.xml`

+var splashImageWidth = 170;

+var splashImageHeight = 200;

+var position = { x: 0, y: 0, width: splashImageWidth, height: splashImageHeight }; 

+var localSplash; // the image to display

+var localSplashImage;

+var bgColor = "#464646";

+var imageSrc = '/img/logo.png';

+var splashScreenDelay = 3000; // in milliseconds

+var showSplashScreen = true; // show splashcreen by default

+var cordova = require('cordova');

+var configHelper = cordova.require('cordova/confighelper');

+var autoHideSplashScreen = true;

+

+function updateImageLocation() {

+    position.width = Math.min(splashImageWidth, window.innerWidth);

+    position.height = position.width * (splashImageHeight / splashImageWidth);

+

+    localSplash.style.width = window.innerWidth + "px";

+    localSplash.style.height = window.innerHeight + "px";

+    localSplash.style.top = "0px";

+    localSplash.style.left = "0px";

+

+    localSplashImage.style.top = "50%";

+    localSplashImage.style.left = "50%";

+    localSplashImage.style.height = position.height + "px";

+    localSplashImage.style.width = position.width + "px";

+    localSplashImage.style.marginTop = (-position.height / 2) + "px";

+    localSplashImage.style.marginLeft = (-position.width / 2) + "px";

+}

+

+function onResize() {

+    updateImageLocation();

+}

+

+var SplashScreen = {

+    setBGColor: function (cssBGColor) {

+        bgColor = cssBGColor;

+        if (localSplash) {

+            localSplash.style.backgroundColor = bgColor;

+        }

+    },

+    show: function () {

+        if(!localSplash) {

+            window.addEventListener("resize", onResize, false);

+            localSplash = document.createElement("div");

+            localSplash.style.backgroundColor = bgColor;

+            localSplash.style.position = "absolute";

+            localSplash.style["z-index"] = "99999";

+

+            localSplashImage = document.createElement("img");

+            localSplashImage.src = imageSrc;

+            localSplashImage.style.position = "absolute";

+

+            updateImageLocation();

+

+            localSplash.appendChild(localSplashImage);

+            document.body.appendChild(localSplash);

+

+            // deviceready fires earlier than the plugin init on cold-start

+            if (SplashScreen.shouldHideImmediately) {

+                SplashScreen.shouldHideImmediately = false;

+                window.setTimeout(function () {

+                    SplashScreen.hide();

+                }, 1000);

+            }

+        }

+    },

+    hide: function () {

+        if(localSplash) {

+            var innerLocalSplash = localSplash;

+            localSplash = null;

+            window.removeEventListener("resize", onResize, false);

+

+            innerLocalSplash.style.opacity = '0';

+            innerLocalSplash.style["-webkit-transition"] = "opacity 1s ease-in-out";

+            innerLocalSplash.style["-moz-transition"] = "opacity 1s ease-in-out";

+            innerLocalSplash.style["-ms-transition"] = "opacity 1s ease-in-out";

+            innerLocalSplash.style["-o-transition"] = "opacity 1s ease-in-out";

+

+            window.setTimeout(function () {

+                document.body.removeChild(innerLocalSplash);

+                innerLocalSplash = null;

+            }, 1000);

+        } else {

+            SplashScreen.shouldHideImmediately = true;

+        }

+    }

+};

+

+/**

+ * Reads preferences via ConfigHelper and substitutes default parameters.

+ */

+function readPreferencesFromCfg(cfg) {

+    try {

+        var value = cfg.getPreferenceValue('ShowSplashScreen');

+        if(typeof value != 'undefined') {

+            showSplashScreen = value === 'true';

+        }

+

+        splashScreenDelay = cfg.getPreferenceValue('SplashScreenDelay') || splashScreenDelay;

+        splashScreenDelay = parseInt(splashScreenDelay, 10);

+

+        imageSrc = cfg.getPreferenceValue('SplashScreen') || imageSrc;

+        bgColor = cfg.getPreferenceValue('SplashScreenBackgroundColor') || bgColor;

+        splashImageWidth = cfg.getPreferenceValue('SplashScreenWidth') || splashImageWidth;

+        splashImageHeight = cfg.getPreferenceValue('SplashScreenHeight') || splashImageHeight;

+        autoHideSplashScreen = cfg.getPreferenceValue('AutoHideSplashScreen') || autoHideSplashScreen;

+        autoHideSplashScreen = (autoHideSplashScreen === true || autoHideSplashScreen.toLowerCase() === 'true');

+    } catch(e) {

+        var msg = '[Browser][SplashScreen] Error occurred on loading preferences from config.xml: ' + JSON.stringify(e);

+        console.error(msg);

+    }

+}

+

+/**

+ * Shows and hides splashscreen if it is enabled, with a delay according the current preferences.

+ */

+function showAndHide() {

+    if(showSplashScreen) {

+        SplashScreen.show();

+

+        window.setTimeout(function() {

+            SplashScreen.hide();

+        }, splashScreenDelay);

+    }

+}

+

+/**

+ * Tries to read config.xml and override default properties and then shows and hides splashscreen if it is enabled.

+ */

+(function initAndShow() {

+    configHelper.readConfig(function(config) {

+        readPreferencesFromCfg(config);

+        if (autoHideSplashScreen) {

+            showAndHide();

+        } else {

+            SplashScreen.show();

+        }

+

+    }, function(err) {

+        console.error(err);

+    });

+})();

+

+module.exports = SplashScreen;

+

+require("cordova/exec/proxy").add("SplashScreen", SplashScreen);

+

+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-splashscreen/www/splashscreen.js b/platforms/browser/platform_www/plugins/cordova-plugin-splashscreen/www/splashscreen.js
new file mode 100644
index 0000000..5b42e17
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-splashscreen/www/splashscreen.js
@@ -0,0 +1,35 @@
+cordova.define("cordova-plugin-splashscreen.SplashScreen", function(require, exports, module) { /*

+ *

+ * 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 exec = require('cordova/exec');

+

+var splashscreen = {

+    show:function() {

+        exec(null, null, "SplashScreen", "show", []);

+    },

+    hide:function() {

+        exec(null, null, "SplashScreen", "hide", []);

+    }

+};

+

+module.exports = splashscreen;

+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-statusbar/src/browser/StatusBarProxy.js b/platforms/browser/platform_www/plugins/cordova-plugin-statusbar/src/browser/StatusBarProxy.js
new file mode 100644
index 0000000..e6e41ec
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-statusbar/src/browser/StatusBarProxy.js
@@ -0,0 +1,52 @@
+cordova.define("cordova-plugin-statusbar.StatusBarProxy", function(require, exports, module) { /*
+ * 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 notSupported(win,fail) {
+    //
+    console.log('StatusBar is not supported');
+    setTimeout(function(){
+        if (win) {
+            win();
+        }
+        // note that while it is not explicitly supported, it does not fail
+        // this is really just here to allow developers to test their code in the browser
+        // and if we fail, then their app might as well. -jm
+    },0);
+}
+
+module.exports = {
+    isVisible: false,
+    styleBlackTranslucent:notSupported,
+    styleDefault:notSupported,
+    styleLightContent:notSupported,
+    styleBlackOpaque:notSupported,
+    overlaysWebView:notSupported,
+    styleLightContect: notSupported,
+    backgroundColorByName: notSupported,
+    backgroundColorByHexString: notSupported,
+    hide: notSupported,
+    show: notSupported,
+    _ready:notSupported
+};
+
+require("cordova/exec/proxy").add("StatusBar", module.exports);
+
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-statusbar/www/statusbar.js b/platforms/browser/platform_www/plugins/cordova-plugin-statusbar/www/statusbar.js
new file mode 100644
index 0000000..9684d23
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-statusbar/www/statusbar.js
@@ -0,0 +1,115 @@
+cordova.define("cordova-plugin-statusbar.statusbar", function(require, exports, module) { /*
+ *
+ * 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.
+ *
+*/
+
+/* global cordova */
+
+var exec = require('cordova/exec');
+
+var namedColors = {
+    "black": "#000000",
+    "darkGray": "#A9A9A9",
+    "lightGray": "#D3D3D3",
+    "white": "#FFFFFF",
+    "gray": "#808080",
+    "red": "#FF0000",
+    "green": "#00FF00",
+    "blue": "#0000FF",
+    "cyan": "#00FFFF",
+    "yellow": "#FFFF00",
+    "magenta": "#FF00FF",
+    "orange": "#FFA500",
+    "purple": "#800080",
+    "brown": "#A52A2A"
+};
+
+var StatusBar = {
+
+    isVisible: true,
+
+    overlaysWebView: function (doOverlay) {
+        exec(null, null, "StatusBar", "overlaysWebView", [doOverlay]);
+    },
+
+    styleDefault: function () {
+        // dark text ( to be used on a light background )
+        exec(null, null, "StatusBar", "styleDefault", []);
+    },
+
+    styleLightContent: function () {
+        // light text ( to be used on a dark background )
+        exec(null, null, "StatusBar", "styleLightContent", []);
+    },
+
+    styleBlackTranslucent: function () {
+        // #88000000 ? Apple says to use lightContent instead
+        exec(null, null, "StatusBar", "styleBlackTranslucent", []);
+    },
+
+    styleBlackOpaque: function () {
+        // #FF000000 ? Apple says to use lightContent instead
+        exec(null, null, "StatusBar", "styleBlackOpaque", []);
+    },
+
+    backgroundColorByName: function (colorname) {
+        return StatusBar.backgroundColorByHexString(namedColors[colorname]);
+    },
+
+    backgroundColorByHexString: function (hexString) {
+        if (hexString.charAt(0) !== "#") {
+            hexString = "#" + hexString;
+        }
+
+        if (hexString.length === 4) {
+            var split = hexString.split("");
+            hexString = "#" + split[1] + split[1] + split[2] + split[2] + split[3] + split[3];
+        }
+
+        exec(null, null, "StatusBar", "backgroundColorByHexString", [hexString]);
+    },
+
+    hide: function () {
+        exec(null, null, "StatusBar", "hide", []);
+        StatusBar.isVisible = false;
+    },
+
+    show: function () {
+        exec(null, null, "StatusBar", "show", []);
+        StatusBar.isVisible = true;
+    }
+
+};
+
+// prime it. setTimeout so that proxy gets time to init
+window.setTimeout(function () {
+    exec(function (res) {
+        if (typeof res == 'object') {
+            if (res.type == 'tap') {
+                cordova.fireWindowEvent('statusTap');
+            }
+        } else {
+            StatusBar.isVisible = res;
+        }
+    }, null, "StatusBar", "_ready", []);
+}, 0);
+
+module.exports = StatusBar;
+
+});
diff --git a/platforms/browser/platform_www/plugins/cordova-plugin-touch-id/www/TouchID.js b/platforms/browser/platform_www/plugins/cordova-plugin-touch-id/www/TouchID.js
new file mode 100644
index 0000000..6e4b765
--- /dev/null
+++ b/platforms/browser/platform_www/plugins/cordova-plugin-touch-id/www/TouchID.js
@@ -0,0 +1,35 @@
+cordova.define("cordova-plugin-touch-id.TouchID", function(require, exports, module) { function TouchID() {
+}
+
+TouchID.prototype.isAvailable = function (successCallback, errorCallback) {
+  cordova.exec(successCallback, errorCallback, "TouchID", "isAvailable", []);
+};
+
+TouchID.prototype.didFingerprintDatabaseChange = function (successCallback, errorCallback) {
+  cordova.exec(successCallback, errorCallback, "TouchID", "didFingerprintDatabaseChange", []);
+};
+
+TouchID.prototype.verifyFingerprint = function (message, successCallback, errorCallback) {
+  cordova.exec(successCallback, errorCallback, "TouchID", "verifyFingerprint", [message]);
+};
+
+TouchID.prototype.verifyFingerprintWithCustomPasswordFallback = function (message, successCallback, errorCallback) {
+  cordova.exec(successCallback, errorCallback, "TouchID", "verifyFingerprintWithCustomPasswordFallback", [message]);
+};
+
+TouchID.prototype.verifyFingerprintWithCustomPasswordFallbackAndEnterPasswordLabel = function (message, enterPasswordLabel, successCallback, errorCallback) {
+  cordova.exec(successCallback, errorCallback, "TouchID", "verifyFingerprintWithCustomPasswordFallbackAndEnterPasswordLabel", [message, enterPasswordLabel]);
+};
+
+TouchID.install = function () {
+  if (!window.plugins) {
+    window.plugins = {};
+  }
+
+  window.plugins.touchid = new TouchID();
+  return window.plugins.touchid;
+};
+
+cordova.addConstructor(TouchID.install);
+
+});