diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 00000000..f6aae321 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,10 @@ +root: true +extends: semistandard +rules: + indent: + - error + - 4 + camelcase: off + padded-blocks: off + operator-linebreak: off + no-throw-literal: off diff --git a/.travis.yml b/.travis.yml index a52db177..7ad8eeae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ android: - tools - tools script: - - npm run jshint + - npm run eslint - npm run cover - npm run test-build after_script: diff --git a/bin/lib/create.js b/bin/lib/create.js index 58766891..f57c62c3 100755 --- a/bin/lib/create.js +++ b/bin/lib/create.js @@ -19,12 +19,12 @@ under the License. */ -var shell = require('shelljs'), - Q = require('q'), - path = require('path'), - fs = require('fs'), - check_reqs = require('./../templates/cordova/lib/check_reqs'), - ROOT = path.join(__dirname, '..', '..'); +var shell = require('shelljs'); +var Q = require('q'); +var path = require('path'); +var fs = require('fs'); +var check_reqs = require('./../templates/cordova/lib/check_reqs'); +var ROOT = path.join(__dirname, '..', '..'); var MIN_SDK_VERSION = 19; @@ -32,14 +32,14 @@ var CordovaError = require('cordova-common').CordovaError; var AndroidStudio = require('../templates/cordova/lib/AndroidStudio'); var AndroidManifest = require('../templates/cordova/lib/AndroidManifest'); -function setShellFatal(value, func) { +function setShellFatal (value, func) { var oldVal = shell.config.fatal; shell.config.fatal = value; func(); shell.config.fatal = oldVal; } -function getFrameworkDir(projectPath, shared) { +function getFrameworkDir (projectPath, shared) { return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib'); } @@ -62,9 +62,9 @@ function copyJsAndLibrary(projectPath, shared, projectName, isLegacy) { // We need these files to build cordova.js if using browserify method. shell.cp('-rf', path.join(ROOT, 'cordova-js-src'), path.join(projectPath, 'platform_www')); - // Don't fail if there are no old jars, because there hasn't been cordova JARs for years! - setShellFatal(false, function() { - shell.ls(path.join(app_path, 'libs', 'cordova-*.jar')).forEach(function(oldJar) { + // Don't fail if there are no old jars. + setShellFatal(false, function () { + shell.ls(path.join(projectPath, 'libs', 'cordova-*.jar')).forEach(function (oldJar) { console.log('Deleting ' + oldJar); shell.rm('-f', oldJar); }); @@ -96,7 +96,7 @@ function copyJsAndLibrary(projectPath, shared, projectName, isLegacy) { } } -function extractSubProjectPaths(data) { +function extractSubProjectPaths (data) { var ret = {}; var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg; var m; @@ -106,7 +106,7 @@ function extractSubProjectPaths(data) { return Object.keys(ret); } -function writeProjectProperties(projectPath, target_api) { +function writeProjectProperties (projectPath, target_api) { var dstPath = path.join(projectPath, 'project.properties'); var templatePath = path.join(ROOT, 'bin', 'templates', 'project', 'project.properties'); var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath; @@ -114,11 +114,10 @@ function writeProjectProperties(projectPath, target_api) { var data = fs.readFileSync(srcPath, 'utf8'); data = data.replace(/^target=.*/m, 'target=' + target_api); var subProjects = extractSubProjectPaths(data); - subProjects = subProjects.filter(function(p) { + subProjects = subProjects.filter(function (p) { return !(/^CordovaLib$/m.exec(p) || - /[\\\/]cordova-android[\\\/]framework$/m.exec(p) || - /^(\.\.[\\\/])+framework$/m.exec(p) - ); + /[\\/]cordova-android[\\/]framework$/m.exec(p) || + /^(\.\.[\\/])+framework$/m.exec(p)); }); subProjects.unshift('CordovaLib'); data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, ''); @@ -126,7 +125,7 @@ function writeProjectProperties(projectPath, target_api) { data += '\n'; } for (var i = 0; i < subProjects.length; ++i) { - data += 'android.library.reference.' + (i+1) + '=' + subProjects[i] + '\n'; + data += 'android.library.reference.' + (i + 1) + '=' + subProjects[i] + '\n'; } fs.writeFileSync(dstPath, data); } @@ -151,7 +150,7 @@ function copyBuildRules(projectPath, isLegacy) { } } -function copyScripts(projectPath) { +function copyScripts (projectPath) { var bin = path.join(ROOT, 'bin'); var srcScriptsDir = path.join(bin, 'templates', 'cordova'); var destScriptsDir = path.join(projectPath, 'cordova'); @@ -178,17 +177,17 @@ function copyScripts(projectPath) { * Returns a promise, fulfilled if the package name is acceptable; rejected * otherwise. */ -function validatePackageName(package_name) { - //Make the package conform to Java package types - //http://developer.android.com/guide/topics/manifest/manifest-element.html#package - //Enforce underscore limitation +function validatePackageName (package_name) { + // Make the package conform to Java package types + // http://developer.android.com/guide/topics/manifest/manifest-element.html#package + // Enforce underscore limitation var msg = 'Error validating package name. '; if (!/^[a-zA-Z][a-zA-Z0-9_]+(\.[a-zA-Z][a-zA-Z0-9_]*)+$/.test(package_name)) { return Q.reject(new CordovaError(msg + 'Package name must look like: com.company.Name')); } - //Class is a reserved word - if(/\b[Cc]lass\b/.test(package_name)) { + // Class is a reserved word + if (/\b[Cc]lass\b/.test(package_name)) { return Q.reject(new CordovaError(msg + '"class" is a reserved word')); } @@ -200,19 +199,19 @@ function validatePackageName(package_name) { * Returns a promise, fulfilled if the project name is acceptable; rejected * otherwise. */ -function validateProjectName(project_name) { +function validateProjectName (project_name) { var msg = 'Error validating project name. '; - //Make sure there's something there + // Make sure there's something there if (project_name === '') { return Q.reject(new CordovaError(msg + 'Project name cannot be empty')); } - //Enforce stupid name error + // Enforce stupid name error if (project_name === 'CordovaActivity') { return Q.reject(new CordovaError(msg + 'Project name cannot be CordovaActivity')); } - //Classes in Java don't begin with numbers + // Classes in Java don't begin with numbers if (/^[0-9]/.test(project_name)) { return Q.reject(new CordovaError(msg + 'Project name must not begin with a number')); } @@ -238,29 +237,29 @@ function validateProjectName(project_name) { * * @return {Promise} Directory where application has been created */ -exports.create = function(project_path, config, options, events) { +exports.create = function (project_path, config, options, events) { options = options || {}; // Set default values for path, package and name project_path = path.relative(process.cwd(), (project_path || 'CordovaExample')); // Check if project already exists - if(fs.existsSync(project_path)) { + if (fs.existsSync(project_path)) { return Q.reject(new CordovaError('Project already exists! Delete and recreate')); } var package_name = config.packageName() || 'my.cordova.project'; var project_name = config.name() ? - config.name().replace(/[^\w.]/g,'_') : 'CordovaExample'; + config.name().replace(/[^\w.]/g, '_') : 'CordovaExample'; var safe_activity_name = config.android_activityName() || options.activityName || 'MainActivity'; - var target_api = check_reqs.get_target(); + var target_api = check_reqs.get_target(); - //Make the package conform to Java package types + // Make the package conform to Java package types return validatePackageName(package_name) - .then(function() { - validateProjectName(project_name); - }).then(function() { + .then(function () { + validateProjectName(project_name); + }).then(function () { // Log the given values for the project events.emit('log', 'Creating Cordova project for the Android platform:'); events.emit('log', '\tPath: ' + project_path); @@ -324,9 +323,9 @@ exports.create = function(project_path, config, options, events) { }).thenResolve(project_path); }; -function generateDoneMessage(type, link) { +function generateDoneMessage (type, link) { var pkg = require('../../package'); - var msg = 'Android project ' + (type == 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version; + var msg = 'Android project ' + (type === 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version; if (link) { msg += ' and has a linked CordovaLib'; } @@ -334,11 +333,11 @@ function generateDoneMessage(type, link) { } // Returns a promise. -exports.update = function(projectPath, options, events) { +exports.update = function (projectPath, options, events) { options = options || {}; return Q() - .then(function() { + .then(function () { var isAndroidStudio = AndroidStudio.isAndroidStudioProject(projectPath); var isLegacy = !isAndroidStudio; @@ -353,15 +352,15 @@ exports.update = function(projectPath, options, events) { builder = 'gradle'; } - if (Number(manifest.getMinSdkVersion()) < MIN_SDK_VERSION) { - events.emit('verbose', 'Updating minSdkVersion to ' + MIN_SDK_VERSION + ' in AndroidManifest.xml'); - manifest.setMinSdkVersion(MIN_SDK_VERSION); - } + if (Number(manifest.getMinSdkVersion()) < MIN_SDK_VERSION) { + events.emit('verbose', 'Updating minSdkVersion to ' + MIN_SDK_VERSION + ' in AndroidManifest.xml'); + manifest.setMinSdkVersion(MIN_SDK_VERSION); + } - manifest.setDebuggable(false).write(); + manifest.setDebuggable(false).write(); - var projectName = manifest.getActivity().getName(); - var target_api = check_reqs.get_target(); + var projectName = manifest.getActivity().getName(); + var target_api = check_reqs.get_target(); copyJsAndLibrary(projectPath, options.link, projectName, isLegacy); copyScripts(projectPath); @@ -372,7 +371,6 @@ exports.update = function(projectPath, options, events) { }).thenResolve(projectPath); }; - // For testing exports.validatePackageName = validatePackageName; exports.validateProjectName = validateProjectName; diff --git a/bin/templates/cordova/Api.js b/bin/templates/cordova/Api.js index 7a68903d..8ef1f339 100644 --- a/bin/templates/cordova/Api.js +++ b/bin/templates/cordova/Api.js @@ -29,8 +29,7 @@ var selfEvents = require('cordova-common').events; var PLATFORM = 'android'; - -function setupEvents(externalEventEmitter) { +function setupEvents (externalEventEmitter) { if (externalEventEmitter) { // This will make the platform internal events visible outside selfEvents.forwardEventsTo(externalEventEmitter); @@ -43,7 +42,6 @@ function setupEvents(externalEventEmitter) { return selfEvents; } - /** * Class, that acts as abstraction over particular platform. Encapsulates the * platform's properties and methods. @@ -55,7 +53,7 @@ function setupEvents(externalEventEmitter) { * * * platform: String that defines a platform name. */ -function Api(platform, platformRootDir, events) { +function Api (platform, platformRootDir, events) { this.platform = PLATFORM; this.root = path.resolve(__dirname, '..'); this.builder = 'gradle'; @@ -82,16 +80,16 @@ function Api(platform, platformRootDir, events) { // XXX Override some locations for Android Studio projects if(AndroidStudio.isAndroidStudioProject(self.root) === true) { - selfEvents.emit('log', 'Android Studio project detected'); - this.builder='studio'; - this.android_studio = true; - this.locations.configXml = path.join(self.root, 'app/src/main/res/xml/config.xml'); - this.locations.strings = path.join(self.root, 'app/src/main/res/values/strings.xml'); - this.locations.manifest = path.join(self.root, 'app/src/main/AndroidManifest.xml'); - //We could have Java Source, we could have other languages - this.locations.javaSrc = path.join(self.root, 'app/src/main/java/'); - this.locations.www = path.join(self.root, 'app/src/main/assets/www'); - this.locations.res = path.join(self.root, 'app/src/main/res'); + selfEvents.emit('log', 'Android Studio project detected'); + this.builder='studio'; + this.android_studio = true; + this.locations.configXml = path.join(self.root, 'app/src/main/res/xml/config.xml'); + this.locations.strings = path.join(self.root, 'app/src/main/res/values/strings.xml'); + this.locations.manifest = path.join(self.root, 'app/src/main/AndroidManifest.xml'); + //We could have Java Source, we could have other languages + this.locations.javaSrc = path.join(self.root, 'app/src/main/java/'); + this.locations.www = path.join(self.root, 'app/src/main/assets/www'); + this.locations.res = path.join(self.root, 'app/src/main/res'); } } @@ -117,16 +115,13 @@ Api.createPlatform = function (destination, config, options, events) { events = setupEvents(events); var result; try { - result = require('../../lib/create') - .create(destination, config, options, events) - .then(function (destination) { + result = require('../../lib/create').create(destination, config, options, events).then(function (destination) { var PlatformApi = require(path.resolve(destination, 'cordova/Api')); return new PlatformApi(PLATFORM, destination, events); }); - } - catch (e) { - events.emit('error','createPlatform is not callable from the android project API.'); - throw(e); + } catch (e) { + events.emit('error', 'createPlatform is not callable from the android project API.'); + throw (e); } return result; }; @@ -151,16 +146,13 @@ Api.updatePlatform = function (destination, options, events) { events = setupEvents(events); var result; try { - result = require('../../lib/create') - .update(destination, options, events) - .then(function (destination) { + result = require('../../lib/create').update(destination, options, events).then(function (destination) { var PlatformApi = require(path.resolve(destination, 'cordova/Api')); return new PlatformApi('android', destination, events); }); - } - catch (e) { - events.emit('error','updatePlatform is not callable from the android project API, you will need to do this manually.'); - throw(e); + } catch (e) { + events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.'); + throw (e); } return result; }; @@ -231,41 +223,35 @@ Api.prototype.addPlugin = function (plugin, installOptions) { installOptions.variables.PACKAGE_NAME = project.getPackageName(); } - if(this.android_studio === true) { - installOptions.android_studio = true; + if (this.android_studio === true) { + installOptions.android_studio = true; } - return Q() - .then(function () { - //CB-11964: Do a clean when installing the plugin code to get around - //the Gradle bug introduced by the Android Gradle Plugin Version 2.2 - //TODO: Delete when the next version of Android Gradle plugin comes out + return Q().then(function () { + // CB-11964: Do a clean when installing the plugin code to get around + // the Gradle bug introduced by the Android Gradle Plugin Version 2.2 + // TODO: Delete when the next version of Android Gradle plugin comes out + // Since clean doesn't just clean the build, it also wipes out www, we need + // to pass additional options. - // Since clean doesn't just clean the build, it also wipes out www, we need - // to pass additional options. + // Do some basic argument parsing + var opts = {}; - // Do some basic argument parsing - var opts = {}; + // Skip cleaning prepared files when not invoking via cordova CLI. + opts.noPrepare = true; - // Skip cleaning prepared files when not invoking via cordova CLI. - opts.noPrepare = true; - - if(!AndroidStudio.isAndroidStudioProject(self.root) && !project.isClean()) { - return self.clean(opts); - } - }) - .then(function () { - return PluginManager.get(self.platform, self.locations, project) - .addPlugin(plugin, installOptions); - }) - .then(function () { - if (plugin.getFrameworks(this.platform).length === 0) return; - - selfEvents.emit('verbose', 'Updating build files since android plugin contained '); - //This should pick the correct builder, not just get gradle - require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles(); - }.bind(this)) - // CB-11022 Return truthy value to prevent running prepare after + if (!AndroidStudio.isAndroidStudioProject(self.root) && !project.isClean()) { + return self.clean(opts); + } + }).then(function () { + return PluginManager.get(self.platform, self.locations, project).addPlugin(plugin, installOptions); + }).then(function () { + if (plugin.getFrameworks(this.platform).length === 0) return; + selfEvents.emit('verbose', 'Updating build files since android plugin contained '); + //This should pick the correct builder, not just get gradle + require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles(); + }.bind(this)) + // CB-11022 Return truthy value to prevent running prepare after .thenResolve(true); }; @@ -285,9 +271,9 @@ Api.prototype.addPlugin = function (plugin, installOptions) { Api.prototype.removePlugin = function (plugin, uninstallOptions) { var project = AndroidProject.getProjectFile(this.root); - if(uninstallOptions && uninstallOptions.usePlatformWww === true && this.android_studio === true) { - uninstallOptions.usePlatformWww = false; - uninstallOptions.android_studio = true; + if (uninstallOptions && uninstallOptions.usePlatformWww === true && this.android_studio === true) { + uninstallOptions.usePlatformWww = false; + uninstallOptions.android_studio = true; } return PluginManager.get(this.platform, this.locations, project) @@ -351,11 +337,9 @@ Api.prototype.build = function (buildOptions) { var self = this; if(this.android_studio) buildOptions.studio = true; - return require('./lib/check_reqs').run() - .then(function () { + return require('./lib/check_reqs').run() .then(function () { return require('./lib/build').run.call(self, buildOptions); - }) - .then(function (buildResults) { + }).then(function (buildResults) { // Cast build result to array of build artifacts return buildResults.apkPaths.map(function (apkPath) { return { @@ -380,10 +364,9 @@ Api.prototype.build = function (buildOptions) { * @return {Promise} A promise either fulfilled if package was built and ran * successfully, or rejected with CordovaError. */ -Api.prototype.run = function(runOptions) { +Api.prototype.run = function (runOptions) { var self = this; - return require('./lib/check_reqs').run() - .then(function () { + return require('./lib/check_reqs').run().then(function () { return require('./lib/run').run.call(self, runOptions); }); }; @@ -395,19 +378,15 @@ Api.prototype.run = function(runOptions) { * @return {Promise} Return a promise either fulfilled, or rejected with * CordovaError. */ -Api.prototype.clean = function(cleanOptions) { +Api.prototype.clean = function (cleanOptions) { var self = this; - return require('./lib/check_reqs').run() - .then(function () { - return require('./lib/build').runClean.call(self, cleanOptions); - }) - .then(function () { - return require('./lib/prepare').clean.call(self, cleanOptions); - }); + return require('./lib/check_reqs').run().then(function () { + return require('./lib/build').runClean.call(self, cleanOptions); + }).then(function () { + return require('./lib/prepare').clean.call(self, cleanOptions); + }); }; - - /** * Performs a requirements check for current platform. Each platform defines its * own set of requirements, which should be resolved before platform can be @@ -416,7 +395,7 @@ Api.prototype.clean = function(cleanOptions) { * @return {Promise} Promise, resolved with set of Requirement * objects for current platform. */ -Api.prototype.requirements = function() { +Api.prototype.requirements = function () { return require('./lib/check_reqs').check_all(); }; diff --git a/bin/templates/cordova/lib/Adb.js b/bin/templates/cordova/lib/Adb.js index 84ae707e..038c67c0 100644 --- a/bin/templates/cordova/lib/Adb.js +++ b/bin/templates/cordova/lib/Adb.js @@ -25,11 +25,11 @@ var CordovaError = require('cordova-common').CordovaError; var Adb = {}; -function isDevice(line) { +function isDevice (line) { return line.match(/\w+\tdevice/) && !line.match(/emulator/); } -function isEmulator(line) { +function isEmulator (line) { return line.match(/device/) && line.match(/emulator/); } @@ -44,8 +44,7 @@ function isEmulator(line) { * devices/emulators */ Adb.devices = function (opts) { - return spawn('adb', ['devices'], {cwd: os.tmpdir()}) - .then(function(output) { + return spawn('adb', ['devices'], {cwd: os.tmpdir()}).then(function (output) { return output.split('\n').filter(function (line) { // Filter out either real devices or emulators, depending on options return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line); @@ -59,8 +58,7 @@ Adb.install = function (target, packagePath, opts) { events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...'); var args = ['-s', target, 'install']; if (opts && opts.replace) args.push('-r'); - return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()}) - .then(function(output) { + return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()}).then(function (output) { // 'adb install' seems to always returns no error, even if installation fails // so we catching output to detect installation failure if (output.match(/Failure/)) { @@ -86,8 +84,7 @@ Adb.shell = function (target, shellCommand) { events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...'); var args = ['-s', target, 'shell']; shellCommand = shellCommand.split(/\s+/); - return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()}) - .catch(function (output) { + return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()}).catch(function (output) { return Q.reject(new CordovaError('Failed to execute shell command "' + shellCommand + '"" on device: ' + output)); }); @@ -95,8 +92,7 @@ Adb.shell = function (target, shellCommand) { Adb.start = function (target, activityName) { events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...'); - return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName) - .catch(function (output) { + return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName).catch(function (output) { return Q.reject(new CordovaError('Failed to start application "' + activityName + '"" on device: ' + output)); }); diff --git a/bin/templates/cordova/lib/AndroidManifest.js b/bin/templates/cordova/lib/AndroidManifest.js index 8248f593..5b7077ae 100644 --- a/bin/templates/cordova/lib/AndroidManifest.js +++ b/bin/templates/cordova/lib/AndroidManifest.js @@ -19,12 +19,12 @@ var fs = require('fs'); var et = require('elementtree'); -var xml= require('cordova-common').xmlHelpers; +var xml = require('cordova-common').xmlHelpers; var DEFAULT_ORIENTATION = 'default'; /** Wraps an AndroidManifest file */ -function AndroidManifest(path) { +function AndroidManifest (path) { this.path = path; this.doc = xml.parseElementtreeSync(path); if (this.doc.getroot().tag !== 'manifest') { @@ -32,38 +32,38 @@ function AndroidManifest(path) { } } -AndroidManifest.prototype.getVersionName = function() { +AndroidManifest.prototype.getVersionName = function () { return this.doc.getroot().attrib['android:versionName']; }; -AndroidManifest.prototype.setVersionName = function(versionName) { +AndroidManifest.prototype.setVersionName = function (versionName) { this.doc.getroot().attrib['android:versionName'] = versionName; return this; }; -AndroidManifest.prototype.getVersionCode = function() { +AndroidManifest.prototype.getVersionCode = function () { return this.doc.getroot().attrib['android:versionCode']; }; -AndroidManifest.prototype.setVersionCode = function(versionCode) { +AndroidManifest.prototype.setVersionCode = function (versionCode) { this.doc.getroot().attrib['android:versionCode'] = versionCode; return this; }; -AndroidManifest.prototype.getPackageId = function() { - /*jshint -W069 */ +AndroidManifest.prototype.getPackageId = function () { + /* jshint -W069 */ return this.doc.getroot().attrib['package']; - /*jshint +W069 */ + /* jshint +W069 */ }; -AndroidManifest.prototype.setPackageId = function(pkgId) { - /*jshint -W069 */ +AndroidManifest.prototype.setPackageId = function (pkgId) { + /* jshint -W069 */ this.doc.getroot().attrib['package'] = pkgId; - /*jshint +W069 */ + /* jshint +W069 */ return this; }; -AndroidManifest.prototype.getActivity = function() { +AndroidManifest.prototype.getActivity = function () { var activity = this.doc.getroot().find('./application/activity'); return { getName: function () { @@ -102,17 +102,16 @@ AndroidManifest.prototype.getActivity = function() { }; }; -['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'] -.forEach(function(sdkPrefName) { +['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'].forEach(function (sdkPrefName) { // Copy variable reference to avoid closure issues var prefName = sdkPrefName; - AndroidManifest.prototype['get' + capitalize(prefName)] = function() { + AndroidManifest.prototype['get' + capitalize(prefName)] = function () { var usesSdk = this.doc.getroot().find('./uses-sdk'); return usesSdk && usesSdk.attrib['android:' + prefName]; }; - AndroidManifest.prototype['set' + capitalize(prefName)] = function(prefValue) { + AndroidManifest.prototype['set' + capitalize(prefName)] = function (prefValue) { var usesSdk = this.doc.getroot().find('./uses-sdk'); if (!usesSdk && prefValue) { // if there is no required uses-sdk element, we should create it first @@ -128,11 +127,11 @@ AndroidManifest.prototype.getActivity = function() { }; }); -AndroidManifest.prototype.getDebuggable = function() { +AndroidManifest.prototype.getDebuggable = function () { return this.doc.getroot().find('./application').attrib['android:debuggable'] === 'true'; }; -AndroidManifest.prototype.setDebuggable = function(value) { +AndroidManifest.prototype.setDebuggable = function (value) { var application = this.doc.getroot().find('./application'); if (value) { application.attrib['android:debuggable'] = 'true'; @@ -150,7 +149,7 @@ AndroidManifest.prototype.setDebuggable = function(value) { * @param {String} [destPath] File to write manifest to. If omitted, * manifest will be written to file it has been read from. */ -AndroidManifest.prototype.write = function(destPath) { +AndroidManifest.prototype.write = function (destPath) { fs.writeFileSync(destPath || this.path, this.doc.write({indent: 4}), 'utf-8'); }; diff --git a/bin/templates/cordova/lib/AndroidProject.js b/bin/templates/cordova/lib/AndroidProject.js index fa1c6129..bf55cad1 100644 --- a/bin/templates/cordova/lib/AndroidProject.js +++ b/bin/templates/cordova/lib/AndroidProject.js @@ -26,16 +26,15 @@ var pluginHandlers = require('./pluginHandlers'); var projectFileCache = {}; -function addToPropertyList(projectProperties, key, value) { +function addToPropertyList (projectProperties, key, value) { var i = 1; - while (projectProperties.get(key + '.' + i)) - i++; + while (projectProperties.get(key + '.' + i)) { i++; } projectProperties.set(key + '.' + i, value); projectProperties.dirty = true; } -function removeFromPropertyList(projectProperties, key, value) { +function removeFromPropertyList (projectProperties, key, value) { var i = 1; var currentValue; while ((currentValue = projectProperties.get(key + '.' + i))) { @@ -54,18 +53,18 @@ function removeFromPropertyList(projectProperties, key, value) { function getRelativeLibraryPath (parentDir, subDir) { var libraryPath = path.relative(parentDir, subDir); - return (path.sep == '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath; + return (path.sep === '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath; } -function AndroidProject(projectDir) { +function AndroidProject (projectDir) { this._propertiesEditors = {}; this._subProjectDirs = {}; this._dirty = false; this.projectDir = projectDir; this.platformWww = path.join(this.projectDir, 'platform_www'); this.www = path.join(this.projectDir, 'assets/www'); - if(AndroidStudio.isAndroidStudioProject(projectDir) === true) { - this.www = path.join(this.projectDir, 'app/src/main/assets/www'); + if (AndroidStudio.isAndroidStudioProject(projectDir) === true) { + this.www = path.join(this.projectDir, 'app/src/main/assets/www'); } } @@ -92,15 +91,15 @@ AndroidProject.purgeCache = function (projectDir) { * * @return {String} The name of the package */ -AndroidProject.prototype.getPackageName = function() { +AndroidProject.prototype.getPackageName = function () { var manifestPath = path.join(this.projectDir, 'AndroidManifest.xml'); - if(AndroidStudio.isAndroidStudioProject(this.projectDir) === true) { - manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml'); + if (AndroidStudio.isAndroidStudioProject(this.projectDir) === true) { + manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml'); } return new AndroidManifest(manifestPath).getPackageId(); }; -AndroidProject.prototype.getCustomSubprojectRelativeDir = function(plugin_id, src) { +AndroidProject.prototype.getCustomSubprojectRelativeDir = function (plugin_id, src) { // All custom subprojects are prefixed with the last portion of the package id. // This is to avoid collisions when opening multiple projects in Eclipse that have subprojects with the same name. var packageName = this.getPackageName(); @@ -110,7 +109,7 @@ AndroidProject.prototype.getCustomSubprojectRelativeDir = function(plugin_id, sr return subRelativeDir; }; -AndroidProject.prototype.addSubProject = function(parentDir, subDir) { +AndroidProject.prototype.addSubProject = function (parentDir, subDir) { var parentProjectFile = path.resolve(parentDir, 'project.properties'); var subProjectFile = path.resolve(subDir, 'project.properties'); var parentProperties = this._getPropertiesFile(parentProjectFile); @@ -126,7 +125,7 @@ AndroidProject.prototype.addSubProject = function(parentDir, subDir) { this._dirty = true; }; -AndroidProject.prototype.removeSubProject = function(parentDir, subDir) { +AndroidProject.prototype.removeSubProject = function (parentDir, subDir) { var parentProjectFile = path.resolve(parentDir, 'project.properties'); var parentProperties = this._getPropertiesFile(parentProjectFile); removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir)); @@ -134,35 +133,35 @@ AndroidProject.prototype.removeSubProject = function(parentDir, subDir) { this._dirty = true; }; -AndroidProject.prototype.addGradleReference = function(parentDir, subDir) { +AndroidProject.prototype.addGradleReference = function (parentDir, subDir) { var parentProjectFile = path.resolve(parentDir, 'project.properties'); var parentProperties = this._getPropertiesFile(parentProjectFile); addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir)); this._dirty = true; }; -AndroidProject.prototype.removeGradleReference = function(parentDir, subDir) { +AndroidProject.prototype.removeGradleReference = function (parentDir, subDir) { var parentProjectFile = path.resolve(parentDir, 'project.properties'); var parentProperties = this._getPropertiesFile(parentProjectFile); removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir)); this._dirty = true; }; -AndroidProject.prototype.addSystemLibrary = function(parentDir, value) { +AndroidProject.prototype.addSystemLibrary = function (parentDir, value) { var parentProjectFile = path.resolve(parentDir, 'project.properties'); var parentProperties = this._getPropertiesFile(parentProjectFile); addToPropertyList(parentProperties, 'cordova.system.library', value); this._dirty = true; }; -AndroidProject.prototype.removeSystemLibrary = function(parentDir, value) { +AndroidProject.prototype.removeSystemLibrary = function (parentDir, value) { var parentProjectFile = path.resolve(parentDir, 'project.properties'); var parentProperties = this._getPropertiesFile(parentProjectFile); removeFromPropertyList(parentProperties, 'cordova.system.library', value); this._dirty = true; }; -AndroidProject.prototype.write = function() { +AndroidProject.prototype.write = function () { if (!this._dirty) { return; } @@ -201,9 +200,9 @@ AndroidProject.prototype.getUninstaller = function (type) { * This checks if an Android project is clean or has old build artifacts */ -AndroidProject.prototype.isClean = function() { +AndroidProject.prototype.isClean = function () { var build_path = path.join(this.projectDir, 'build'); - //If the build directory doesn't exist, it's clean + // If the build directory doesn't exist, it's clean return !(fs.existsSync(build_path)); }; diff --git a/bin/templates/cordova/lib/AndroidStudio.js b/bin/templates/cordova/lib/AndroidStudio.js index ee4cd3fe..3b015640 100644 --- a/bin/templates/cordova/lib/AndroidStudio.js +++ b/bin/templates/cordova/lib/AndroidStudio.js @@ -4,7 +4,7 @@ * @param {String} root Root folder of the project */ -/*jshint esnext: false */ +/* jshint esnext: false */ var path = require('path'); var fs = require('fs'); @@ -18,21 +18,21 @@ module.exports.isAndroidStudioProject = function isAndroidStudioProject(root) { var isEclipse = false; var isAS = true; - if(!fs.existsSync(root)) { + if (!fs.existsSync(root)) { throw new CordovaError('AndroidStudio.js:inAndroidStudioProject root does not exist: ' + root); } // if any of the following exists, then we are not an ASProj - eclipseFiles.forEach(function(file) { - if(fs.existsSync(path.join(root, file))) { + eclipseFiles.forEach(function (file) { + if (fs.existsSync(path.join(root, file))) { isEclipse = true; } }); // if it is NOT an eclipse project, check that all required files exist - if(!isEclipse) { - androidStudioFiles.forEach(function(file){ - if(!fs.existsSync(path.join(root, file))) { + if (!isEclipse) { + androidStudioFiles.forEach(function (file) { + if (!fs.existsSync(path.join(root, file))) { console.log('missing file :: ' + file); isAS = false; } diff --git a/bin/templates/cordova/lib/android_sdk.js b/bin/templates/cordova/lib/android_sdk.js index a1a806a6..ca8f9e33 100755 --- a/bin/templates/cordova/lib/android_sdk.js +++ b/bin/templates/cordova/lib/android_sdk.js @@ -17,8 +17,8 @@ under the License. */ -var Q = require('q'), - superspawn = require('cordova-common').superspawn; +var Q = require('q'); +var superspawn = require('cordova-common').superspawn; var suffix_number_regex = /(\d+)$/; // Used for sorting Android targets, example strings to sort: @@ -29,7 +29,7 @@ var suffix_number_regex = /(\d+)$/; // The idea is to sort based on largest "suffix" number - meaning the bigger // the number at the end, the more recent the target, the closer to the // start of the array. -function sort_by_largest_numerical_suffix(a, b) { +function sort_by_largest_numerical_suffix (a, b) { var suffix_a = a.match(suffix_number_regex); var suffix_b = b.match(suffix_number_regex); if (suffix_a && suffix_b) { @@ -43,9 +43,8 @@ function sort_by_largest_numerical_suffix(a, b) { } } -module.exports.print_newest_available_sdk_target = function() { - return module.exports.list_targets() - .then(function(targets) { +module.exports.print_newest_available_sdk_target = function () { + return module.exports.list_targets().then(function (targets) { targets.sort(sort_by_largest_numerical_suffix); console.log(targets[0]); }); @@ -66,38 +65,34 @@ module.exports.version_string_to_api_level = { '7.1.1': 25 }; -function parse_targets(output) { +function parse_targets (output) { var target_out = output.split('\n'); var targets = []; for (var i = target_out.length - 1; i >= 0; i--) { - if(target_out[i].match(/id:/)) { // if "id:" is in the line... - targets.push(target_out[i].match(/"(.+)"/)[1]); //.. match whatever is in quotes. + if (target_out[i].match(/id:/)) { // if "id:" is in the line... + targets.push(target_out[i].match(/"(.+)"/)[1]); // .. match whatever is in quotes. } } return targets; } -module.exports.list_targets_with_android = function() { - return superspawn.spawn('android', ['list', 'target']) - .then(parse_targets); +module.exports.list_targets_with_android = function () { + return superspawn.spawn('android', ['list', 'target']).then(parse_targets); }; -module.exports.list_targets_with_avdmanager = function() { - return superspawn.spawn('avdmanager', ['list', 'target']) - .then(parse_targets); +module.exports.list_targets_with_avdmanager = function () { + return superspawn.spawn('avdmanager', ['list', 'target']).then(parse_targets); }; -module.exports.list_targets = function() { - return module.exports.list_targets_with_avdmanager() - .catch(function(err) { +module.exports.list_targets = function () { + return module.exports.list_targets_with_avdmanager().catch(function (err) { // If there's an error, like avdmanager could not be found, we can try // as a last resort, to run `android`, in case this is a super old // SDK installation. - if (err && (err.code == 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) { + if (err && (err.code === 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) { return module.exports.list_targets_with_android(); } else throw err; - }) - .then(function(targets) { + }).then(function (targets) { if (targets.length === 0) { return Q.reject(new Error('No android targets (SDKs) installed!')); } diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js index 2fcf0b8e..b6d95f87 100644 --- a/bin/templates/cordova/lib/build.js +++ b/bin/templates/cordova/lib/build.js @@ -19,10 +19,10 @@ under the License. */ -var Q = require('q'), - path = require('path'), - fs = require('fs'), - nopt = require('nopt'); +var Q = require('q'); +var path = require('path'); +var fs = require('fs'); +var nopt = require('nopt'); var Adb = require('./Adb'); @@ -31,7 +31,7 @@ var events = require('cordova-common').events; var spawn = require('cordova-common').superspawn.spawn; var CordovaError = require('cordova-common').CordovaError; -function parseOpts(options, resolvedTarget, projectRoot) { +function parseOpts (options, resolvedTarget, projectRoot) { options = options || {}; options.argv = nopt({ gradle: Boolean, @@ -64,11 +64,9 @@ function parseOpts(options, resolvedTarget, projectRoot) { if (options.nobuild) ret.buildMethod = 'none'; - if (options.argv.versionCode) - ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode); + if (options.argv.versionCode) { ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode); } - if (options.argv.minSdkVersion) - ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion); + if (options.argv.minSdkVersion) { ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion); } if (options.argv.gradleArg) { ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg); @@ -76,12 +74,10 @@ function parseOpts(options, resolvedTarget, projectRoot) { var packageArgs = {}; - if (options.argv.keystore) - packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore)); + if (options.argv.keystore) { packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore)); } - ['alias','storePassword','password','keystoreType'].forEach(function (flagName) { - if (options.argv[flagName]) - packageArgs[flagName] = options.argv[flagName]; + ['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (flagName) { + if (options.argv[flagName]) { packageArgs[flagName] = options.argv[flagName]; } }); var buildConfig = options.buildConfig; @@ -92,20 +88,20 @@ function parseOpts(options, resolvedTarget, projectRoot) { if (!fs.existsSync(buildConfig)) { throw new Error('Specified build config file does not exist: ' + buildConfig); } - events.emit('log', 'Reading build config file: '+ path.resolve(buildConfig)); + events.emit('log', 'Reading build config file: ' + path.resolve(buildConfig)); var buildjson = fs.readFileSync(buildConfig, 'utf8'); var config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM if (config.android && config.android[ret.buildType]) { var androidInfo = config.android[ret.buildType]; - if(androidInfo.keystore && !packageArgs.keystore) { - if(androidInfo.keystore.substr(0,1) === '~') { + if (androidInfo.keystore && !packageArgs.keystore) { + if (androidInfo.keystore.substr(0, 1) === '~') { androidInfo.keystore = process.env.HOME + androidInfo.keystore.substr(1); } packageArgs.keystore = path.resolve(path.dirname(buildConfig), androidInfo.keystore); events.emit('log', 'Reading the keystore from: ' + packageArgs.keystore); } - ['alias', 'storePassword', 'password','keystoreType'].forEach(function (key){ + ['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (key) { packageArgs[key] = packageArgs[key] || androidInfo[key]; }); } @@ -116,8 +112,8 @@ function parseOpts(options, resolvedTarget, projectRoot) { packageArgs.password, packageArgs.keystoreType); } - if(!ret.packageInfo) { - if(Object.keys(packageArgs).length > 0) { + if (!ret.packageInfo) { + if (Object.keys(packageArgs).length > 0) { events.emit('warn', '\'keystore\' and \'alias\' need to be specified to generate a signed archive.'); } } @@ -129,11 +125,10 @@ function parseOpts(options, resolvedTarget, projectRoot) { * Builds the project with the specifed options * Returns a promise. */ -module.exports.runClean = function(options) { +module.exports.runClean = function (options) { var opts = parseOpts(options, null, this.root); var builder = builders.getBuilder(opts.buildMethod); - return builder.prepEnv(opts) - .then(function() { + return builder.prepEnv(opts).then(function () { return builder.clean(opts); }); }; @@ -150,18 +145,16 @@ module.exports.runClean = function(options) { * @return {Promise} Promise, resolved with built packages * information. */ -module.exports.run = function(options, optResolvedTarget) { +module.exports.run = function (options, optResolvedTarget) { var opts = parseOpts(options, optResolvedTarget, this.root); console.log(opts.buildMethod); var builder = builders.getBuilder(opts.buildMethod); - return builder.prepEnv(opts) - .then(function() { + return builder.prepEnv(opts).then(function () { if (opts.prepEnv) { events.emit('verbose', 'Build file successfully prepared.'); return; } - return builder.build(opts) - .then(function() { + return builder.build(opts).then(function () { var apkPaths = builder.findOutputApks(opts.buildType, opts.arch); events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t')); return { @@ -177,38 +170,31 @@ module.exports.run = function(options, optResolvedTarget) { * Detects the architecture of a device/emulator * Returns "arm" or "x86". */ -module.exports.detectArchitecture = function(target) { - function helper() { - return Adb.shell(target, 'cat /proc/cpuinfo') - .then(function(output) { +module.exports.detectArchitecture = function (target) { + function helper () { + return Adb.shell(target, 'cat /proc/cpuinfo').then(function (output) { return /intel/i.exec(output) ? 'x86' : 'arm'; }); } // It sometimes happens (at least on OS X), that this command will hang forever. // To fix it, either unplug & replug device, or restart adb server. - return helper() - .timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.')) - .then(null, function(err) { + return helper().timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.')).then(null, function (err) { if (/timed out/.exec('' + err)) { // adb kill-server doesn't seem to do the trick. // Could probably find a x-platform version of killall, but I'm not actually // sure that this scenario even happens on non-OSX machines. events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.'); - return spawn('killall', ['adb']) - .then(function() { - return helper() - .then(null, function() { + return spawn('killall', ['adb']).then(function () { + return helper().then(null, function () { // The double kill is sadly often necessary, at least on mac. events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.'); - return spawn('killall', ['adb']) - .then(function() { - return helper() - .then(null, function() { + return spawn('killall', ['adb']).then(function () { + return helper().then(null, function () { return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.')); }); }); }); - }, function() { + }, function () { // For non-killall OS's. return Q.reject(err); }); @@ -217,10 +203,10 @@ module.exports.detectArchitecture = function(target) { }); }; -module.exports.findBestApkForArchitecture = function(buildResults, arch) { - var paths = buildResults.apkPaths.filter(function(p) { +module.exports.findBestApkForArchitecture = function (buildResults, arch) { + var paths = buildResults.apkPaths.filter(function (p) { var apkName = path.basename(p); - if (buildResults.buildType == 'debug') { + if (buildResults.buildType === 'debug') { return /-debug/.exec(apkName); } return !/-debug/.exec(apkName); @@ -240,7 +226,7 @@ module.exports.findBestApkForArchitecture = function(buildResults, arch) { throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType); }; -function PackageInfo(keystore, alias, storePassword, password, keystoreType) { +function PackageInfo (keystore, alias, storePassword, password, keystoreType) { this.keystore = { 'name': 'key.store', 'value': keystore @@ -270,10 +256,10 @@ function PackageInfo(keystore, alias, storePassword, password, keystoreType) { } PackageInfo.prototype = { - toProperties: function() { + toProperties: function () { var self = this; var result = ''; - Object.keys(self).forEach(function(key) { + Object.keys(self).forEach(function (key) { result += self[key].name; result += '='; result += self[key].value.replace(/\\/g, '\\\\'); @@ -283,7 +269,7 @@ PackageInfo.prototype = { } }; -module.exports.help = function() { +module.exports.help = function () { console.log('Usage: ' + path.relative(process.cwd(), path.join('../build')) + ' [flags] [Signed APK flags]'); console.log('Flags:'); console.log(' \'--debug\': will build project in debug mode (default)'); diff --git a/bin/templates/cordova/lib/builders/GenericBuilder.js b/bin/templates/cordova/lib/builders/GenericBuilder.js index 9b5d07ac..eb431e85 100644 --- a/bin/templates/cordova/lib/builders/GenericBuilder.js +++ b/bin/templates/cordova/lib/builders/GenericBuilder.js @@ -16,6 +16,8 @@ specific language governing permissions and limitations under the License. */ +/* eslint no-self-assign: 0 */ +/* eslint no-unused-vars: 0 */ var Q = require('q'); var fs = require('fs'); @@ -35,29 +37,27 @@ GenericBuilder.prototype.prepEnv = function() { return Q(); }; -GenericBuilder.prototype.build = function() { +GenericBuilder.prototype.build = function () { events.emit('log', 'Skipping build...'); return Q(null); }; -GenericBuilder.prototype.clean = function() { +GenericBuilder.prototype.clean = function () { return Q(); }; -GenericBuilder.prototype.findOutputApks = function(build_type, arch) { +GenericBuilder.prototype.findOutputApks = function (build_type, arch) { var self = this; - return Object.keys(this.binDirs) - .reduce(function (result, builderName) { + return Object.keys(this.binDirs) .reduce(function (result, builderName) { console.log('builderName:'+ builderName); var binDir = self.binDirs[builderName]; return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch)); - }, []) - .sort(apkSorter); + }, []).sort(apkSorter); }; module.exports = GenericBuilder; -function apkSorter(fileA, fileB) { +function apkSorter (fileA, fileB) { // De-prioritize unsigned builds var unsignedRE = /-unsigned/; if (unsignedRE.exec(fileA)) { @@ -70,12 +70,11 @@ function apkSorter(fileA, fileB) { return timeDiff === 0 ? fileA.length - fileB.length : timeDiff; } -function findOutputApksHelper(dir, build_type, arch) { +function findOutputApksHelper (dir, build_type, arch) { var shellSilent = shell.config.silent; shell.config.silent = true; - var ret = shell.ls(path.join(dir, '*.apk')) - .filter(function(candidate) { + var ret = shell.ls(path.join(dir, '*.apk')).filter(function (candidate) { var apkName = path.basename(candidate); // Need to choose between release and debug .apk. if (build_type === 'debug') { @@ -85,8 +84,7 @@ function findOutputApksHelper(dir, build_type, arch) { return /-release/.exec(apkName) && !/-unaligned/.exec(apkName); } return true; - }) - .sort(apkSorter); + }).sort(apkSorter); shellSilent = shellSilent; @@ -96,15 +94,15 @@ function findOutputApksHelper(dir, build_type, arch) { // Assume arch-specific build if newest apk has -x86 or -arm. var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0])); // And show only arch-specific ones (or non-arch-specific) - ret = ret.filter(function(p) { - /*jshint -W018 */ - return !!/-x86|-arm/.exec(path.basename(p)) == archSpecific; - /*jshint +W018 */ + ret = ret.filter(function (p) { + /* jshint -W018 */ + return !!/-x86|-arm/.exec(path.basename(p)) === archSpecific; + /* jshint +W018 */ }); if (archSpecific && ret.length > 1 && arch) { - ret = ret.filter(function(p) { - return path.basename(p).indexOf('-' + arch) != -1; + ret = ret.filter(function (p) { + return path.basename(p).indexOf('-' + arch) !== -1; }); } diff --git a/bin/templates/cordova/lib/builders/GradleBuilder.js b/bin/templates/cordova/lib/builders/GradleBuilder.js index 8357055f..716b1f91 100644 --- a/bin/templates/cordova/lib/builders/GradleBuilder.js +++ b/bin/templates/cordova/lib/builders/GradleBuilder.js @@ -42,10 +42,10 @@ function GradleBuilder (projectRoot) { util.inherits(GradleBuilder, GenericBuilder); -GradleBuilder.prototype.getArgs = function(cmd, opts) { - if (cmd == 'release') { +GradleBuilder.prototype.getArgs = function (cmd, opts) { + if (cmd === 'release') { cmd = 'cdvBuildRelease'; - } else if (cmd == 'debug') { + } else if (cmd === 'debug') { cmd = 'cdvBuildDebug'; } var args = [cmd, '-b', path.join(this.root, 'build.gradle')]; @@ -69,13 +69,13 @@ GradleBuilder.prototype.getArgs = function(cmd, opts) { * This returns a promise */ -GradleBuilder.prototype.runGradleWrapper = function(gradle_cmd) { +GradleBuilder.prototype.runGradleWrapper = function (gradle_cmd) { var gradlePath = path.join(this.root, 'gradlew'); var wrapperGradle = path.join(this.root, 'wrapper.gradle'); - if(fs.existsSync(gradlePath)) { - //Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows + if (fs.existsSync(gradlePath)) { + // Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows } else { - return superspawn.spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'}); + return superspawn.spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'}); } }; @@ -116,7 +116,7 @@ GradleBuilder.prototype.extractRealProjectNameFromManifest = function () { // Makes the project buildable, minus the gradle wrapper. -GradleBuilder.prototype.prepBuildFiles = function() { +GradleBuilder.prototype.prepBuildFiles = function () { // Update the version of build.gradle in each dependent library. var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle'); var propertiesObj = this.readProjectProperties(); @@ -125,33 +125,32 @@ GradleBuilder.prototype.prepBuildFiles = function() { // Check and copy the gradle file into the subproject. // Called by the loop below this function def. var checkAndCopy = function(subProject, root) { - var subProjectGradle = path.join(root, subProject, 'build.gradle'); - // This is the future-proof way of checking if a file exists - // This must be synchronous to satisfy a Travis test - try { - fs.accessSync(subProjectGradle, fs.F_OK); - } catch (e) { - shell.cp('-f', pluginBuildGradle, subProjectGradle); - } + var subProjectGradle = path.join(root, subProject, 'build.gradle'); + // This is the future-proof way of checking if a file exists + // This must be synchronous to satisfy a Travis test + try { + fs.accessSync(subProjectGradle, fs.F_OK); + } catch (e) { + shell.cp('-f', pluginBuildGradle, subProjectGradle); + } }; // Some dependencies on Android don't use gradle, or don't have default // gradle files. This copies a dummy gradle file into them for (var i = 0; i < subProjects.length; ++i) { if (subProjects[i] !== 'CordovaLib' && subProjects[i] !== 'app') { - checkAndCopy(subProjects[i], this.root); + checkAndCopy(subProjects[i], this.root); } } var name = this.extractRealProjectNameFromManifest(); - //Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149 - var settingsGradlePaths = subProjects.map(function(p){ - var realDir=p.replace(/[/\\]/g, ':'); - var libName=realDir.replace(name+'-',''); - var str='include ":'+libName+'"\n'; - if(realDir.indexOf(name+'-')!==-1) - str+='project(":'+libName+'").projectDir = new File("'+p+'")\n'; + // Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149 + var settingsGradlePaths = subProjects.map(function (p) { + var realDir = p.replace(/[/\\]/g, ':'); + var libName = realDir.replace(name + '-', ''); + var str = 'include ":' + libName + '"\n'; + if (realDir.indexOf(name + '-') !== -1) { str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n'; } return str; }); @@ -169,19 +168,18 @@ GradleBuilder.prototype.prepBuildFiles = function() { // dependency. Because we need to make sure that Cordova is compiled only once for // dexing, we make sure to exclude CordovaLib from these modules var insertExclude = function(p) { - var gradlePath = path.join(root, p, 'build.gradle'); - var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8'); - if(projectGradleFile.indexOf('CordovaLib') != -1) { + var gradlePath = path.join(root, p, 'build.gradle'); + var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8'); + if(projectGradleFile.indexOf('CordovaLib') != -1) { depsList += '{\n exclude module:("CordovaLib")\n }\n'; - } - else { - depsList +='\n'; - } + } else { + depsList += '\n'; + } }; - subProjects.forEach(function(p) { + subProjects.forEach(function (p) { console.log('Subproject Path: ' + p); - var libName=p.replace(/[/\\]/g, ':').replace(name+'-',''); + var libName = p.replace(/[/\\]/g, ':').replace(name + '-', ''); depsList += ' debugCompile(project(path: "' + libName + '", configuration: "debug"))'; insertExclude(p); depsList += ' releaseCompile(project(path: "' + libName + '", configuration: "release"))'; @@ -194,8 +192,7 @@ GradleBuilder.prototype.prepBuildFiles = function() { [/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'], [/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+'] ]; - - propertiesObj.systemLibs.forEach(function(p) { + propertiesObj.systemLibs.forEach(function (p) { var mavenRef; // It's already in gradle form if it has two ':'s if (/:.*:/.exec(p)) { @@ -219,21 +216,20 @@ GradleBuilder.prototype.prepBuildFiles = function() { //Try not to mess with this if possible buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2'); var includeList = ''; - propertiesObj.gradleIncludes.forEach(function(includePath) { + propertiesObj.gradleIncludes.forEach(function (includePath) { includeList += 'apply from: "' + includePath + '"\n'; }); buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2'); fs.writeFileSync(path.join(this.root, 'build.gradle'), buildGradle); }; -GradleBuilder.prototype.prepEnv = function(opts) { +GradleBuilder.prototype.prepEnv = function (opts) { var self = this; - return check_reqs.check_gradle() - .then(function(gradlePath) { + return check_reqs.check_gradle().then(function (gradlePath) { return self.runGradleWrapper(gradlePath); - }).then(function() { - return self.prepBuildFiles(); - }).then(function() { + }).then(function () { + return self.prepBuildFiles(); + }).then(function () { // We now copy the gradle out of the framework // This is a dirty patch to get the build working /* @@ -253,12 +249,12 @@ GradleBuilder.prototype.prepEnv = function(opts) { // If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with. // For some reason, using ^ and $ don't work. This does the job, though. var distributionUrlRegex = /distributionUrl.*zip/; - /*jshint -W069 */ + /* jshint -W069 */ var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-3.3-all.zip'; - /*jshint +W069 */ + /* jshint +W069 */ var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties'); shell.chmod('u+w', gradleWrapperPropertiesPath); - shell.sed('-i', distributionUrlRegex, 'distributionUrl='+distributionUrl, gradleWrapperPropertiesPath); + shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath); var propertiesFile = opts.buildType + SIGNING_PROPERTIES; var propertiesFilePath = path.join(self.root, propertiesFile); @@ -274,12 +270,11 @@ GradleBuilder.prototype.prepEnv = function(opts) { * Builds the project with gradle. * Returns a promise. */ -GradleBuilder.prototype.build = function(opts) { +GradleBuilder.prototype.build = function (opts) { var wrapper = path.join(this.root, 'gradlew'); - var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts); + var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts); - return superspawn.spawn(wrapper, args, {stdio: 'pipe'}) - .progress(function (stdio){ + return superspawn.spawn(wrapper, args, {stdio: 'pipe'}).progress(function (stdio) { if (stdio.stderr) { /* * Workaround for the issue with Java printing some unwanted information to @@ -298,7 +293,7 @@ GradleBuilder.prototype.build = function(opts) { } }).catch(function (error) { if (error.toString().indexOf('failed to find target with hash string') >= 0) { - return check_reqs.check_android_target(error).then(function() { + return check_reqs.check_android_target(error).then(function () { // If due to some odd reason - check_android_target succeeds // we should still fail here. return Q.reject(error); @@ -308,19 +303,18 @@ GradleBuilder.prototype.build = function(opts) { }); }; -GradleBuilder.prototype.clean = function(opts) { +GradleBuilder.prototype.clean = function (opts) { var builder = this; var wrapper = path.join(this.root, 'gradlew'); var args = builder.getArgs('clean', opts); - return Q().then(function() { + return Q().then(function () { return superspawn.spawn(wrapper, args, {stdio: 'inherit'}); - }) - .then(function () { + }).then(function () { shell.rm('-rf', path.join(builder.root, 'out')); - ['debug', 'release'].forEach(function(config) { + ['debug', 'release'].forEach(function (config) { var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES); - if(isAutoGenerated(propertiesFilePath)){ + if (isAutoGenerated(propertiesFilePath)) { shell.rm('-f', propertiesFilePath); } }); @@ -329,6 +323,6 @@ GradleBuilder.prototype.clean = function(opts) { module.exports = GradleBuilder; -function isAutoGenerated(file) { +function isAutoGenerated (file) { return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0; } diff --git a/bin/templates/cordova/lib/builders/builders.js b/bin/templates/cordova/lib/builders/builders.js index d0e06373..aedf9be5 100644 --- a/bin/templates/cordova/lib/builders/builders.js +++ b/bin/templates/cordova/lib/builders/builders.js @@ -35,8 +35,7 @@ var knownBuilders = { * @return {Builder} A builder instance for specified build type. */ module.exports.getBuilder = function (builderType, projectRoot) { - if (!knownBuilders[builderType]) - throw new CordovaError('Builder ' + builderType + ' is not supported.'); + if (!knownBuilders[builderType]) { throw new CordovaError('Builder ' + builderType + ' is not supported.'); } try { var Builder = require('./' + knownBuilders[builderType]); diff --git a/bin/templates/cordova/lib/check_reqs.js b/bin/templates/cordova/lib/check_reqs.js index 1fd397ad..2c3f291d 100644 --- a/bin/templates/cordova/lib/check_reqs.js +++ b/bin/templates/cordova/lib/check_reqs.js @@ -21,19 +21,19 @@ /* jshint sub:true */ -var shelljs = require('shelljs'), - child_process = require('child_process'), - Q = require('q'), - path = require('path'), - fs = require('fs'), - os = require('os'), - REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'), - PROJECT_ROOT = path.join(__dirname, '..', '..'); +var shelljs = require('shelljs'); +var child_process = require('child_process'); +var Q = require('q'); +var path = require('path'); +var fs = require('fs'); +var os = require('os'); +var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'); +var PROJECT_ROOT = path.join(__dirname, '..', '..'); var CordovaError = require('cordova-common').CordovaError; var superspawn = require('cordova-common').superspawn; var android_sdk = require('./android_sdk'); -function forgivingWhichSync(cmd) { +function forgivingWhichSync (cmd) { try { return fs.realpathSync(shelljs.which(cmd)); } catch (e) { @@ -41,9 +41,9 @@ function forgivingWhichSync(cmd) { } } -function tryCommand(cmd, errMsg, catchStderr) { +function tryCommand (cmd, errMsg, catchStderr) { var d = Q.defer(); - child_process.exec(cmd, function(err, stdout, stderr) { + child_process.exec(cmd, function (err, stdout, stderr) { if (err) d.reject(new CordovaError(errMsg)); // Sometimes it is necessary to return an stderr instead of stdout in case of success, since // some commands prints theirs output to stderr instead of stdout. 'javac' is the example @@ -52,18 +52,18 @@ function tryCommand(cmd, errMsg, catchStderr) { return d.promise; } -module.exports.isWindows = function() { - return (os.platform() == 'win32'); +module.exports.isWindows = function () { + return (os.platform() === 'win32'); }; -module.exports.isDarwin = function() { - return (os.platform() == 'darwin'); +module.exports.isDarwin = function () { + return (os.platform() === 'darwin'); }; // Get valid target from framework/project.properties if run from this repo // Otherwise get target from project.properties file within a generated cordova-android project -module.exports.get_target = function() { - function extractFromFile(filePath) { +module.exports.get_target = function () { + function extractFromFile (filePath) { var target = shelljs.grep(/\btarget=/, filePath); if (!target) { throw new Error('Could not find android target within: ' + filePath); @@ -83,17 +83,18 @@ module.exports.get_target = function() { }; // Returns a promise. Called only by build and clean commands. -module.exports.check_ant = function() { - return superspawn.spawn('ant', ['-version']) - .then(function(output) { +module.exports.check_ant = function () { + return superspawn.spawn('ant', ['-version']).then(function (output) { // Parse Ant version from command output return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1]; - }).catch(function(err) { - throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.'); + }).catch(function (err) { + if (err) { + throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.'); + } }); }; -module.exports.get_gradle_wrapper = function() { +module.exports.get_gradle_wrapper = function () { var androidStudioPath; var i = 0; var foundStudio = false; @@ -102,58 +103,70 @@ module.exports.get_gradle_wrapper = function() { program_dir = fs.readdirSync('/Applications'); while (i < program_dir.length && !foundStudio) { if (program_dir[i].startsWith('Android Studio')) { - //TODO: Check for a specific Android Studio version, make sure it's not Canary + // TODO: Check for a specific Android Studio version, make sure it's not Canary androidStudioPath = path.join('/Applications', program_dir[i], 'Contents', 'gradle'); foundStudio = true; } else { ++i; } } } else if (module.exports.isWindows()) { - var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/'; - if (fs.existsSync(androidPath)) { - program_dir = fs.readdirSync(androidPath); - while (i < program_dir.length && !foundStudio) { - if (program_dir[i].startsWith('Android Studio')) { - foundStudio = true; - androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle'); - } else { ++i; } + + var result = child_process.spawnSync(path.join(__dirname, 'getASPath.bat')); + // console.log('result.stdout =' + result.stdout.toString()); + // console.log('result.stderr =' + result.stderr.toString()); + + if (result.stderr.toString().length > 0) { + var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/'; + if (fs.existsSync(androidPath)) { + program_dir = fs.readdirSync(androidPath); + while (i < program_dir.length && !foundStudio) { + if (program_dir[i].startsWith('Android Studio')) { + foundStudio = true; + androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle'); + } else { ++i; } + } } + } else { + // console.log('got android studio path from registry'); + // remove the (os independent) new line char at the end of stdout + // add gradle to match the above. + androidStudioPath = path.join(result.stdout.toString().split('\r\n')[0], 'gradle'); } } if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) { var dirs = fs.readdirSync(androidStudioPath); - if(dirs[0].split('-')[0] == 'gradle') { + if (dirs[0].split('-')[0] === 'gradle') { return path.join(androidStudioPath, dirs[0], 'bin', 'gradle'); } } else { - //OK, let's try to check for Gradle! + // OK, let's try to check for Gradle! return forgivingWhichSync('gradle'); } }; // Returns a promise. Called only by build and clean commands. -module.exports.check_gradle = function() { +module.exports.check_gradle = function () { var sdkDir = process.env['ANDROID_HOME']; var d = Q.defer(); - if (!sdkDir) + if (!sdkDir) { return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' + 'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.')); + } var gradlePath = module.exports.get_gradle_wrapper(); - if (gradlePath.length !== 0) - d.resolve(gradlePath); - else + if (gradlePath.length !== 0) { d.resolve(gradlePath); } else { d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' + 'or on your system to install the gradle wrapper. Please include gradle \n' + 'in your path, or install Android Studio')); + } return d.promise; }; // Returns a promise. -module.exports.check_java = function() { +module.exports.check_java = function () { var javacPath = forgivingWhichSync('javac'); var hasJavaHome = !!process.env['JAVA_HOME']; - return Q().then(function() { + return Q().then(function () { if (hasJavaHome) { // Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh). if (!javacPath) { @@ -165,11 +178,12 @@ module.exports.check_java = function() { var find_java = '/usr/libexec/java_home'; var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.'; if (fs.existsSync(find_java)) { - return superspawn.spawn(find_java) - .then(function(stdout) { + return superspawn.spawn(find_java).then(function (stdout) { process.env['JAVA_HOME'] = stdout.trim(); - }).catch(function(err) { - throw new CordovaError(default_java_error_msg); + }).catch(function (err) { + if (err) { + throw new CordovaError(default_java_error_msg); + } }); } else { // See if we can derive it from javac's location. @@ -200,7 +214,7 @@ module.exports.check_java = function() { } } } - }).then(function() { + }).then(function () { var msg = 'Failed to run "javac -version", make sure that you have a JDK installed.\n' + 'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n'; @@ -209,9 +223,8 @@ module.exports.check_java = function() { } // We use tryCommand with catchStderr = true, because // javac writes version info to stderr instead of stdout - return tryCommand('javac -version', msg, true) - .then(function (output) { - //Let's check for at least Java 8, and keep it future proof so we can support Java 10 + return tryCommand('javac -version', msg, true).then(function (output) { + // Let's check for at least Java 8, and keep it future proof so we can support Java 10 var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output); return match && match[1]; }); @@ -219,13 +232,13 @@ module.exports.check_java = function() { }; // Returns a promise. -module.exports.check_android = function() { - return Q().then(function() { +module.exports.check_android = function () { + return Q().then(function () { var androidCmdPath = forgivingWhichSync('android'); var adbInPath = forgivingWhichSync('adb'); var avdmanagerInPath = forgivingWhichSync('avdmanager'); var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']); - function maybeSetAndroidHome(value) { + function maybeSetAndroidHome (value) { if (!hasAndroidHome && fs.existsSync(value)) { hasAndroidHome = true; process.env['ANDROID_HOME'] = value; @@ -265,7 +278,7 @@ module.exports.check_android = function() { if (androidCmdPath) { parentDir = path.dirname(androidCmdPath); grandParentDir = path.dirname(parentDir); - if (path.basename(parentDir) == 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) { + if (path.basename(parentDir) === 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) { maybeSetAndroidHome(grandParentDir); } else { throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' + @@ -276,7 +289,7 @@ module.exports.check_android = function() { if (adbInPath) { parentDir = path.dirname(adbInPath); grandParentDir = path.dirname(parentDir); - if (path.basename(parentDir) == 'platform-tools') { + if (path.basename(parentDir) === 'platform-tools') { maybeSetAndroidHome(grandParentDir); } else { throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' + @@ -287,7 +300,7 @@ module.exports.check_android = function() { if (avdmanagerInPath) { parentDir = path.dirname(avdmanagerInPath); grandParentDir = path.dirname(parentDir); - if (path.basename(parentDir) == 'bin' && path.basename(grandParentDir) == 'tools') { + if (path.basename(parentDir) === 'bin' && path.basename(grandParentDir) === 'tools') { maybeSetAndroidHome(path.dirname(grandParentDir)); } else { throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' + @@ -330,20 +343,19 @@ module.exports.getAbsoluteAndroidCmd = function () { return cmd.replace(/(\s)/g, '\\$1'); }; -module.exports.check_android_target = function(originalError) { +module.exports.check_android_target = function (originalError) { // valid_target can look like: // android-19 // android-L // Google Inc.:Google APIs:20 // Google Inc.:Glass Development Kit Preview:20 var desired_api_level = module.exports.get_target(); - return android_sdk.list_targets() - .then(function(targets) { + return android_sdk.list_targets().then(function (targets) { if (targets.indexOf(desired_api_level) >= 0) { return targets; } var androidCmd = module.exports.getAbsoluteAndroidCmd(); - var msg = 'Please install Android target / API level: "' + desired_api_level + '".\n\n' + + var msg = 'Please install Android target / API level: "' + desired_api_level + '".\n\n' + 'Hint: Open the SDK manager by running: ' + androidCmd + '\n' + 'You will require:\n' + '1. "SDK Platform" for API level ' + desired_api_level + '\n' + @@ -357,23 +369,21 @@ module.exports.check_android_target = function(originalError) { }; // Returns a promise. -module.exports.run = function() { - return Q.all([this.check_java(), this.check_android()]) - .then(function(values) { - console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']); - console.log('JAVA_HOME=' + process.env['JAVA_HOME']); +module.exports.run = function () { + return Q.all([this.check_java(), this.check_android()]).then(function (values) { + console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']); + console.log('JAVA_HOME=' + process.env['JAVA_HOME']); - if (!values[0]) { + if (!values[0]) { throw new CordovaError('Requirements check failed for JDK 1.8 or greater'); - } + } - if (!values[1]) { + if (!values[1]) { throw new CordovaError('Requirements check failed for Android SDK'); - } - }); + } + }); }; - /** * Object thar represents one of requirements for current platform. * @param {String} id The unique identifier for this requirements. @@ -387,7 +397,7 @@ var Requirement = function (id, name, version, installed) { this.name = name; this.installed = installed || false; this.metadata = { - version: version, + version: version }; }; @@ -397,7 +407,7 @@ var Requirement = function (id, name, version, installed) { * * @return Promise Array of requirements. Due to implementation, promise is always fulfilled. */ -module.exports.check_all = function() { +module.exports.check_all = function () { var requirements = [ new Requirement('java', 'Java JDK'), @@ -417,15 +427,13 @@ module.exports.check_all = function() { return checkFns.reduce(function (promise, checkFn, idx) { // Update each requirement with results var requirement = requirements[idx]; - return promise.then(checkFn) - .then(function (version) { + return promise.then(checkFn).then(function (version) { requirement.installed = true; requirement.metadata.version = version; }, function (err) { requirement.metadata.reason = err instanceof Error ? err.message : err; }); - }, Q()) - .then(function () { + }, Q()).then(function () { // When chain is completed, return requirements array to upstream API return requirements; }); diff --git a/bin/templates/cordova/lib/device.js b/bin/templates/cordova/lib/device.js index 47828f7f..84b50947 100644 --- a/bin/templates/cordova/lib/device.js +++ b/bin/templates/cordova/lib/device.js @@ -19,8 +19,8 @@ under the License. */ -var Q = require('q'), - build = require('./build'); +var Q = require('q'); +var build = require('./build'); var path = require('path'); var Adb = require('./Adb'); var AndroidManifest = require('./AndroidManifest'); @@ -32,18 +32,16 @@ var events = require('cordova-common').events; * Returns a promise for the list of the device ID's found * @param lookHarder When true, try restarting adb if no devices are found. */ -module.exports.list = function(lookHarder) { - return Adb.devices() - .then(function(list) { +module.exports.list = function (lookHarder) { + return Adb.devices().then(function (list) { if (list.length === 0 && lookHarder) { // adb kill-server doesn't seem to do the trick. // Could probably find a x-platform version of killall, but I'm not actually // sure that this scenario even happens on non-OSX machines. - return spawn('killall', ['adb']) - .then(function() { + return spawn('killall', ['adb']).then(function () { events.emit('verbose', 'Restarting adb to see if more devices are detected.'); return Adb.devices(); - }, function() { + }, function () { // For non-killall OS's. return list; }); @@ -52,9 +50,8 @@ module.exports.list = function(lookHarder) { }); }; -module.exports.resolveTarget = function(target) { - return this.list(true) - .then(function(device_list) { +module.exports.resolveTarget = function (target) { + return this.list(true).then(function (device_list) { if (!device_list || !device_list.length) { return Q.reject(new CordovaError('Failed to deploy to device, no devices found.')); } @@ -65,8 +62,7 @@ module.exports.resolveTarget = function(target) { return Q.reject('ERROR: Unable to find target \'' + target + '\'.'); } - return build.detectArchitecture(target) - .then(function(arch) { + return build.detectArchitecture(target).then(function (arch) { return { target: target, arch: arch, isEmulator: false }; }); }); @@ -77,13 +73,13 @@ module.exports.resolveTarget = function(target) { * and launches it. * Returns a promise. */ -module.exports.install = function(target, buildResults) { - return Q().then(function() { - if (target && typeof target == 'object') { +module.exports.install = function (target, buildResults) { + return Q().then(function () { + if (target && typeof target === 'object') { return target; } return module.exports.resolveTarget(target); - }).then(function(resolvedTarget) { + }).then(function (resolvedTarget) { var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch); var manifest = new AndroidManifest(path.join(__dirname, '../../app/src/main/AndroidManifest.xml')); var pkgName = manifest.getPackageId(); @@ -91,29 +87,25 @@ module.exports.install = function(target, buildResults) { events.emit('log', 'Using apk: ' + apk_path); events.emit('log', 'Package name: ' + pkgName); - return Adb.install(resolvedTarget.target, apk_path, {replace: true}) - .catch(function (error) { + return Adb.install(resolvedTarget.target, apk_path, {replace: true}).catch(function (error) { // CB-9557 CB-10157 only uninstall and reinstall app if the one that // is already installed on device was signed w/different certificate - if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) - throw error; + if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; } events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' + 'installed app already signed with different key'); // This promise is always resolved, even if 'adb uninstall' fails to uninstall app // or the app doesn't installed at all, so no error catching needed. - return Adb.uninstall(resolvedTarget.target, pkgName) - .then(function() { + return Adb.uninstall(resolvedTarget.target, pkgName).then(function () { return Adb.install(resolvedTarget.target, apk_path, {replace: true}); }); - }) - .then(function() { - //unlock screen + }).then(function () { + // unlock screen return Adb.shell(resolvedTarget.target, 'input keyevent 82'); - }).then(function() { + }).then(function () { return Adb.start(resolvedTarget.target, launchName); - }).then(function() { + }).then(function () { events.emit('log', 'LAUNCH SUCCESS'); }); }); diff --git a/bin/templates/cordova/lib/emulator.js b/bin/templates/cordova/lib/emulator.js index 22209aa0..fbbe520a 100644 --- a/bin/templates/cordova/lib/emulator.js +++ b/bin/templates/cordova/lib/emulator.js @@ -21,8 +21,8 @@ /* jshint sub:true */ -var retry = require('./retry'); -var build = require('./build'); +var retry = require('./retry'); +var build = require('./build'); var path = require('path'); var Adb = require('./Adb'); var AndroidManifest = require('./AndroidManifest'); @@ -33,20 +33,20 @@ var shelljs = require('shelljs'); var android_sdk = require('./android_sdk'); var check_reqs = require('./check_reqs'); -var Q = require('q'); -var os = require('os'); -var fs = require('fs'); +var Q = require('q'); +var os = require('os'); +var fs = require('fs'); var child_process = require('child_process'); // constants -var ONE_SECOND = 1000; // in milliseconds -var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds +var ONE_SECOND = 1000; // in milliseconds +var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds var INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds -var NUM_INSTALL_RETRIES = 3; -var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds -var EXEC_KILL_SIGNAL = 'SIGKILL'; +var NUM_INSTALL_RETRIES = 3; +var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds +var EXEC_KILL_SIGNAL = 'SIGKILL'; -function forgivingWhichSync(cmd) { +function forgivingWhichSync (cmd) { try { return fs.realpathSync(shelljs.which(cmd)); } catch (e) { @@ -55,8 +55,7 @@ function forgivingWhichSync(cmd) { } module.exports.list_images_using_avdmanager = function () { - return superspawn.spawn('avdmanager', ['list', 'avd']) - .then(function(output) { + return superspawn.spawn('avdmanager', ['list', 'avd']).then(function (output) { var response = output.split('\n'); var emulator_list = []; for (var i = 1; i < response.length; i++) { @@ -108,16 +107,15 @@ module.exports.list_images_using_avdmanager = function () { /* To just return a list of names use this if (response[i].match(/Name:\s/)) { emulator_list.push(response[i].split('Name: ')[1].replace('\r', ''); - }*/ + } */ } return emulator_list; }); }; -module.exports.list_images_using_android = function() { - return superspawn.spawn('android', ['list', 'avd']) - .then(function(output) { +module.exports.list_images_using_android = function () { + return superspawn.spawn('android', ['list', 'avd']).then(function (output) { var response = output.split('\n'); var emulator_list = []; for (var i = 1; i < response.length; i++) { @@ -152,7 +150,7 @@ module.exports.list_images_using_android = function() { /* To just return a list of names use this if (response[i].match(/Name:\s/)) { emulator_list.push(response[i].split('Name: ')[1].replace('\r', ''); - }*/ + } */ } return emulator_list; @@ -170,13 +168,13 @@ module.exports.list_images_using_android = function() { skin : } */ -module.exports.list_images = function() { +module.exports.list_images = function () { if (forgivingWhichSync('avdmanager')) { return module.exports.list_images_using_avdmanager(); } else if (forgivingWhichSync('android')) { return module.exports.list_images_using_android(); } else { - return Q().then(function() { + return Q().then(function () { throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?'); }); } @@ -187,9 +185,8 @@ module.exports.list_images = function() { * or undefined if no avds exist. * Returns a promise. */ -module.exports.best_image = function() { - return this.list_images() - .then(function(images) { +module.exports.best_image = function () { + return this.list_images().then(function (images) { // Just return undefined if there is no images if (images.length === 0) return; @@ -198,9 +195,9 @@ module.exports.best_image = function() { var project_target = check_reqs.get_target().replace('android-', ''); for (var i in images) { var target = images[i].target; - if(target) { + if (target) { var num = target.split('(API level ')[1].replace(')', ''); - if (num == project_target) { + if (num === project_target) { return images[i]; } else if (project_target - num < closest && project_target > num) { closest = project_target - num; @@ -213,19 +210,18 @@ module.exports.best_image = function() { }; // Returns a promise. -module.exports.list_started = function() { +module.exports.list_started = function () { return Adb.devices({emulators: true}); }; // Returns a promise. // TODO: we should remove this, there's a more robust method under android_sdk.js -module.exports.list_targets = function() { - return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()}) - .then(function(output) { +module.exports.list_targets = function () { + return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()}).then(function (output) { var target_out = output.split('\n'); var targets = []; for (var i = target_out.length; i >= 0; i--) { - if(target_out[i].match(/id:/)) { + if (target_out[i].match(/id:/)) { targets.push(targets[i].split(' ')[1]); } } @@ -240,9 +236,8 @@ module.exports.list_targets = function() { module.exports.get_available_port = function () { var self = this; - return self.list_started() - .then(function (emulators) { - for (var p = 5584; p >= 5554; p-=2) { + return self.list_started().then(function (emulators) { + for (var p = 5584; p >= 5554; p -= 2) { if (emulators.indexOf('emulator-' + p) === -1) { events.emit('verbose', 'Found available port: ' + p); return p; @@ -262,14 +257,13 @@ module.exports.get_available_port = function () { * * Returns a promise. */ -module.exports.start = function(emulator_ID, boot_timeout) { +module.exports.start = function (emulator_ID, boot_timeout) { var self = this; - return Q().then(function() { + return Q().then(function () { if (emulator_ID) return Q(emulator_ID); - return self.best_image() - .then(function(best) { + return self.best_image().then(function (best) { if (best && best.name) { events.emit('warn', 'No emulator specified, defaulting to ' + best.name); return best.name; @@ -281,9 +275,8 @@ module.exports.start = function(emulator_ID, boot_timeout) { '2. Create an AVD by running: ' + androidCmd + ' avd\n' + 'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n')); }); - }).then(function(emulatorId) { - return self.get_available_port() - .then(function (port) { + }).then(function (emulatorId) { + return self.get_available_port().then(function (port) { // Figure out the directory the emulator binary runs in, and set the cwd to that directory. // Workaround for https://code.google.com/p/android/issues/detail?id=235461 var emulator_dir = path.dirname(shelljs.which('emulator')); @@ -297,20 +290,17 @@ module.exports.start = function(emulator_ID, boot_timeout) { events.emit('log', 'Waiting for emulator to start...'); return self.wait_for_emulator(port); }); - }).then(function(emulatorId) { - if (!emulatorId) - return Q.reject(new CordovaError('Failed to start emulator')); + }).then(function (emulatorId) { + if (!emulatorId) { return Q.reject(new CordovaError('Failed to start emulator')); } - //wait for emulator to boot up + // wait for emulator to boot up process.stdout.write('Waiting for emulator to boot (this may take a while)...'); - return self.wait_for_boot(emulatorId, boot_timeout) - .then(function(success) { + return self.wait_for_boot(emulatorId, boot_timeout).then(function (success) { if (success) { - events.emit('log','BOOT COMPLETE'); - //unlock screen - return Adb.shell(emulatorId, 'input keyevent 82') - .then(function() { - //return the new emulator id for the started emulators + events.emit('log', 'BOOT COMPLETE'); + // unlock screen + return Adb.shell(emulatorId, 'input keyevent 82').then(function () { + // return the new emulator id for the started emulators return emulatorId; }); } else { @@ -325,20 +315,19 @@ module.exports.start = function(emulator_ID, boot_timeout) { * Waits for an emulator to boot on a given port. * Returns this emulator's ID in a promise. */ -module.exports.wait_for_emulator = function(port) { +module.exports.wait_for_emulator = function (port) { var self = this; - return Q().then(function() { + return Q().then(function () { var emulator_id = 'emulator-' + port; - return Adb.shell(emulator_id, 'getprop dev.bootcomplete') - .then(function (output) { + return Adb.shell(emulator_id, 'getprop dev.bootcomplete').then(function (output) { if (output.indexOf('1') >= 0) { return emulator_id; } return self.wait_for_emulator(port); }, function (error) { - if (error && error.message && - (error.message.indexOf('not found') > -1) || - error.message.indexOf('device offline') > -1) { + if ((error && error.message && + (error.message.indexOf('not found') > -1)) || + (error.message.indexOf('device offline') > -1)) { // emulator not yet started, continue waiting return self.wait_for_emulator(port); } else { @@ -346,7 +335,7 @@ module.exports.wait_for_emulator = function(port) { throw error; } }); - }); + }); }; /* @@ -354,10 +343,9 @@ module.exports.wait_for_emulator = function(port) { * promise that resolves to a boolean indicating success. Not specifying a * time_remaining or passing a negative value will cause it to wait forever */ -module.exports.wait_for_boot = function(emulator_id, time_remaining) { +module.exports.wait_for_boot = function (emulator_id, time_remaining) { var self = this; - return Adb.shell(emulator_id, 'ps') - .then(function(output) { + return Adb.shell(emulator_id, 'ps').then(function (output) { if (output.match(/android\.process\.acore/)) { return true; } else if (time_remaining === 0) { @@ -366,7 +354,7 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) { process.stdout.write('.'); // Check at regular intervals - return Q.delay(time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL).then(function() { + return Q.delay(time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL).then(function () { var updated_time = time_remaining >= 0 ? Math.max(time_remaining - CHECK_BOOTED_INTERVAL, 0) : time_remaining; return self.wait_for_boot(emulator_id, updated_time); }); @@ -379,11 +367,10 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) { * TODO : Enter the stdin input required to complete the creation of an avd. * Returns a promise. */ -module.exports.create_image = function(name, target) { +module.exports.create_image = function (name, target) { console.log('Creating new avd named ' + name); if (target) { - return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target]) - .then(null, function(error) { + return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) { console.error('ERROR : Failed to create emulator image : '); console.error(' Do you have the latest android targets including ' + target + '?'); console.error(error); @@ -391,22 +378,20 @@ module.exports.create_image = function(name, target) { } else { console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.'); // TODO: there's a more robust method for finding targets in android_sdk.js - return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]) - .then(function() { + return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () { // TODO: This seems like another error case, even though it always happens. console.error('ERROR : Unable to create an avd emulator, no targets found.'); console.error('Ensure you have targets available by running the "android" command'); return Q.reject(); - }, function(error) { + }, function (error) { console.error('ERROR : Failed to create emulator image : '); console.error(error); }); } }; -module.exports.resolveTarget = function(target) { - return this.list_started() - .then(function(emulator_list) { +module.exports.resolveTarget = function (target) { + return this.list_started().then(function (emulator_list) { if (emulator_list.length < 1) { return Q.reject('No running Android emulators found, please start an emulator before deploying your project.'); } @@ -417,9 +402,8 @@ module.exports.resolveTarget = function(target) { return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.'); } - return build.detectArchitecture(target) - .then(function(arch) { - return {target:target, arch:arch, isEmulator:true}; + return build.detectArchitecture(target).then(function (arch) { + return {target: target, arch: arch, isEmulator: true}; }); }); }; @@ -430,7 +414,7 @@ module.exports.resolveTarget = function(target) { * If no started emulators are found, error out. * Returns a promise. */ -module.exports.install = function(givenTarget, buildResults) { +module.exports.install = function (givenTarget, buildResults) { var target; var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml')); @@ -438,7 +422,7 @@ module.exports.install = function(givenTarget, buildResults) { // resolve the target emulator return Q().then(function () { - if (givenTarget && typeof givenTarget == 'object') { + if (givenTarget && typeof givenTarget === 'object') { return givenTarget; } else { return module.exports.resolveTarget(givenTarget); @@ -452,13 +436,12 @@ module.exports.install = function(givenTarget, buildResults) { }).then(function () { // This promise is always resolved, even if 'adb uninstall' fails to uninstall app // or the app doesn't installed at all, so no error catching needed. - return Q.when() - .then(function() { + return Q.when().then(function () { var apk_path = build.findBestApkForArchitecture(buildResults, target.arch); var execOptions = { cwd: os.tmpdir(), - timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds + timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds killSignal: EXEC_KILL_SIGNAL }; @@ -469,12 +452,12 @@ module.exports.install = function(givenTarget, buildResults) { // A special function to call adb install in specific environment w/ specific options. // Introduced as a part of fix for http://issues.apache.org/jira/browse/CB-9119 // to workaround sporadic emulator hangs - function adbInstallWithOptions(target, apk, opts) { + function adbInstallWithOptions (target, apk, opts) { events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...'); var command = 'adb -s ' + target + ' install -r "' + apk + '"'; return Q.promise(function (resolve, reject) { - child_process.exec(command, opts, function(err, stdout, stderr) { + child_process.exec(command, opts, function (err, stdout, stderr) { if (err) reject(new CordovaError('Error executing "' + command + '": ' + stderr)); // adb does not return an error code even if installation fails. Instead it puts a specific // message to stdout, so we have to use RegExp matching to detect installation failure. @@ -494,27 +477,23 @@ module.exports.install = function(givenTarget, buildResults) { } function installPromise () { - return adbInstallWithOptions(target.target, apk_path, execOptions) - .catch(function (error) { + return adbInstallWithOptions(target.target, apk_path, execOptions).catch(function (error) { // CB-9557 CB-10157 only uninstall and reinstall app if the one that // is already installed on device was signed w/different certificate - if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) - throw error; + if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; } events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' + 'currently installed app was signed with different key'); // This promise is always resolved, even if 'adb uninstall' fails to uninstall app // or the app doesn't installed at all, so no error catching needed. - return Adb.uninstall(target.target, pkgName) - .then(function() { + return Adb.uninstall(target.target, pkgName).then(function () { return adbInstallWithOptions(target.target, apk_path, execOptions); }); }); } - return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise) - .then(function (output) { + return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise).then(function (output) { events.emit('log', 'INSTALL SUCCESS'); }); }); diff --git a/bin/templates/cordova/lib/getASPath.bat b/bin/templates/cordova/lib/getASPath.bat new file mode 100644 index 00000000..14dad439 --- /dev/null +++ b/bin/templates/cordova/lib/getASPath.bat @@ -0,0 +1,3 @@ +@ECHO OFF +for /f "tokens=2*" %%a in ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Android Studio" /v Path') do set "ASPath=%%~b" +ECHO %ASPath% \ No newline at end of file diff --git a/bin/templates/cordova/lib/log.js b/bin/templates/cordova/lib/log.js index ebf836d5..ef2dd5cb 100644 --- a/bin/templates/cordova/lib/log.js +++ b/bin/templates/cordova/lib/log.js @@ -19,28 +19,28 @@ under the License. */ -var path = require('path'), - os = require('os'), - Q = require('q'), - child_process = require('child_process'), - ROOT = path.join(__dirname, '..', '..'); +var path = require('path'); +var os = require('os'); +var Q = require('q'); +var child_process = require('child_process'); +var ROOT = path.join(__dirname, '..', '..'); /* * Starts running logcat in the shell. * Returns a promise. */ -module.exports.run = function() { +module.exports.run = function () { var d = Q.defer(); var adb = child_process.spawn('adb', ['logcat'], {cwd: os.tmpdir()}); - adb.stdout.on('data', function(data) { + adb.stdout.on('data', function (data) { var lines = data ? data.toString().split('\n') : []; - var out = lines.filter(function(x) { return x.indexOf('nativeGetEnabledTags') < 0; }); + var out = lines.filter(function (x) { return x.indexOf('nativeGetEnabledTags') < 0; }); console.log(out.join('\n')); }); adb.stderr.on('data', console.error); - adb.on('close', function(code) { + adb.on('close', function (code) { if (code > 0) { d.reject('Failed to run logcat command.'); } else d.resolve(); @@ -49,7 +49,7 @@ module.exports.run = function() { return d.promise; }; -module.exports.help = function() { +module.exports.help = function () { console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'log'))); console.log('Gives the logcat output on the command line.'); process.exit(0); diff --git a/bin/templates/cordova/lib/pluginHandlers.js b/bin/templates/cordova/lib/pluginHandlers.js index 5e745fd5..d14baba6 100644 --- a/bin/templates/cordova/lib/pluginHandlers.js +++ b/bin/templates/cordova/lib/pluginHandlers.js @@ -26,15 +26,15 @@ var events = require('cordova-common').events; var CordovaError = require('cordova-common').CordovaError; var handlers = { - 'source-file':{ - install:function(obj, plugin, project, options) { + 'source-file': { + install: function (obj, plugin, project, options) { if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id)); if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id)); var dest = path.join(obj.targetDir, path.basename(obj.src)); - if(options && options.android_studio === true) { - dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src)); + if (options && options.android_studio === true) { + dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src)); } if (options && options.force) { @@ -43,42 +43,42 @@ var handlers = { copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link)); } }, - uninstall:function(obj, plugin, project, options) { + uninstall: function (obj, plugin, project, options) { var dest = path.join(obj.targetDir, path.basename(obj.src)); - - if(options && options.android_studio === true) { - dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src)); + + if (options && options.android_studio === true) { + dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src)); } deleteJava(project.projectDir, dest); } }, - 'lib-file':{ - install:function(obj, plugin, project, options) { + 'lib-file': { + install: function (obj, plugin, project, options) { var dest = path.join('libs', path.basename(obj.src)); - if(options && options.android_studio === true) { - dest = path.join('app/libs', path.basename(obj.src)); + if (options && options.android_studio === true) { + dest = path.join('app/libs', path.basename(obj.src)); } copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link)); }, - uninstall:function(obj, plugin, project, options) { + uninstall: function (obj, plugin, project, options) { var dest = path.join('libs', path.basename(obj.src)); - if(options && options.android_studio === true) { - dest = path.join('app/libs', path.basename(obj.src)); + if (options && options.android_studio === true) { + dest = path.join('app/libs', path.basename(obj.src)); } removeFile(project.projectDir, dest); } }, - 'resource-file':{ - install:function(obj, plugin, project, options) { + 'resource-file': { + install: function (obj, plugin, project, options) { copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), !!(options && options.link)); }, - uninstall:function(obj, plugin, project, options) { + uninstall: function (obj, plugin, project, options) { removeFile(project.projectDir, path.normalize(obj.target)); } }, 'framework': { - install:function(obj, plugin, project, options) { + install: function (obj, plugin, project, options) { var src = obj.src; if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id)); @@ -95,15 +95,15 @@ var handlers = { subDir = src; } - if (obj.type == 'gradleReference') { + if (obj.type === 'gradleReference') { project.addGradleReference(parentDir, subDir); - } else if (obj.type == 'sys') { + } else if (obj.type === 'sys') { project.addSystemLibrary(parentDir, subDir); } else { project.addSubProject(parentDir, subDir); } }, - uninstall:function(obj, plugin, project, options) { + uninstall: function (obj, plugin, project, options) { var src = obj.src; if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id)); @@ -125,17 +125,17 @@ var handlers = { subDir = src; } - if (obj.type == 'gradleReference') { + if (obj.type === 'gradleReference') { project.removeGradleReference(parentDir, subDir); - } else if (obj.type == 'sys') { + } else if (obj.type === 'sys') { project.removeSystemLibrary(parentDir, subDir); } else { project.removeSubProject(parentDir, subDir); } } }, - asset:{ - install:function(obj, plugin, project, options) { + asset: { + install: function (obj, plugin, project, options) { if (!obj.src) { throw new CordovaError(generateAttributeError('src', 'asset', plugin.id)); } @@ -149,7 +149,7 @@ var handlers = { copyFile(plugin.dir, obj.src, project.platformWww, obj.target); } }, - uninstall:function(obj, plugin, project, options) { + uninstall: function (obj, plugin, project, options) { var target = obj.target || obj.src; if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id)); @@ -167,7 +167,7 @@ var handlers = { install: function (obj, plugin, project, options) { // Copy the plugin's files into the www directory. var moduleSource = path.resolve(plugin.dir, obj.src); - var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname (obj.src))); + var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname(obj.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 @@ -206,7 +206,7 @@ module.exports.getInstaller = function (type) { events.emit('verbose', '<' + type + '> is not supported for android plugins'); }; -module.exports.getUninstaller = function(type) { +module.exports.getUninstaller = function (type) { if (handlers[type] && handlers[type].uninstall) { return handlers[type].uninstall; } @@ -221,21 +221,19 @@ function copyFile (plugin_dir, src, project_dir, dest, link) { // check that src path is inside plugin directory var real_path = fs.realpathSync(src); var real_plugin_path = fs.realpathSync(plugin_dir); - if (real_path.indexOf(real_plugin_path) !== 0) - throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); + if (real_path.indexOf(real_plugin_path) !== 0) { throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); } dest = path.resolve(project_dir, dest); // check that dest path is located in project directory - if (dest.indexOf(project_dir) !== 0) - throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); + if (dest.indexOf(project_dir) !== 0) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); } shell.mkdir('-p', path.dirname(dest)); if (link) { symlinkFileOrDirTree(src, dest); } else if (fs.statSync(src).isDirectory()) { // XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq - shell.cp('-Rf', src+'/*', dest); + shell.cp('-Rf', src + '/*', dest); } else { shell.cp('-f', src, dest); } @@ -244,24 +242,22 @@ function copyFile (plugin_dir, src, project_dir, dest, link) { // Same as copy file but throws error if target exists function copyNewFile (plugin_dir, src, project_dir, dest, link) { var target_path = path.resolve(project_dir, dest); - if (fs.existsSync(target_path)) - throw new CordovaError('"' + target_path + '" already exists!'); + if (fs.existsSync(target_path)) { throw new CordovaError('"' + target_path + '" already exists!'); } copyFile(plugin_dir, src, project_dir, dest, !!link); } -function symlinkFileOrDirTree(src, dest) { +function symlinkFileOrDirTree (src, dest) { if (fs.existsSync(dest)) { shell.rm('-Rf', dest); } if (fs.statSync(src).isDirectory()) { shell.mkdir('-p', dest); - fs.readdirSync(src).forEach(function(entry) { + fs.readdirSync(src).forEach(function (entry) { symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry)); }); - } - else { + } else { fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest); } } @@ -292,8 +288,8 @@ function removeFileAndParents (baseDir, destFile, stopper) { // check if directory is empty var curDir = path.dirname(file); - while(curDir !== path.resolve(baseDir, stopper)) { - if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) { + while (curDir !== path.resolve(baseDir, stopper)) { + if (fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) { fs.rmdirSync(curDir); curDir = path.resolve(curDir, '..'); } else { @@ -303,6 +299,6 @@ function removeFileAndParents (baseDir, destFile, stopper) { } } -function generateAttributeError(attribute, element, id) { +function generateAttributeError (attribute, element, id) { return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id; } diff --git a/bin/templates/cordova/lib/prepare.js b/bin/templates/cordova/lib/prepare.js index 1532cab9..226f5756 100644 --- a/bin/templates/cordova/lib/prepare.js +++ b/bin/templates/cordova/lib/prepare.js @@ -16,6 +16,7 @@ specific language governing permissions and limitations under the License. */ +/* eslint no-useless-escape: 0 */ var Q = require('q'); var fs = require('fs'); @@ -40,17 +41,14 @@ module.exports.prepare = function (cordovaProject, options) { this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations); // Update own www dir with project's www assets and plugins' assets and js-files - return Q.when(updateWww(cordovaProject, this.locations)) - .then(function () { + return Q.when(updateWww(cordovaProject, this.locations)).then(function () { // update project according to config.xml changes. return updateProjectAccordingTo(self._config, self.locations); - }) - .then(function () { + }).then(function () { updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res)); updateSplashes(cordovaProject, path.relative(cordovaProject.root, self.locations.res)); updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root)); - }) - .then(function () { + }).then(function () { events.emit('verbose', 'Prepared android project successfully'); }); }; @@ -91,7 +89,7 @@ module.exports.clean = function (options) { * represents current project's configuration. When returned, the * configuration is already dumped to appropriate config.xml file. */ -function updateConfigFilesFrom(sourceConfig, configMunger, locations) { +function updateConfigFilesFrom (sourceConfig, configMunger, locations) { events.emit('verbose', 'Generating platform-specific config.xml from defaults for android at ' + locations.configXml); // First cleanup current config and merge project's one into own @@ -106,7 +104,7 @@ function updateConfigFilesFrom(sourceConfig, configMunger, locations) { // Merge changes from app's config.xml into platform's one var config = new ConfigParser(locations.configXml); xmlHelpers.mergeXml(sourceConfig.doc.getroot(), - config.doc.getroot(), 'android', /*clobber=*/true); + config.doc.getroot(), 'android', /* clobber= */true); config.write(); return config; @@ -115,7 +113,7 @@ function updateConfigFilesFrom(sourceConfig, configMunger, locations) { /** * Logs all file operations via the verbose event stream, indented. */ -function logFileOp(message) { +function logFileOp (message) { events.emit('verbose', ' ' + message); } @@ -128,7 +126,7 @@ function logFileOp(message) { * @param {Object} destinations An object that contains destination * paths for www files. */ -function updateWww(cordovaProject, destinations) { +function updateWww (cordovaProject, destinations) { var sourceDirs = [ path.relative(cordovaProject.root, cordovaProject.locations.www), path.relative(cordovaProject.root, destinations.platformWww) @@ -151,7 +149,7 @@ function updateWww(cordovaProject, destinations) { /** * Cleans all files from the platform 'www' directory. */ -function cleanWww(projectRoot, locations) { +function cleanWww (projectRoot, locations) { var targetDir = path.relative(projectRoot, locations.www); events.emit('verbose', 'Cleaning ' + targetDir); @@ -167,7 +165,7 @@ function cleanWww(projectRoot, locations) { * be used to update project * @param {Object} locations A map of locations for this platform */ -function updateProjectAccordingTo(platformConfig, locations) { +function updateProjectAccordingTo (platformConfig, locations) { // Update app name by editing res/values/strings.xml var name = platformConfig.name(); var strings = xmlHelpers.parseElementtreeSync(locations.strings); @@ -202,7 +200,7 @@ function updateProjectAccordingTo(platformConfig, locations) { if (java_files.length === 0) { throw new CordovaError('No Java files found that extend CordovaActivity.'); - } else if(java_files.length > 1) { + } else if (java_files.length > 1) { events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]); } @@ -213,12 +211,12 @@ function updateProjectAccordingTo(platformConfig, locations) { if (orig_pkg !== pkg) { // If package was name changed we need to remove old java with main activity - shell.rm('-Rf',java_files[0]); + shell.rm('-Rf', java_files[0]); // remove any empty directories var currentDir = path.dirname(java_files[0]); var sourcesRoot = path.resolve(locations.root, 'src'); - while(currentDir !== sourcesRoot) { - if(fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) { + while (currentDir !== sourcesRoot) { + if (fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) { fs.rmdirSync(currentDir); currentDir = path.resolve(currentDir, '..'); } else { @@ -231,7 +229,7 @@ function updateProjectAccordingTo(platformConfig, locations) { // Consturct the default value for versionCode as // PATCH + MINOR * 100 + MAJOR * 10000 // see http://developer.android.com/tools/publishing/versioning.html -function default_versionCode(version) { +function default_versionCode (version) { var nums = version.split('-')[0].split('.'); var versionCode = 0; if (+nums[0]) { @@ -248,7 +246,7 @@ function default_versionCode(version) { return versionCode; } -function getImageResourcePath(resourcesDir, type, density, name, sourceName) { +function getImageResourcePath (resourcesDir, type, density, name, sourceName) { if (/\.9\.png$/.test(sourceName)) { name = name.replace(/\.png$/, '.9.png'); } @@ -256,7 +254,7 @@ function getImageResourcePath(resourcesDir, type, density, name, sourceName) { return resourcePath; } -function updateSplashes(cordovaProject, platformResourcesDir) { +function updateSplashes (cordovaProject, platformResourcesDir) { var resources = cordovaProject.projectConfig.getSplashScreens('android'); // if there are "splash" elements in config.xml @@ -272,7 +270,7 @@ function updateSplashes(cordovaProject, platformResourcesDir) { if (!resource.density) { return; } - if (resource.density == 'mdpi') { + if (resource.density === 'mdpi') { hadMdpi = true; } var targetPath = getImageResourcePath( @@ -292,7 +290,7 @@ function updateSplashes(cordovaProject, platformResourcesDir) { resourceMap, { rootDir: cordovaProject.root }, logFileOp); } -function cleanSplashes(projectRoot, projectConfig, platformResourcesDir) { +function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) { var resources = projectConfig.getSplashScreens('android'); if (resources.length > 0) { var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'drawable', 'screen.png'); @@ -304,7 +302,7 @@ function cleanSplashes(projectRoot, projectConfig, platformResourcesDir) { } } -function updateIcons(cordovaProject, platformResourcesDir) { +function updateIcons (cordovaProject, platformResourcesDir) { var icons = cordovaProject.projectConfig.getIcons('android'); // if there are icon elements in config.xml @@ -328,7 +326,7 @@ function updateIcons(cordovaProject, platformResourcesDir) { }; // find the best matching icon for a given density or size // @output android_icons - var parseIcon = function(icon, icon_size) { + var parseIcon = function (icon, icon_size) { // do I have a platform icon for that density already var density = icon.density || sizeToDensityMap[icon_size]; if (!density) { @@ -343,7 +341,7 @@ function updateIcons(cordovaProject, platformResourcesDir) { }; // iterate over all icon elements to find the default icon and call parseIcon - for (var i=0; i 0) { var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'icon.png'); @@ -395,18 +393,16 @@ function cleanIcons(projectRoot, projectConfig, platformResourcesDir) { /** * Gets a map containing resources of a specified name from all drawable folders in a directory. */ -function mapImageResources(rootDir, subDir, type, resourceName) { +function mapImageResources (rootDir, subDir, type, resourceName) { var pathMap = {}; - shell.ls(path.join(rootDir, subDir, type + '-*')) - .forEach(function (drawableFolder) { + shell.ls(path.join(rootDir, subDir, type + '-*')).forEach(function (drawableFolder) { var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName); pathMap[imagePath] = null; }); return pathMap; } - -function updateFileResources(cordovaProject, platformDir) { +function updateFileResources (cordovaProject, platformDir) { var files = cordovaProject.projectConfig.getFileResources('android'); // if there are resource-file elements in config.xml @@ -416,7 +412,7 @@ function updateFileResources(cordovaProject, platformDir) { } var resourceMap = {}; - files.forEach(function(res) { + files.forEach(function (res) { var targetPath = path.join(platformDir, res.target); resourceMap[targetPath] = res.src; }); @@ -426,20 +422,20 @@ function updateFileResources(cordovaProject, platformDir) { resourceMap, { rootDir: cordovaProject.root }, logFileOp); } - -function cleanFileResources(projectRoot, projectConfig, platformDir) { +function cleanFileResources (projectRoot, projectConfig, platformDir) { var files = projectConfig.getFileResources('android', true); if (files.length > 0) { events.emit('verbose', 'Cleaning resource files at ' + platformDir); var resourceMap = {}; - files.forEach(function(res) { + files.forEach(function (res) { var filePath = path.join(platformDir, res.target); resourceMap[filePath] = null; }); FileUpdater.updatePaths( - resourceMap, { rootDir: projectRoot, all: true}, logFileOp); + resourceMap, { + rootDir: projectRoot, all: true}, logFileOp); } } @@ -454,7 +450,7 @@ function cleanFileResources(projectRoot, projectConfig, platformDir) { * default value, if there is no such preference. The default value is * 'singleTop' */ -function findAndroidLaunchModePreference(platformConfig) { +function findAndroidLaunchModePreference (platformConfig) { var launchMode = platformConfig.getPreference('AndroidLaunchMode'); if (!launchMode) { // Return a default value diff --git a/bin/templates/cordova/lib/retry.js b/bin/templates/cordova/lib/retry.js index 3cb49274..c464b9d5 100644 --- a/bin/templates/cordova/lib/retry.js +++ b/bin/templates/cordova/lib/retry.js @@ -45,12 +45,12 @@ module.exports.retryPromise = function (attemts_left, promiseFunction) { return promiseFunction.apply(undefined, promiseFunctionArguments).then( // on success pass results through - function onFulfilled(value) { + function onFulfilled (value) { return value; }, // on rejection either retry, or throw the error - function onRejected(error) { + function onRejected (error) { attemts_left -= 1; diff --git a/bin/templates/cordova/lib/run.js b/bin/templates/cordova/lib/run.js index 214a1e19..b97fce20 100644 --- a/bin/templates/cordova/lib/run.js +++ b/bin/templates/cordova/lib/run.js @@ -21,14 +21,14 @@ /* jshint loopfunc:true */ -var path = require('path'), - build = require('./build'), - emulator = require('./emulator'), - device = require('./device'), - Q = require('q'), - events = require('cordova-common').events; +var path = require('path'); +var build = require('./build'); +var emulator = require('./emulator'); +var device = require('./device'); +var Q = require('q'); +var events = require('cordova-common').events; -function getInstallTarget(runOptions) { +function getInstallTarget (runOptions) { var install_target; if (runOptions.target) { install_target = runOptions.target; @@ -51,17 +51,15 @@ function getInstallTarget(runOptions) { * * @return {Promise} */ - module.exports.run = function(runOptions) { +module.exports.run = function (runOptions) { var self = this; var install_target = getInstallTarget(runOptions); - return Q() - .then(function() { + return Q().then(function () { if (!install_target) { // no target given, deploy to device if available, otherwise use the emulator. - return device.list() - .then(function(device_list) { + return device.list().then(function (device_list) { if (device_list.length > 0) { events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.'); install_target = device_list[0]; @@ -71,36 +69,31 @@ function getInstallTarget(runOptions) { } }); } - }).then(function() { - if (install_target == '--device') { + }).then(function () { + if (install_target === '--device') { return device.resolveTarget(null); - } else if (install_target == '--emulator') { + } else if (install_target === '--emulator') { // Give preference to any already started emulators. Else, start one. - return emulator.list_started() - .then(function(started) { + return emulator.list_started().then(function (started) { return started && started.length > 0 ? started[0] : emulator.start(); - }).then(function(emulatorId) { + }).then(function (emulatorId) { return emulator.resolveTarget(emulatorId); }); } // They specified a specific device/emulator ID. - return device.list() - .then(function(devices) { + return device.list().then(function (devices) { if (devices.indexOf(install_target) > -1) { return device.resolveTarget(install_target); } - return emulator.list_started() - .then(function(started_emulators) { + return emulator.list_started().then(function (started_emulators) { if (started_emulators.indexOf(install_target) > -1) { return emulator.resolveTarget(install_target); } - return emulator.list_images() - .then(function(avds) { + return emulator.list_images().then(function (avds) { // if target emulator isn't started, then start it. for (var avd in avds) { - if (avds[avd].name == install_target) { - return emulator.start(install_target) - .then(function(emulatorId) { + if (avds[avd].name === install_target) { + return emulator.start(install_target).then(function (emulatorId) { return emulator.resolveTarget(emulatorId); }); } @@ -109,16 +102,14 @@ function getInstallTarget(runOptions) { }); }); }); - }).then(function(resolvedTarget) { + }).then(function (resolvedTarget) { // Better just call self.build, but we're doing some processing of // build results (according to platformApi spec) so they are in different // format than emulator.install expects. // TODO: Update emulator/device.install to handle this change - return build.run.call(self, runOptions, resolvedTarget) - .then(function(buildResults) { + return build.run.call(self, runOptions, resolvedTarget).then(function (buildResults) { if (resolvedTarget.isEmulator) { - return emulator.wait_for_boot(resolvedTarget.target) - .then(function () { + return emulator.wait_for_boot(resolvedTarget.target).then(function () { return emulator.install(resolvedTarget, buildResults); }); } @@ -127,7 +118,7 @@ function getInstallTarget(runOptions) { }); }; -module.exports.help = function() { +module.exports.help = function () { console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]) + ' [options]'); console.log('Build options :'); console.log(' --debug : Builds project in debug mode'); diff --git a/bin/templates/project/assets/www/cordova.js b/bin/templates/project/assets/www/cordova.js index 576fa567..dc1bf5ab 100644 --- a/bin/templates/project/assets/www/cordova.js +++ b/bin/templates/project/assets/www/cordova.js @@ -8,9 +8,9 @@ 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 @@ -18,2191 +18,2168 @@ specific language governing permissions and limitations under the License. */ -;(function() { -var PLATFORM_VERSION_BUILD_LABEL = '6.3.0-dev'; -// file: src/scripts/require.js - -/*jshint -W079 */ -/*jshint -W020 */ - -var require, - define; - (function () { - var modules = {}, - // Stack of moduleIds currently being built. - requireStack = [], - // Map of module ID -> index into requireStack of modules currently being built. - inProgressModules = {}, - SEPARATOR = "."; + var PLATFORM_VERSION_BUILD_LABEL = '6.3.0-dev'; + // file: src/scripts/require.js + /* global prompt */ + /* global atob */ + /* eslint no-eval : 0 */ + /* eslint no-cond-assign : 0 */ + /* eslint valid-typeof : 0 */ + 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, - localRequire = function (id) { + 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) === ".") { + // 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; + module.exports = {}; + delete module.factory; + factory(localRequire, module.exports, module); + return module.exports; } - if (modules[id].factory) { - try { - inProgressModules[id] = requireStack.length; - requireStack.push(id); - return build(modules[id]); - } finally { - delete inProgressModules[id]; - requireStack.pop(); + + 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; } - } - return modules[id].exports; - }; - - define = function (id, factory) { - if (modules[id]) { - throw "module " + id + " already defined"; - } - - modules[id] = { - id: id, - factory: factory + 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.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)) { - 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 = {}, - 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; -} - - -var cordova = { - define:define, - require:require, - version:PLATFORM_VERSION_BUILD_LABEL, - platformVersion:PLATFORM_VERSION_BUILD_LABEL, - platformId:platform.id, - /** - * 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); - 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: /Users/maj/src/cordova-android/cordova-js-src/android/nativeapiprovider.js -define("cordova/android/nativeapiprovider", function(require, exports, module) { - -/** - * Exports the ExposedJsApi.java object if available, otherwise exports the PromptBasedNativeApi. - */ - -var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi'); -var currentApi = nativeApi; - -module.exports = { - get: function() { return currentApi; }, - setPreferPrompt: function(value) { - currentApi = value ? require('cordova/android/promptbasednativeapi') : nativeApi; - }, - // Used only by tests. - set: function(value) { - currentApi = value; - } -}; - -}); - -// file: /Users/maj/src/cordova-android/cordova-js-src/android/promptbasednativeapi.js -define("cordova/android/promptbasednativeapi", function(require, exports, module) { - -/** - * Implements the API of ExposedJsApi.java, but uses prompt() to communicate. - * This is used pre-JellyBean, where addJavascriptInterface() is disabled. - */ - -module.exports = { - exec: function(bridgeSecret, service, action, callbackId, argsJson) { - return prompt(argsJson, 'gap:'+JSON.stringify([bridgeSecret, service, action, callbackId])); - }, - setNativeToJsBridgeMode: function(bridgeSecret, value) { - prompt(value, 'gap_bridge_mode:' + bridgeSecret); - }, - retrieveJsMessages: function(bridgeSecret, fromOnlineEvent) { - return prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret); - } -}; - -}); - -// 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), - cUpper = c.toUpperCase(), - arg = args[i]; - // Asterix means allow anything. - if (c == '*') { - continue; - } - typeName = utils.typeName(arg); - if ((arg === null || arg === undefined) && c == cUpper) { - continue; - } - if (typeName != typeMap[cUpper]) { - errMsg = 'Expected ' + typeMap[cUpper]; - break; - } - } - if (errMsg) { - errMsg += ', but got ' + typeName + '.'; - errMsg = 'Wrong type for parameter "' + extractParamName(opt_callee || args.callee, i) + '" of ' + functionName + ': ' + errMsg; - // Don't log when running 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) : new Buffer(str,'base64').toString('binary'); - 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> 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]; - } + define = function (id, factory) { + if (modules[id]) { + throw 'module ' + id + ' already defined'; } - if (obj.children) { - include(result, obj.children, clobber, merge); - } - } catch(e) { - utils.alert('Exception building Cordova JS globals: ' + e + ' for key "' + key + '"'); - } - }); -} + modules[id] = { + id: id, + factory: factory + }; + }; -/** - * 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]); - } - } - } + 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; } -} -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/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 + var HTMLElement; + if (window.cordova && !(window.cordova instanceof HTMLElement)) { + throw new Error('cordova already defined'); + } -// file: src/common/channel.js -define("cordova/channel", function(require, exports, module) { + var channel = require('cordova/channel'); + var platform = require('cordova/platform'); -var utils = require('cordova/utils'), - nextGuid = 1; - -/** - * Custom pub-sub "channel" that can have functions subscribed to it - * This object is used to define and control firing of events for - * cordova initialization, as well as for custom events thereafter. - * - * The order of events during page load and Cordova startup is as follows: - * - * onDOMContentLoaded* Internal event that is received when the web page is loaded and parsed. - * onNativeReady* Internal event that indicates the Cordova native side is ready. - * onCordovaReady* Internal event fired when all Cordova JavaScript objects have been created. - * 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; -}, - channel = { /** - * Calls the provided function only after all of the channels specified - * have been fired. All channels must be sticky channels. + * Intercept calls to addEventListener + removeEventListener and handle deviceready, + * resume, and pause events. */ - join: function(h, c) { - var len = c.length, - i = len, - f = function() { + 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; + } + + var cordova = { + define: define, + require: require, + version: PLATFORM_VERSION_BUILD_LABEL, + platformVersion: PLATFORM_VERSION_BUILD_LABEL, + platformId: platform.id, + /** + * 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); + 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: /Users/maj/src/cordova-android/cordova-js-src/android/nativeapiprovider.js + define('cordova/android/nativeapiprovider', function (require, exports, module) { + + /** + * Exports the ExposedJsApi.java object if available, otherwise exports the PromptBasedNativeApi. + */ + + var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi'); + var currentApi = nativeApi; + + module.exports = { + get: function () { return currentApi; }, + setPreferPrompt: function (value) { + currentApi = value ? require('cordova/android/promptbasednativeapi') : nativeApi; + }, + // Used only by tests. + set: function (value) { + currentApi = value; + } + }; + + }); + + // file: /Users/maj/src/cordova-android/cordova-js-src/android/promptbasednativeapi.js + define('cordova/android/promptbasednativeapi', function (require, exports, module) { + + /** + * Implements the API of ExposedJsApi.java, but uses prompt() to communicate. + * This is used pre-JellyBean, where addJavascriptInterface() is disabled. + */ + + module.exports = { + exec: function (bridgeSecret, service, action, callbackId, argsJson) { + return prompt(argsJson, 'gap:' + JSON.stringify([bridgeSecret, service, action, callbackId])); + }, + setNativeToJsBridgeMode: function (bridgeSecret, value) { + prompt(value, 'gap_bridge_mode:' + bridgeSecret); + }, + retrieveJsMessages: function (bridgeSecret, fromOnlineEvent) { + return prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret); + } + }; + + }); + + // 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.alloc(str, 'base64').toString('binary'); + 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; jNative bridge. - POLLING: 0, - // For LOAD_URL to be viable, it would need to have a work-around for - // the bug where the soft-keyboard gets dismissed when a message is sent. - LOAD_URL: 1, - // For the ONLINE_EVENT to be viable, it would need to intercept all event - // listeners (both through addEventListener and window.ononline) as well - // as set the navigator property itself. - ONLINE_EVENT: 2, - EVAL_BRIDGE: 3 - }, - jsToNativeBridgeMode, // Set lazily. - nativeToJsBridgeMode = nativeToJsModes.EVAL_BRIDGE, - pollEnabled = false, - bridgeSecret = -1; - -var messagesFromNative = []; -var isProcessing = false; -var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve(); -var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); }; - -function androidExec(success, fail, service, action, args) { - if (bridgeSecret < 0) { - // If we ever catch this firing, we'll need to queue up exec()s - // and fire them once we get a secret. For now, I don't think - // it's possible for exec() to be called since plugins are parsed but - // not run until until after onNativeReady. - throw new Error('exec() called without bridgeSecret'); - } - // Set default bridge modes if they have not already been set. - // By default, we use the failsafe, since addJavascriptInterface breaks too often - if (jsToNativeBridgeMode === undefined) { - androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT); - } - - // If args is not provided, default to an empty array - args = args || []; - - // Process any ArrayBuffers in the args into a string. - for (var i = 0; i < args.length; i++) { - if (utils.typeName(args[i]) == 'ArrayBuffer') { - args[i] = base64.fromArrayBuffer(args[i]); - } - } - - var callbackId = service + cordova.callbackId++, - argsJson = JSON.stringify(args); - if (success || fail) { - cordova.callbacks[callbackId] = {success:success, fail:fail}; - } - - var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson); - // If argsJson was received by Java as null, try again with the PROMPT bridge mode. - // This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666. - if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && msgs === "@Null arguments.") { - androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT); - androidExec(success, fail, service, action, args); - androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT); - } else if (msgs) { - messagesFromNative.push(msgs); - // Always process async to avoid exceptions messing up stack. - nextTick(processMessages); - } -} - -androidExec.init = function() { - //CB-11828 - //This failsafe checks the version of Android and if it's Jellybean, it switches it to - //using the Online Event bridge for communicating from Native to JS - // - //It's ugly, but it's necessary. - var check = navigator.userAgent.toLowerCase().match(/android\s[0-9].[0-9]/); - var version_code = check && check[0].match(/4.[0-3].*/); - if (version_code != null && nativeToJsBridgeMode == nativeToJsModes.EVAL_BRIDGE) { - nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT; - } - - bridgeSecret = +prompt('', 'gap_init:' + nativeToJsBridgeMode); - channel.onNativeReady.fire(); -}; - -function pollOnceFromOnlineEvent() { - pollOnce(true); -} - -function pollOnce(opt_fromOnlineEvent) { - if (bridgeSecret < 0) { - // This can happen when the NativeToJsMessageQueue resets the online state on page transitions. - // We know there's nothing to retrieve, so no need to poll. - return; - } - var msgs = nativeApiProvider.get().retrieveJsMessages(bridgeSecret, !!opt_fromOnlineEvent); - if (msgs) { - messagesFromNative.push(msgs); - // Process sync since we know we're already top-of-stack. - processMessages(); - } -} - -function pollingTimerFunc() { - if (pollEnabled) { - pollOnce(); - setTimeout(pollingTimerFunc, 50); - } -} - -function hookOnlineApis() { - function proxyEvent(e) { - cordova.fireWindowEvent(e.type); - } - // The network module takes care of firing online and offline events. - // It currently fires them only on document though, so we bridge them - // to window here (while first listening for exec()-releated online/offline - // events). - window.addEventListener('online', pollOnceFromOnlineEvent, false); - window.addEventListener('offline', pollOnceFromOnlineEvent, false); - cordova.addWindowEventHandler('online'); - cordova.addWindowEventHandler('offline'); - document.addEventListener('online', proxyEvent, false); - document.addEventListener('offline', proxyEvent, false); -} - -hookOnlineApis(); - -androidExec.jsToNativeModes = jsToNativeModes; -androidExec.nativeToJsModes = nativeToJsModes; - -androidExec.setJsToNativeBridgeMode = function(mode) { - if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaNative) { - mode = jsToNativeModes.PROMPT; - } - nativeApiProvider.setPreferPrompt(mode == jsToNativeModes.PROMPT); - jsToNativeBridgeMode = mode; -}; - -androidExec.setNativeToJsBridgeMode = function(mode) { - if (mode == nativeToJsBridgeMode) { - return; - } - if (nativeToJsBridgeMode == nativeToJsModes.POLLING) { - pollEnabled = false; - } - - nativeToJsBridgeMode = mode; - // Tell the native side to switch modes. - // Otherwise, it will be set by androidExec.init() - if (bridgeSecret >= 0) { - nativeApiProvider.get().setNativeToJsBridgeMode(bridgeSecret, mode); - } - - if (mode == nativeToJsModes.POLLING) { - pollEnabled = true; - setTimeout(pollingTimerFunc, 1); - } -}; - -function buildPayload(payload, message) { - var payloadKind = message.charAt(0); - if (payloadKind == 's') { - payload.push(message.slice(1)); - } else if (payloadKind == 't') { - payload.push(true); - } else if (payloadKind == 'f') { - payload.push(false); - } else if (payloadKind == 'N') { - payload.push(null); - } else if (payloadKind == 'n') { - payload.push(+message.slice(1)); - } else if (payloadKind == 'A') { - var data = message.slice(1); - payload.push(base64.toArrayBuffer(data)); - } else if (payloadKind == 'S') { - payload.push(window.atob(message.slice(1))); - } else if (payloadKind == 'M') { - var multipartMessages = message.slice(1); - while (multipartMessages !== "") { - var spaceIdx = multipartMessages.indexOf(' '); - var msgLen = +multipartMessages.slice(0, spaceIdx); - var multipartMessage = multipartMessages.substr(spaceIdx + 1, msgLen); - multipartMessages = multipartMessages.slice(spaceIdx + msgLen + 1); - buildPayload(payload, multipartMessage); - } - } else { - payload.push(JSON.parse(message)); - } -} - -// Processes a single message, as encoded by NativeToJsMessageQueue.java. -function processMessage(message) { - var firstChar = message.charAt(0); - if (firstChar == 'J') { - // This is deprecated on the .java side. It doesn't work with CSP enabled. - eval(message.slice(1)); - } else if (firstChar == 'S' || firstChar == 'F') { - var success = firstChar == 'S'; - var keepCallback = message.charAt(1) == '1'; - var spaceIdx = message.indexOf(' ', 2); - var status = +message.slice(2, spaceIdx); - var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1); - var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx); - var payloadMessage = message.slice(nextSpaceIdx + 1); - var payload = []; - buildPayload(payload, payloadMessage); - cordova.callbackFromNative(callbackId, success, status, payload, keepCallback); - } else { - console.log("processMessage failed: invalid message: " + JSON.stringify(message)); - } -} - -function processMessages() { - // Check for the reentrant case. - if (isProcessing) { - return; - } - if (messagesFromNative.length === 0) { - return; - } - isProcessing = true; - try { - var msg = popMessageFromQueue(); - // The Java side can send a * message to indicate that it - // still has messages waiting to be retrieved. - if (msg == '*' && messagesFromNative.length === 0) { - nextTick(pollOnce); - return; - } - processMessage(msg); - } finally { - isProcessing = false; - if (messagesFromNative.length > 0) { - nextTick(processMessages); - } - } -} - -function popMessageFromQueue() { - var messageBatch = messagesFromNative.shift(); - if (messageBatch == '*') { - return '*'; - } - - var spaceIdx = messageBatch.indexOf(' '); - var msgLen = +messageBatch.slice(0, spaceIdx); - var message = messageBatch.substr(spaceIdx + 1, msgLen); - messageBatch = messageBatch.slice(spaceIdx + msgLen + 1); - if (messageBatch) { - messagesFromNative.unshift(messageBatch); - } - return message; -} - -module.exports = androidExec; - -}); - -// 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/init_b.js -define("cordova/init_b", 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.onDOMContentLoaded, channel.onNativeReady, channel.onPluginsReady]; - -// setting exec -cordova.exec = require('cordova/exec'); - -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(); -} - -// 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'), - moduleMap = define.moduleMap, - symbolList, - deprecationMap; - -exports.reset = function() { - symbolList = []; - deprecationMap = {}; -}; - -function addEntry(strategy, moduleName, symbolPath, opt_deprecationMessage) { - if (!(moduleName in moduleMap)) { - throw new Error('Module ' + moduleName + ' does not exist.'); - } - symbolList.push(strategy, moduleName, symbolPath); - if (opt_deprecationMessage) { - deprecationMap[symbolPath] = opt_deprecationMessage; - } -} - -// Note: Android 2.3 does have Function.bind(). -exports.clobbers = function(moduleName, symbolPath, opt_deprecationMessage) { - addEntry('c', moduleName, symbolPath, opt_deprecationMessage); -}; - -exports.merges = function(moduleName, symbolPath, opt_deprecationMessage) { - addEntry('m', moduleName, symbolPath, opt_deprecationMessage); -}; - -exports.defaults = function(moduleName, symbolPath, opt_deprecationMessage) { - addEntry('d', moduleName, symbolPath, opt_deprecationMessage); -}; - -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) { - 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); - // - 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: src/common/modulemapper_b.js -define("cordova/modulemapper_b", function(require, exports, module) { - -var builder = require('cordova/builder'), - symbolList = [], - deprecationMap; - -exports.reset = function() { - symbolList = []; - deprecationMap = {}; -}; - -function addEntry(strategy, moduleName, symbolPath, opt_deprecationMessage) { - 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) { - 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); - // - 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/maj/src/cordova-android/cordova-js-src/platform.js -define("cordova/platform", function(require, exports, module) { - -// The last resume event that was received that had the result of a plugin call. -var lastResumeEvent = null; - -module.exports = { - id: 'android', - bootstrap: function() { - var channel = require('cordova/channel'), - cordova = require('cordova'), - exec = require('cordova/exec'), - modulemapper = require('cordova/modulemapper'); - - // Get the shared secret needed to use the bridge. - exec.init(); - - // TODO: Extract this as a proper plugin. - modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app'); - - var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App'; - - // Inject a listener for the backbutton on the document. - var backButtonChannel = cordova.addDocumentEventHandler('backbutton'); - backButtonChannel.onHasSubscribersChange = function() { - // If we just attached the first handler or detached the last handler, - // let native know we need to override the back button. - exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [this.numHandlers == 1]); }; - // Add hardware MENU and SEARCH button handlers - cordova.addDocumentEventHandler('menubutton'); - cordova.addDocumentEventHandler('searchbutton'); + 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.' + ); + } + } - function bindButtonChannel(buttonName) { - // generic button bind used for volumeup/volumedown buttons - var volumeButtonChannel = cordova.addDocumentEventHandler(buttonName + 'button'); - volumeButtonChannel.onHasSubscribersChange = function() { - exec(null, null, APP_PLUGIN_NAME, "overrideButton", [buttonName, this.numHandlers == 1]); + /** + * 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 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/maj/src/cordova-android/cordova-js-src/exec.js + define('cordova/exec', function (require, exports, module) { + + /** + * Execute a cordova command. It is up to the native side whether this action + * is synchronous or asynchronous. The native side can return: + * Synchronous: PluginResult object as a JSON string + * Asynchronous: Empty string "" + * If async, the native side will cordova.callbackSuccess or cordova.callbackError, + * depending upon the result of the action. + * + * @param {Function} success The success callback + * @param {Function} fail The fail callback + * @param {String} service The name of the service to use + * @param {String} action Action to be run in cordova + * @param {String[]} [args] Zero or more arguments to pass to the method + */ + var cordova = require('cordova'); + var nativeApiProvider = require('cordova/android/nativeapiprovider'); + var utils = require('cordova/utils'); + var base64 = require('cordova/base64'); + var channel = require('cordova/channel'); + var jsToNativeModes = { + PROMPT: 0, + JS_OBJECT: 1 + }; + var nativeToJsModes = { + // Polls for messages using the JS->Native bridge. + POLLING: 0, + // For LOAD_URL to be viable, it would need to have a work-around for + // the bug where the soft-keyboard gets dismissed when a message is sent. + LOAD_URL: 1, + // For the ONLINE_EVENT to be viable, it would need to intercept all event + // listeners (both through addEventListener and window.ononline) as well + // as set the navigator property itself. + ONLINE_EVENT: 2, + EVAL_BRIDGE: 3 + }; + var jsToNativeBridgeMode; // Set lazily. + var nativeToJsBridgeMode = nativeToJsModes.EVAL_BRIDGE; + var pollEnabled = false; + var bridgeSecret = -1; + + var messagesFromNative = []; + var isProcessing = false; + var resolvedPromise = typeof Promise === 'undefined' ? null : Promise.resolve(); + var nextTick = resolvedPromise ? function (fn) { resolvedPromise.then(fn); } : function (fn) { setTimeout(fn); }; + + function androidExec (success, fail, service, action, args) { + if (bridgeSecret < 0) { + // If we ever catch this firing, we'll need to queue up exec()s + // and fire them once we get a secret. For now, I don't think + // it's possible for exec() to be called since plugins are parsed but + // not run until until after onNativeReady. + throw new Error('exec() called without bridgeSecret'); + } + // Set default bridge modes if they have not already been set. + // By default, we use the failsafe, since addJavascriptInterface breaks too often + if (jsToNativeBridgeMode === undefined) { + androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT); + } + + // If args is not provided, default to an empty array + args = args || []; + + // Process any ArrayBuffers in the args into a string. + for (var i = 0; i < args.length; i++) { + if (utils.typeName(args[i]) === 'ArrayBuffer') { + args[i] = base64.fromArrayBuffer(args[i]); + } + } + + var callbackId = service + cordova.callbackId++; + var argsJson = JSON.stringify(args); + if (success || fail) { + cordova.callbacks[callbackId] = {success: success, fail: fail}; + } + + var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson); + // If argsJson was received by Java as null, try again with the PROMPT bridge mode. + // This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666. + if (jsToNativeBridgeMode === jsToNativeModes.JS_OBJECT && msgs === '@Null arguments.') { + androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT); + androidExec(success, fail, service, action, args); + androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT); + } else if (msgs) { + messagesFromNative.push(msgs); + // Always process async to avoid exceptions messing up stack. + nextTick(processMessages); + } + } + + androidExec.init = function () { + // CB-11828 + // This failsafe checks the version of Android and if it's Jellybean, it switches it to + // using the Online Event bridge for communicating from Native to JS + // + // It's ugly, but it's necessary. + var check = navigator.userAgent.toLowerCase().match(/android\s[0-9].[0-9]/); + var version_code = check && check[0].match(/4.[0-3].*/); + if (version_code !== null && nativeToJsBridgeMode === nativeToJsModes.EVAL_BRIDGE) { + nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT; + } + + bridgeSecret = +prompt('', 'gap_init:' + nativeToJsBridgeMode); + channel.onNativeReady.fire(); + }; + + function pollOnceFromOnlineEvent () { + pollOnce(true); + } + + function pollOnce (opt_fromOnlineEvent) { + if (bridgeSecret < 0) { + // This can happen when the NativeToJsMessageQueue resets the online state on page transitions. + // We know there's nothing to retrieve, so no need to poll. + return; + } + var msgs = nativeApiProvider.get().retrieveJsMessages(bridgeSecret, !!opt_fromOnlineEvent); + if (msgs) { + messagesFromNative.push(msgs); + // Process sync since we know we're already top-of-stack. + processMessages(); + } + } + + function pollingTimerFunc () { + if (pollEnabled) { + pollOnce(); + setTimeout(pollingTimerFunc, 50); + } + } + + function hookOnlineApis () { + function proxyEvent (e) { + cordova.fireWindowEvent(e.type); + } + // The network module takes care of firing online and offline events. + // It currently fires them only on document though, so we bridge them + // to window here (while first listening for exec()-releated online/offline + // events). + window.addEventListener('online', pollOnceFromOnlineEvent, false); + window.addEventListener('offline', pollOnceFromOnlineEvent, false); + cordova.addWindowEventHandler('online'); + cordova.addWindowEventHandler('offline'); + document.addEventListener('online', proxyEvent, false); + document.addEventListener('offline', proxyEvent, false); + } + + hookOnlineApis(); + + androidExec.jsToNativeModes = jsToNativeModes; + androidExec.nativeToJsModes = nativeToJsModes; + + androidExec.setJsToNativeBridgeMode = function (mode) { + if (mode === jsToNativeModes.JS_OBJECT && !window._cordovaNative) { + mode = jsToNativeModes.PROMPT; + } + nativeApiProvider.setPreferPrompt(mode === jsToNativeModes.PROMPT); + jsToNativeBridgeMode = mode; + }; + + androidExec.setNativeToJsBridgeMode = function (mode) { + if (mode === nativeToJsBridgeMode) { + return; + } + if (nativeToJsBridgeMode === nativeToJsModes.POLLING) { + pollEnabled = false; + } + + nativeToJsBridgeMode = mode; + // Tell the native side to switch modes. + // Otherwise, it will be set by androidExec.init() + if (bridgeSecret >= 0) { + nativeApiProvider.get().setNativeToJsBridgeMode(bridgeSecret, mode); + } + + if (mode === nativeToJsModes.POLLING) { + pollEnabled = true; + setTimeout(pollingTimerFunc, 1); + } + }; + + function buildPayload (payload, message) { + var payloadKind = message.charAt(0); + if (payloadKind === 's') { + payload.push(message.slice(1)); + } else if (payloadKind === 't') { + payload.push(true); + } else if (payloadKind === 'f') { + payload.push(false); + } else if (payloadKind === 'N') { + payload.push(null); + } else if (payloadKind === 'n') { + payload.push(+message.slice(1)); + } else if (payloadKind === 'A') { + var data = message.slice(1); + payload.push(base64.toArrayBuffer(data)); + } else if (payloadKind === 'S') { + payload.push(window.atob(message.slice(1))); + } else if (payloadKind === 'M') { + var multipartMessages = message.slice(1); + while (multipartMessages !== '') { + var spaceIdx = multipartMessages.indexOf(' '); + var msgLen = +multipartMessages.slice(0, spaceIdx); + var multipartMessage = multipartMessages.substr(spaceIdx + 1, msgLen); + multipartMessages = multipartMessages.slice(spaceIdx + msgLen + 1); + buildPayload(payload, multipartMessage); + } + } else { + payload.push(JSON.parse(message)); + } + } + + // Processes a single message, as encoded by NativeToJsMessageQueue.java. + function processMessage (message) { + var firstChar = message.charAt(0); + if (firstChar === 'J') { + // This is deprecated on the .java side. It doesn't work with CSP enabled. + eval(message.slice(1)); + } else if (firstChar === 'S' || firstChar === 'F') { + var success = firstChar === 'S'; + var keepCallback = message.charAt(1) === '1'; + var spaceIdx = message.indexOf(' ', 2); + var status = +message.slice(2, spaceIdx); + var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1); + var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx); + var payloadMessage = message.slice(nextSpaceIdx + 1); + var payload = []; + buildPayload(payload, payloadMessage); + cordova.callbackFromNative(callbackId, success, status, payload, keepCallback); + } else { + console.log('processMessage failed: invalid message: ' + JSON.stringify(message)); + } + } + + function processMessages () { + // Check for the reentrant case. + if (isProcessing) { + return; + } + if (messagesFromNative.length === 0) { + return; + } + isProcessing = true; + try { + var msg = popMessageFromQueue(); + // The Java side can send a * message to indicate that it + // still has messages waiting to be retrieved. + if (msg === '*' && messagesFromNative.length === 0) { + nextTick(pollOnce); + return; + } + processMessage(msg); + } finally { + isProcessing = false; + if (messagesFromNative.length > 0) { + nextTick(processMessages); + } + } + } + + function popMessageFromQueue () { + var messageBatch = messagesFromNative.shift(); + if (messageBatch === '*') { + return '*'; + } + + var spaceIdx = messageBatch.indexOf(' '); + var msgLen = +messageBatch.slice(0, spaceIdx); + var message = messageBatch.substr(spaceIdx + 1, msgLen); + messageBatch = messageBatch.slice(spaceIdx + msgLen + 1); + if (messageBatch) { + messagesFromNative.unshift(messageBatch); + } + return message; + } + + module.exports = androidExec; + + }); + + // 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); }; } - // Inject a listener for the volume buttons on the document. - bindButtonChannel('volumeup'); - bindButtonChannel('volumedown'); - // The resume event is not "sticky", but it is possible that the event - // will contain the result of a plugin call. We need to ensure that the - // plugin result is delivered even after the event is fired (CB-10498) - var cordovaAddEventListener = document.addEventListener; + // 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'); - document.addEventListener = function(evt, handler, capture) { - cordovaAddEventListener(evt, handler, capture); + // 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); + } - if (evt === 'resume' && lastResumeEvent) { - handler(lastResumeEvent); + // _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/init_b.js + define('cordova/init_b', 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.onDOMContentLoaded, channel.onNativeReady, channel.onPluginsReady]; + + // setting exec + cordova.exec = require('cordova/exec'); + + 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(); + } + + // 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; + 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) { + 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); + // + 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); + } } }; - // Let native code know we are all done on the JS side. - // Native code will then un-hide the WebView. - channel.onCordovaReady.subscribe(function() { - exec(onMessageFromNative, null, APP_PLUGIN_NAME, 'messageChannel', []); - exec(null, null, APP_PLUGIN_NAME, "show", []); - }); - } -}; - -function onMessageFromNative(msg) { - var cordova = require('cordova'); - var action = msg.action; - - switch (action) - { - // Button events - case 'backbutton': - case 'menubutton': - case 'searchbutton': - // App life cycle events - case 'pause': - // Volume events - case 'volumedownbutton': - case 'volumeupbutton': - cordova.fireDocumentEvent(action); - break; - case 'resume': - if(arguments.length > 1 && msg.pendingResult) { - if(arguments.length === 2) { - msg.pendingResult.result = arguments[1]; - } else { - // The plugin returned a multipart message - var res = []; - for(var i = 1; i < arguments.length; i++) { - res.push(arguments[i]); - } - msg.pendingResult.result = res; - } - - // Save the plugin result so that it can be delivered to the js - // even if they miss the initial firing of the event - lastResumeEvent = msg; + exports.getOriginalSymbol = function (context, symbolPath) { + var origSymbols = context.CDV_origSymbols; + if (origSymbols && (symbolPath in origSymbols)) { + return origSymbols[symbolPath]; } - cordova.fireDocumentEvent(action, msg); - break; - default: - throw new Error('Unknown event action ' + action); - } -} + 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/maj/src/cordova-android/cordova-js-src/plugin/android/app.js -define("cordova/plugin/android/app", function(require, exports, module) { + }); -var exec = require('cordova/exec'); -var APP_PLUGIN_NAME = Number(require('cordova').platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App'; + // file: src/common/modulemapper_b.js + define('cordova/modulemapper_b', function (require, exports, module) { -module.exports = { - /** - * Clear the resource cache. - */ - clearCache:function() { - exec(null, null, APP_PLUGIN_NAME, "clearCache", []); - }, + var builder = require('cordova/builder'); + var symbolList = []; + var deprecationMap; - /** - * Load the url into the webview or into new browser instance. - * - * @param url The URL to load - * @param props Properties that can be passed in to the activity: - * wait: int => wait msec before loading URL - * loadingDialog: "Title,Message" => display a native loading dialog - * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error - * clearHistory: boolean => clear webview history (default=false) - * openExternal: boolean => open in a new browser (default=false) - * - * Example: - * navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000}); - */ - loadUrl:function(url, props) { - exec(null, null, APP_PLUGIN_NAME, "loadUrl", [url, props]); - }, + exports.reset = function () { + symbolList = []; + deprecationMap = {}; + }; - /** - * Cancel loadUrl that is waiting to be loaded. - */ - cancelLoadUrl:function() { - exec(null, null, APP_PLUGIN_NAME, "cancelLoadUrl", []); - }, + function addEntry (strategy, moduleName, symbolPath, opt_deprecationMessage) { + symbolList.push(strategy, moduleName, symbolPath); + if (opt_deprecationMessage) { + deprecationMap[symbolPath] = opt_deprecationMessage; + } + } - /** - * Clear web history in this web view. - * Instead of BACK button loading the previous web page, it will exit the app. - */ - clearHistory:function() { - exec(null, null, APP_PLUGIN_NAME, "clearHistory", []); - }, + // Note: Android 2.3 does have Function.bind(). + exports.clobbers = function (moduleName, symbolPath, opt_deprecationMessage) { + addEntry('c', moduleName, symbolPath, opt_deprecationMessage); + }; - /** - * Go to previous page displayed. - * This is the same as pressing the backbutton on Android device. - */ - backHistory:function() { - exec(null, null, APP_PLUGIN_NAME, "backHistory", []); - }, + exports.merges = function (moduleName, symbolPath, opt_deprecationMessage) { + addEntry('m', moduleName, symbolPath, opt_deprecationMessage); + }; - /** - * Override the default behavior of the Android back button. - * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. - * - * Note: The user should not have to call this method. Instead, when the user - * registers for the "backbutton" event, this is automatically done. - * - * @param override T=override, F=cancel override - */ - overrideBackbutton:function(override) { - exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [override]); - }, + exports.defaults = function (moduleName, symbolPath, opt_deprecationMessage) { + addEntry('d', moduleName, symbolPath, opt_deprecationMessage); + }; - /** - * Override the default behavior of the Android volume button. - * If overridden, when the volume button is pressed, the "volume[up|down]button" - * JavaScript event will be fired. - * - * Note: The user should not have to call this method. Instead, when the user - * registers for the "volume[up|down]button" event, this is automatically done. - * - * @param button volumeup, volumedown - * @param override T=override, F=cancel override - */ - overrideButton:function(button, override) { - exec(null, null, APP_PLUGIN_NAME, "overrideButton", [button, override]); - }, + exports.runs = function (moduleName) { + addEntry('r', moduleName, null); + }; - /** - * Exit and terminate the application. - */ - exitApp:function() { - return exec(null, null, APP_PLUGIN_NAME, "exitApp", []); - } -}; + function prepareNamespace (symbolPath, context) { + if (!symbolPath) { + return context; + } + var parts = symbolPath.split('.'); + var cur = context; + for (var i = 0, part; part = parts[i]; ++i) { + cur = cur[part] = cur[part] || {}; + } + return cur; + } -}); + exports.mapModules = function (context) { + var origSymbols = {}; + context.CDV_origSymbols = origSymbols; + for (var i = 0, len = symbolList.length; i < len; i += 3) { + var strategy = symbolList[i]; + var moduleName = symbolList[i + 1]; + var module = require(moduleName); + // + 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); -// file: src/common/pluginloader.js -define("cordova/pluginloader", function(require, exports, module) { + var deprecationMsg = symbolPath in deprecationMap ? 'Access made to deprecated symbol: ' + symbolPath + '. ' + deprecationMsg : null; + var parentObj = prepareNamespace(namespace, context); + var target = parentObj[lastName]; -var modulemapper = require('cordova/modulemapper'); -var urlutil = require('cordova/urlutil'); + 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); + } + } + }; -// Helper function to inject a