You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by st...@apache.org on 2017/05/01 21:17:19 UTC
[37/63] [abbrv] cordova-lib git commit: CB-11242: updated tests and
fixtures
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js
new file mode 100644
index 0000000..e477a89
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js
@@ -0,0 +1,568 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* jshint sub:true */
+
+var et = require('elementtree'),
+ xml= require('../util/xml-helpers'),
+ CordovaError = require('../CordovaError/CordovaError'),
+ fs = require('fs'),
+ events = require('../events');
+
+
+/** Wraps a config.xml file */
+function ConfigParser(path) {
+ this.path = path;
+ try {
+ this.doc = xml.parseElementtreeSync(path);
+ this.cdvNamespacePrefix = getCordovaNamespacePrefix(this.doc);
+ et.register_namespace(this.cdvNamespacePrefix, 'http://cordova.apache.org/ns/1.0');
+ } catch (e) {
+ console.error('Parsing '+path+' failed');
+ throw e;
+ }
+ var r = this.doc.getroot();
+ if (r.tag !== 'widget') {
+ throw new CordovaError(path + ' has incorrect root node name (expected "widget", was "' + r.tag + '")');
+ }
+}
+
+function getNodeTextSafe(el) {
+ return el && el.text && el.text.trim();
+}
+
+function findOrCreate(doc, name) {
+ var ret = doc.find(name);
+ if (!ret) {
+ ret = new et.Element(name);
+ doc.getroot().append(ret);
+ }
+ return ret;
+}
+
+function getCordovaNamespacePrefix(doc){
+ var rootAtribs = Object.getOwnPropertyNames(doc.getroot().attrib);
+ var prefix = 'cdv';
+ for (var j = 0; j < rootAtribs.length; j++ ) {
+ if(rootAtribs[j].indexOf('xmlns:') === 0 &&
+ doc.getroot().attrib[rootAtribs[j]] === 'http://cordova.apache.org/ns/1.0'){
+ var strings = rootAtribs[j].split(':');
+ prefix = strings[1];
+ break;
+ }
+ }
+ return prefix;
+}
+
+/**
+ * Finds the value of an element's attribute
+ * @param {String} attributeName Name of the attribute to search for
+ * @param {Array} elems An array of ElementTree nodes
+ * @return {String}
+ */
+function findElementAttributeValue(attributeName, elems) {
+
+ elems = Array.isArray(elems) ? elems : [ elems ];
+
+ var value = elems.filter(function (elem) {
+ return elem.attrib.name.toLowerCase() === attributeName.toLowerCase();
+ }).map(function (filteredElems) {
+ return filteredElems.attrib.value;
+ }).pop();
+
+ return value ? value : '';
+}
+
+ConfigParser.prototype = {
+ getAttribute: function(attr) {
+ return this.doc.getroot().attrib[attr];
+ },
+
+ packageName: function(id) {
+ return this.getAttribute('id');
+ },
+ setPackageName: function(id) {
+ this.doc.getroot().attrib['id'] = id;
+ },
+ android_packageName: function() {
+ return this.getAttribute('android-packageName');
+ },
+ android_activityName: function() {
+ return this.getAttribute('android-activityName');
+ },
+ ios_CFBundleIdentifier: function() {
+ return this.getAttribute('ios-CFBundleIdentifier');
+ },
+ name: function() {
+ return getNodeTextSafe(this.doc.find('name'));
+ },
+ setName: function(name) {
+ var el = findOrCreate(this.doc, 'name');
+ el.text = name;
+ },
+ description: function() {
+ return getNodeTextSafe(this.doc.find('description'));
+ },
+ setDescription: function(text) {
+ var el = findOrCreate(this.doc, 'description');
+ el.text = text;
+ },
+ version: function() {
+ return this.getAttribute('version');
+ },
+ windows_packageVersion: function() {
+ return this.getAttribute('windows-packageVersion');
+ },
+ android_versionCode: function() {
+ return this.getAttribute('android-versionCode');
+ },
+ ios_CFBundleVersion: function() {
+ return this.getAttribute('ios-CFBundleVersion');
+ },
+ setVersion: function(value) {
+ this.doc.getroot().attrib['version'] = value;
+ },
+ author: function() {
+ return getNodeTextSafe(this.doc.find('author'));
+ },
+ getGlobalPreference: function (name) {
+ return findElementAttributeValue(name, this.doc.findall('preference'));
+ },
+ setGlobalPreference: function (name, value) {
+ var pref = this.doc.find('preference[@name="' + name + '"]');
+ if (!pref) {
+ pref = new et.Element('preference');
+ pref.attrib.name = name;
+ this.doc.getroot().append(pref);
+ }
+ pref.attrib.value = value;
+ },
+ getPlatformPreference: function (name, platform) {
+ return findElementAttributeValue(name, this.doc.findall('platform[@name=\'' + platform + '\']/preference'));
+ },
+ getPreference: function(name, platform) {
+
+ var platformPreference = '';
+
+ if (platform) {
+ platformPreference = this.getPlatformPreference(name, platform);
+ }
+
+ return platformPreference ? platformPreference : this.getGlobalPreference(name);
+
+ },
+ /**
+ * Returns all resources for the platform specified.
+ * @param {String} platform The platform.
+ * @param {string} resourceName Type of static resources to return.
+ * "icon" and "splash" currently supported.
+ * @return {Array} Resources for the platform specified.
+ */
+ getStaticResources: function(platform, resourceName) {
+ var ret = [],
+ staticResources = [];
+ if (platform) { // platform specific icons
+ this.doc.findall('platform[@name=\'' + platform + '\']/' + resourceName).forEach(function(elt){
+ elt.platform = platform; // mark as platform specific resource
+ staticResources.push(elt);
+ });
+ }
+ // root level resources
+ staticResources = staticResources.concat(this.doc.findall(resourceName));
+ // parse resource elements
+ var that = this;
+ staticResources.forEach(function (elt) {
+ var res = {};
+ res.src = elt.attrib.src;
+ res.target = elt.attrib.target || undefined;
+ res.density = elt.attrib['density'] || elt.attrib[that.cdvNamespacePrefix+':density'] || elt.attrib['gap:density'];
+ res.platform = elt.platform || null; // null means icon represents default icon (shared between platforms)
+ res.width = +elt.attrib.width || undefined;
+ res.height = +elt.attrib.height || undefined;
+
+ // default icon
+ if (!res.width && !res.height && !res.density) {
+ ret.defaultResource = res;
+ }
+ ret.push(res);
+ });
+
+ /**
+ * Returns resource with specified width and/or height.
+ * @param {number} width Width of resource.
+ * @param {number} height Height of resource.
+ * @return {Resource} Resource object or null if not found.
+ */
+ ret.getBySize = function(width, height) {
+ return ret.filter(function(res) {
+ if (!res.width && !res.height) {
+ return false;
+ }
+ return ((!res.width || (width == res.width)) &&
+ (!res.height || (height == res.height)));
+ })[0] || null;
+ };
+
+ /**
+ * Returns resource with specified density.
+ * @param {string} density Density of resource.
+ * @return {Resource} Resource object or null if not found.
+ */
+ ret.getByDensity = function(density) {
+ return ret.filter(function(res) {
+ return res.density == density;
+ })[0] || null;
+ };
+
+ /** Returns default icons */
+ ret.getDefault = function() {
+ return ret.defaultResource;
+ };
+
+ return ret;
+ },
+
+ /**
+ * Returns all icons for specific platform.
+ * @param {string} platform Platform name
+ * @return {Resource[]} Array of icon objects.
+ */
+ getIcons: function(platform) {
+ return this.getStaticResources(platform, 'icon');
+ },
+
+ /**
+ * Returns all splash images for specific platform.
+ * @param {string} platform Platform name
+ * @return {Resource[]} Array of Splash objects.
+ */
+ getSplashScreens: function(platform) {
+ return this.getStaticResources(platform, 'splash');
+ },
+
+ /**
+ * Returns all resource-files for a specific platform.
+ * @param {string} platform Platform name
+ * @return {Resource[]} Array of resource file objects.
+ */
+ getFileResources: function(platform) {
+ var fileResources = [];
+
+ if (platform) { // platform specific resources
+ fileResources = this.doc.findall('platform[@name=\'' + platform + '\']/resource-file').map(function(tag) {
+ return {
+ platform: platform,
+ src: tag.attrib.src,
+ target: tag.attrib.target,
+ versions: tag.attrib.versions,
+ deviceTarget: tag.attrib['device-target'],
+ arch: tag.attrib.arch
+ };
+ });
+ }
+
+ return fileResources;
+ },
+
+ /**
+ * Returns all hook scripts for the hook type specified.
+ * @param {String} hook The hook type.
+ * @param {Array} platforms Platforms to look for scripts into (root scripts will be included as well).
+ * @return {Array} Script elements.
+ */
+ getHookScripts: function(hook, platforms) {
+ var self = this;
+ var scriptElements = self.doc.findall('./hook');
+
+ if(platforms) {
+ platforms.forEach(function (platform) {
+ scriptElements = scriptElements.concat(self.doc.findall('./platform[@name="' + platform + '"]/hook'));
+ });
+ }
+
+ function filterScriptByHookType(el) {
+ return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
+ }
+
+ return scriptElements.filter(filterScriptByHookType);
+ },
+ /**
+ * Returns a list of plugin (IDs).
+ *
+ * This function also returns any plugin's that
+ * were defined using the legacy <feature> tags.
+ * @return {string[]} Array of plugin IDs
+ */
+ getPluginIdList: function () {
+ var plugins = this.doc.findall('plugin');
+ var result = plugins.map(function(plugin){
+ return plugin.attrib.name;
+ });
+ var features = this.doc.findall('feature');
+ features.forEach(function(element ){
+ var idTag = element.find('./param[@name="id"]');
+ if(idTag){
+ result.push(idTag.attrib.value);
+ }
+ });
+ return result;
+ },
+ getPlugins: function () {
+ return this.getPluginIdList().map(function (pluginId) {
+ return this.getPlugin(pluginId);
+ }, this);
+ },
+ /**
+ * Adds a plugin element. Does not check for duplicates.
+ * @name addPlugin
+ * @function
+ * @param {object} attributes name and spec are supported
+ * @param {Array|object} variables name, value or arbitary object
+ */
+ addPlugin: function (attributes, variables) {
+ if (!attributes && !attributes.name) return;
+ var el = new et.Element('plugin');
+ el.attrib.name = attributes.name;
+ if (attributes.spec) {
+ el.attrib.spec = attributes.spec;
+ }
+
+ // support arbitrary object as variables source
+ if (variables && typeof variables === 'object' && !Array.isArray(variables)) {
+ variables = Object.keys(variables)
+ .map(function (variableName) {
+ return {name: variableName, value: variables[variableName]};
+ });
+ }
+
+ if (variables) {
+ variables.forEach(function (variable) {
+ el.append(new et.Element('variable', { name: variable.name, value: variable.value }));
+ });
+ }
+ this.doc.getroot().append(el);
+ },
+ /**
+ * Retrives the plugin with the given id or null if not found.
+ *
+ * This function also returns any plugin's that
+ * were defined using the legacy <feature> tags.
+ * @name getPlugin
+ * @function
+ * @param {String} id
+ * @returns {object} plugin including any variables
+ */
+ getPlugin: function(id){
+ if(!id){
+ return undefined;
+ }
+ var pluginElement = this.doc.find('./plugin/[@name="' + id + '"]');
+ if (null === pluginElement) {
+ var legacyFeature = this.doc.find('./feature/param[@name="id"][@value="' + id + '"]/..');
+ if(legacyFeature){
+ events.emit('log', 'Found deprecated feature entry for ' + id +' in config.xml.');
+ return featureToPlugin(legacyFeature);
+ }
+ return undefined;
+ }
+ var plugin = {};
+
+ plugin.name = pluginElement.attrib.name;
+ plugin.spec = pluginElement.attrib.spec || pluginElement.attrib.src || pluginElement.attrib.version;
+ plugin.variables = {};
+ var variableElements = pluginElement.findall('variable');
+ variableElements.forEach(function(varElement){
+ var name = varElement.attrib.name;
+ var value = varElement.attrib.value;
+ if(name){
+ plugin.variables[name] = value;
+ }
+ });
+ return plugin;
+ },
+ /**
+ * Remove the plugin entry with give name (id).
+ *
+ * This function also operates on any plugin's that
+ * were defined using the legacy <feature> tags.
+ * @name removePlugin
+ * @function
+ * @param id name of the plugin
+ */
+ removePlugin: function(id){
+ if(id){
+ var plugins = this.doc.findall('./plugin/[@name="' + id + '"]')
+ .concat(this.doc.findall('./feature/param[@name="id"][@value="' + id + '"]/..'));
+ var children = this.doc.getroot().getchildren();
+ plugins.forEach(function (plugin) {
+ var idx = children.indexOf(plugin);
+ if (idx > -1) {
+ children.splice(idx, 1);
+ }
+ });
+ }
+ },
+
+ // Add any element to the root
+ addElement: function(name, attributes) {
+ var el = et.Element(name);
+ for (var a in attributes) {
+ el.attrib[a] = attributes[a];
+ }
+ this.doc.getroot().append(el);
+ },
+
+ /**
+ * Adds an engine. Does not check for duplicates.
+ * @param {String} name the engine name
+ * @param {String} spec engine source location or version (optional)
+ */
+ addEngine: function(name, spec){
+ if(!name) return;
+ var el = et.Element('engine');
+ el.attrib.name = name;
+ if(spec){
+ el.attrib.spec = spec;
+ }
+ this.doc.getroot().append(el);
+ },
+ /**
+ * Removes all the engines with given name
+ * @param {String} name the engine name.
+ */
+ removeEngine: function(name){
+ var engines = this.doc.findall('./engine/[@name="' +name+'"]');
+ for(var i=0; i < engines.length; i++){
+ var children = this.doc.getroot().getchildren();
+ var idx = children.indexOf(engines[i]);
+ if(idx > -1){
+ children.splice(idx,1);
+ }
+ }
+ },
+ getEngines: function(){
+ var engines = this.doc.findall('./engine');
+ return engines.map(function(engine){
+ var spec = engine.attrib.spec || engine.attrib.version;
+ return {
+ 'name': engine.attrib.name,
+ 'spec': spec ? spec : null
+ };
+ });
+ },
+ /* Get all the access tags */
+ getAccesses: function() {
+ var accesses = this.doc.findall('./access');
+ return accesses.map(function(access){
+ var minimum_tls_version = access.attrib['minimum-tls-version']; /* String */
+ var requires_forward_secrecy = access.attrib['requires-forward-secrecy']; /* Boolean */
+ var requires_certificate_transparency = access.attrib['requires-certificate-transparency']; /* Boolean */
+ var allows_arbitrary_loads_in_web_content = access.attrib['allows-arbitrary-loads-in-web-content']; /* Boolean */
+ var allows_arbitrary_loads_in_media = access.attrib['allows-arbitrary-loads-in-media']; /* Boolean */
+ var allows_local_networking = access.attrib['allows-local-networking']; /* Boolean */
+
+ return {
+ 'origin': access.attrib.origin,
+ 'minimum_tls_version': minimum_tls_version,
+ 'requires_forward_secrecy' : requires_forward_secrecy,
+ 'requires_certificate_transparency' : requires_certificate_transparency,
+ 'allows_arbitrary_loads_in_web_content' : allows_arbitrary_loads_in_web_content,
+ 'allows_arbitrary_loads_in_media' : allows_arbitrary_loads_in_media,
+ 'allows_local_networking' : allows_local_networking
+ };
+ });
+ },
+ /* Get all the allow-navigation tags */
+ getAllowNavigations: function() {
+ var allow_navigations = this.doc.findall('./allow-navigation');
+ return allow_navigations.map(function(allow_navigation){
+ var minimum_tls_version = allow_navigation.attrib['minimum-tls-version']; /* String */
+ var requires_forward_secrecy = allow_navigation.attrib['requires-forward-secrecy']; /* Boolean */
+ var requires_certificate_transparency = allow_navigation.attrib['requires-certificate-transparency']; /* Boolean */
+
+ return {
+ 'href': allow_navigation.attrib.href,
+ 'minimum_tls_version': minimum_tls_version,
+ 'requires_forward_secrecy' : requires_forward_secrecy,
+ 'requires_certificate_transparency' : requires_certificate_transparency
+ };
+ });
+ },
+ /* Get all the allow-intent tags */
+ getAllowIntents: function() {
+ var allow_intents = this.doc.findall('./allow-intent');
+ return allow_intents.map(function(allow_intent){
+ return {
+ 'href': allow_intent.attrib.href
+ };
+ });
+ },
+ /* Get all edit-config tags */
+ getEditConfigs: function(platform) {
+ var platform_tag = this.doc.find('./platform[@name="' + platform + '"]');
+ var platform_edit_configs = platform_tag ? platform_tag.findall('edit-config') : [];
+
+ var edit_configs = this.doc.findall('edit-config').concat(platform_edit_configs);
+
+ return edit_configs.map(function(tag) {
+ var editConfig =
+ {
+ file : tag.attrib['file'],
+ target : tag.attrib['target'],
+ mode : tag.attrib['mode'],
+ id : 'config.xml',
+ xmls : tag.getchildren()
+ };
+ return editConfig;
+ });
+ },
+ write:function() {
+ fs.writeFileSync(this.path, this.doc.write({indent: 4}), 'utf-8');
+ }
+};
+
+function featureToPlugin(featureElement) {
+ var plugin = {};
+ plugin.variables = [];
+ var pluginVersion,
+ pluginSrc;
+
+ var nodes = featureElement.findall('param');
+ nodes.forEach(function (element) {
+ var n = element.attrib.name;
+ var v = element.attrib.value;
+ if (n === 'id') {
+ plugin.name = v;
+ } else if (n === 'version') {
+ pluginVersion = v;
+ } else if (n === 'url' || n === 'installPath') {
+ pluginSrc = v;
+ } else {
+ plugin.variables[n] = v;
+ }
+ });
+
+ var spec = pluginSrc || pluginVersion;
+ if (spec) {
+ plugin.spec = spec;
+ }
+
+ return plugin;
+}
+module.exports = ConfigParser;
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md
new file mode 100644
index 0000000..e5cd1bf
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md
@@ -0,0 +1,86 @@
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+-->
+
+# Cordova-Lib
+
+## ConfigParser
+
+wraps a valid cordova config.xml file
+
+### Usage
+
+### Include the ConfigParser module in a projet
+
+ var ConfigParser = require('cordova-lib').configparser;
+
+### Create a new ConfigParser
+
+ var config = new ConfigParser('path/to/config/xml/');
+
+### Utility Functions
+
+#### packageName(id)
+returns document root 'id' attribute value
+#### Usage
+
+ config.packageName: function(id)
+
+/*
+ * sets document root element 'id' attribute to @id
+ *
+ * @id - new id value
+ *
+ */
+#### setPackageName(id)
+set document root 'id' attribute to
+ function(id) {
+ this.doc.getroot().attrib['id'] = id;
+ },
+
+###
+ name: function() {
+ return getNodeTextSafe(this.doc.find('name'));
+ },
+ setName: function(name) {
+ var el = findOrCreate(this.doc, 'name');
+ el.text = name;
+ },
+
+### read the description element
+
+ config.description()
+
+ var text = "New and improved description of App"
+ setDescription(text)
+
+### version management
+ version()
+ android_versionCode()
+ ios_CFBundleVersion()
+ setVersion()
+
+### read author element
+
+ config.author();
+
+### read preference
+
+ config.getPreference(name);
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js
new file mode 100644
index 0000000..46e733f
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js
@@ -0,0 +1,76 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+var fs = require('fs'),
+ path = require('path');
+
+function isRootDir(dir) {
+ if (fs.existsSync(path.join(dir, 'www'))) {
+ if (fs.existsSync(path.join(dir, 'config.xml'))) {
+ // For sure is.
+ if (fs.existsSync(path.join(dir, 'platforms'))) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+ // Might be (or may be under platforms/).
+ if (fs.existsSync(path.join(dir, 'www', 'config.xml'))) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+// Runs up the directory chain looking for a .cordova directory.
+// IF it is found we are in a Cordova project.
+// Omit argument to use CWD.
+function isCordova(dir) {
+ if (!dir) {
+ // Prefer PWD over cwd so that symlinked dirs within your PWD work correctly (CB-5687).
+ var pwd = process.env.PWD;
+ var cwd = process.cwd();
+ if (pwd && pwd != cwd && pwd != 'undefined') {
+ return isCordova(pwd) || isCordova(cwd);
+ }
+ return isCordova(cwd);
+ }
+ var bestReturnValueSoFar = false;
+ for (var i = 0; i < 1000; ++i) {
+ var result = isRootDir(dir);
+ if (result === 2) {
+ return dir;
+ }
+ if (result === 1) {
+ bestReturnValueSoFar = dir;
+ }
+ var parentDir = path.normalize(path.join(dir, '..'));
+ // Detect fs root.
+ if (parentDir == dir) {
+ return bestReturnValueSoFar;
+ }
+ dir = parentDir;
+ }
+ console.error('Hit an unhandled case in CordovaCheck.isCordova');
+ return false;
+}
+
+module.exports = {
+ findProjectRoot : isCordova
+};
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js
new file mode 100644
index 0000000..7262448
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js
@@ -0,0 +1,91 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* jshint proto:true */
+
+var EOL = require('os').EOL;
+
+/**
+ * A derived exception class. See usage example in cli.js
+ * Based on:
+ * stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/8460753#8460753
+ * @param {String} message Error message
+ * @param {Number} [code=0] Error code
+ * @param {CordovaExternalToolErrorContext} [context] External tool error context object
+ * @constructor
+ */
+function CordovaError(message, code, context) {
+ Error.captureStackTrace(this, this.constructor);
+ this.name = this.constructor.name;
+ this.message = message;
+ this.code = code || CordovaError.UNKNOWN_ERROR;
+ this.context = context;
+}
+CordovaError.prototype.__proto__ = Error.prototype;
+
+// TODO: Extend error codes according the projects specifics
+CordovaError.UNKNOWN_ERROR = 0;
+CordovaError.EXTERNAL_TOOL_ERROR = 1;
+
+/**
+ * Translates instance's error code number into error code name, e.g. 0 -> UNKNOWN_ERROR
+ * @returns {string} Error code string name
+ */
+CordovaError.prototype.getErrorCodeName = function() {
+ for(var key in CordovaError) {
+ if(CordovaError.hasOwnProperty(key)) {
+ if(CordovaError[key] === this.code) {
+ return key;
+ }
+ }
+ }
+};
+
+/**
+ * Converts CordovaError instance to string representation
+ * @param {Boolean} [isVerbose] Set up verbose mode. Used to provide more
+ * details including information about error code name and context
+ * @return {String} Stringified error representation
+ */
+CordovaError.prototype.toString = function(isVerbose) {
+ var message = '', codePrefix = '';
+
+ if(this.code !== CordovaError.UNKNOWN_ERROR) {
+ codePrefix = 'code: ' + this.code + (isVerbose ? (' (' + this.getErrorCodeName() + ')') : '') + ' ';
+ }
+
+ if(this.code === CordovaError.EXTERNAL_TOOL_ERROR) {
+ if(typeof this.context !== 'undefined') {
+ if(isVerbose) {
+ message = codePrefix + EOL + this.context.toString(isVerbose) + '\n failed with an error: ' +
+ this.message + EOL + 'Stack trace: ' + this.stack;
+ } else {
+ message = codePrefix + '\'' + this.context.toString(isVerbose) + '\' ' + this.message;
+ }
+ } else {
+ message = 'External tool failed with an error: ' + this.message;
+ }
+ } else {
+ message = isVerbose ? codePrefix + this.stack : codePrefix + this.message;
+ }
+
+ return message;
+};
+
+module.exports = CordovaError;
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js
new file mode 100644
index 0000000..ca9a4aa
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js
@@ -0,0 +1,48 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+/* jshint proto:true */
+
+var path = require('path');
+
+/**
+ * @param {String} cmd Command full path
+ * @param {String[]} args Command args
+ * @param {String} [cwd] Command working directory
+ * @constructor
+ */
+function CordovaExternalToolErrorContext(cmd, args, cwd) {
+ this.cmd = cmd;
+ // Helper field for readability
+ this.cmdShortName = path.basename(cmd);
+ this.args = args;
+ this.cwd = cwd;
+}
+
+CordovaExternalToolErrorContext.prototype.toString = function(isVerbose) {
+ if(isVerbose) {
+ return 'External tool \'' + this.cmdShortName + '\'' +
+ '\nCommand full path: ' + this.cmd + '\nCommand args: ' + this.args +
+ (typeof this.cwd !== 'undefined' ? '\nCommand cwd: ' + this.cwd : '');
+ }
+
+ return this.cmdShortName;
+};
+
+module.exports = CordovaExternalToolErrorContext;
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaLogger.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaLogger.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaLogger.js
new file mode 100644
index 0000000..dc49a4b
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaLogger.js
@@ -0,0 +1,220 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+var ansi = require('ansi');
+var EventEmitter = require('events').EventEmitter;
+var CordovaError = require('./CordovaError/CordovaError');
+var EOL = require('os').EOL;
+
+var INSTANCE;
+
+/**
+ * @class CordovaLogger
+ *
+ * Implements logging facility that anybody could use. Should not be
+ * instantiated directly, `CordovaLogger.get()` method should be used instead
+ * to acquire logger instance
+ */
+function CordovaLogger () {
+ this.levels = {};
+ this.colors = {};
+ this.stdout = process.stdout;
+ this.stderr = process.stderr;
+
+ this.stdoutCursor = ansi(this.stdout);
+ this.stderrCursor = ansi(this.stderr);
+
+ this.addLevel('verbose', 1000, 'grey');
+ this.addLevel('normal' , 2000);
+ this.addLevel('warn' , 2000, 'yellow');
+ this.addLevel('info' , 3000, 'blue');
+ this.addLevel('error' , 5000, 'red');
+ this.addLevel('results' , 10000);
+
+ this.setLevel('normal');
+}
+
+/**
+ * Static method to create new or acquire existing instance.
+ *
+ * @return {CordovaLogger} Logger instance
+ */
+CordovaLogger.get = function () {
+ return INSTANCE || (INSTANCE = new CordovaLogger());
+};
+
+CordovaLogger.VERBOSE = 'verbose';
+CordovaLogger.NORMAL = 'normal';
+CordovaLogger.WARN = 'warn';
+CordovaLogger.INFO = 'info';
+CordovaLogger.ERROR = 'error';
+CordovaLogger.RESULTS = 'results';
+
+/**
+ * Emits log message to process' stdout/stderr depending on message's severity
+ * and current log level. If severity is less than current logger's level,
+ * then the message is ignored.
+ *
+ * @param {String} logLevel The message's log level. The logger should have
+ * corresponding level added (via logger.addLevel), otherwise
+ * `CordovaLogger.NORMAL` level will be used.
+ * @param {String} message The message, that should be logged to process'
+ * stdio
+ *
+ * @return {CordovaLogger} Current instance, to allow calls chaining.
+ */
+CordovaLogger.prototype.log = function (logLevel, message) {
+ // if there is no such logLevel defined, or provided level has
+ // less severity than active level, then just ignore this call and return
+ if (!this.levels[logLevel] || this.levels[logLevel] < this.levels[this.logLevel])
+ // return instance to allow to chain calls
+ return this;
+
+ var isVerbose = this.logLevel === 'verbose';
+ var cursor = this.stdoutCursor;
+
+ if (message instanceof Error || logLevel === CordovaLogger.ERROR) {
+ message = formatError(message, isVerbose);
+ cursor = this.stderrCursor;
+ }
+
+ var color = this.colors[logLevel];
+ if (color) {
+ cursor.bold().fg[color]();
+ }
+
+ cursor.write(message).reset().write(EOL);
+
+ return this;
+};
+
+/**
+ * Adds a new level to logger instance. This method also creates a shortcut
+ * method to log events with the level provided (i.e. after adding new level
+ * 'debug', the method `debug(message)`, equal to logger.log('debug', message),
+ * will be added to logger instance)
+ *
+ * @param {String} level A log level name. The levels with the following
+ * names added by default to every instance: 'verbose', 'normal', 'warn',
+ * 'info', 'error', 'results'
+ * @param {Number} severity A number that represents level's severity.
+ * @param {String} color A valid color name, that will be used to log
+ * messages with this level. Any CSS color code or RGB value is allowed
+ * (according to ansi documentation:
+ * https://github.com/TooTallNate/ansi.js#features)
+ *
+ * @return {CordovaLogger} Current instance, to allow calls chaining.
+ */
+CordovaLogger.prototype.addLevel = function (level, severity, color) {
+
+ this.levels[level] = severity;
+
+ if (color) {
+ this.colors[level] = color;
+ }
+
+ // Define own method with corresponding name
+ if (!this[level]) {
+ this[level] = this.log.bind(this, level);
+ }
+
+ return this;
+};
+
+/**
+ * Sets the current logger level to provided value. If logger doesn't have level
+ * with this name, `CordovaLogger.NORMAL` will be used.
+ *
+ * @param {String} logLevel Level name. The level with this name should be
+ * added to logger before.
+ *
+ * @return {CordovaLogger} Current instance, to allow calls chaining.
+ */
+CordovaLogger.prototype.setLevel = function (logLevel) {
+ this.logLevel = this.levels[logLevel] ? logLevel : CordovaLogger.NORMAL;
+
+ return this;
+};
+
+/**
+ * Adjusts the current logger level according to the passed options.
+ *
+ * @param {Object|Array} opts An object or args array with options
+ *
+ * @return {CordovaLogger} Current instance, to allow calls chaining.
+ */
+CordovaLogger.prototype.adjustLevel = function (opts) {
+ if (opts.verbose || (Array.isArray(opts) && opts.indexOf('--verbose') !== -1)) {
+ this.setLevel('verbose');
+ } else if (opts.silent || (Array.isArray(opts) && opts.indexOf('--silent') !== -1)) {
+ this.setLevel('error');
+ }
+
+ return this;
+};
+
+/**
+ * Attaches logger to EventEmitter instance provided.
+ *
+ * @param {EventEmitter} eventEmitter An EventEmitter instance to attach
+ * logger to.
+ *
+ * @return {CordovaLogger} Current instance, to allow calls chaining.
+ */
+CordovaLogger.prototype.subscribe = function (eventEmitter) {
+
+ if (!(eventEmitter instanceof EventEmitter))
+ throw new Error('Subscribe method only accepts an EventEmitter instance as argument');
+
+ eventEmitter.on('verbose', this.verbose)
+ .on('log', this.normal)
+ .on('info', this.info)
+ .on('warn', this.warn)
+ .on('warning', this.warn)
+ // Set up event handlers for logging and results emitted as events.
+ .on('results', this.results);
+
+ return this;
+};
+
+function formatError(error, isVerbose) {
+ var message = '';
+
+ if (error instanceof CordovaError) {
+ message = error.toString(isVerbose);
+ } else if (error instanceof Error) {
+ if (isVerbose) {
+ message = error.stack;
+ } else {
+ message = error.message;
+ }
+ } else {
+ // Plain text error message
+ message = error;
+ }
+
+ if (typeof message === 'string' && message.toUpperCase().indexOf('ERROR:') !== 0) {
+ // Needed for backward compatibility with external tools
+ message = 'Error: ' + message;
+ }
+
+ return message;
+}
+
+module.exports = CordovaLogger;
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/FileUpdater.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/FileUpdater.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/FileUpdater.js
new file mode 100644
index 0000000..8b6876b
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/FileUpdater.js
@@ -0,0 +1,416 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+"use strict";
+
+var fs = require("fs");
+var path = require("path");
+var shell = require("shelljs");
+var minimatch = require("minimatch");
+
+/**
+ * Logging callback used in the FileUpdater methods.
+ * @callback loggingCallback
+ * @param {string} message A message describing a single file update operation.
+ */
+
+/**
+ * Updates a target file or directory with a source file or directory. (Directory updates are
+ * not recursive.) Stats for target and source items must be passed in. This is an internal
+ * helper function used by other methods in this module.
+ *
+ * @param {?string} sourcePath Source file or directory to be used to update the
+ * destination. If the source is null, then the destination is deleted if it exists.
+ * @param {?fs.Stats} sourceStats An instance of fs.Stats for the source path, or null if
+ * the source does not exist.
+ * @param {string} targetPath Required destination file or directory to be updated. If it does
+ * not exist, it will be created.
+ * @param {?fs.Stats} targetStats An instance of fs.Stats for the target path, or null if
+ * the target does not exist.
+ * @param {Object} [options] Optional additional parameters for the update.
+ * @param {string} [options.rootDir] Optional root directory (such as a project) to which target
+ * and source path parameters are relative; may be omitted if the paths are absolute. The
+ * rootDir is always omitted from any logged paths, to make the logs easier to read.
+ * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times.
+ * Otherwise, a file is copied if the source's last-modified time is greather than or
+ * equal to the target's last-modified time, or if the file sizes are different.
+ * @param {loggingCallback} [log] Optional logging callback that takes a string message
+ * describing any file operations that are performed.
+ * @return {boolean} true if any changes were made, or false if the force flag is not set
+ * and everything was up to date
+ */
+function updatePathWithStats(sourcePath, sourceStats, targetPath, targetStats, options, log) {
+ var updated = false;
+
+ var rootDir = (options && options.rootDir) || "";
+ var copyAll = (options && options.all) || false;
+
+ var targetFullPath = path.join(rootDir || "", targetPath);
+
+ if (sourceStats) {
+ var sourceFullPath = path.join(rootDir || "", sourcePath);
+
+ if (targetStats) {
+ // The target exists. But if the directory status doesn't match the source, delete it.
+ if (targetStats.isDirectory() && !sourceStats.isDirectory()) {
+ log("rmdir " + targetPath + " (source is a file)");
+ shell.rm("-rf", targetFullPath);
+ targetStats = null;
+ updated = true;
+ } else if (!targetStats.isDirectory() && sourceStats.isDirectory()) {
+ log("delete " + targetPath + " (source is a directory)");
+ shell.rm("-f", targetFullPath);
+ targetStats = null;
+ updated = true;
+ }
+ }
+
+ if (!targetStats) {
+ if (sourceStats.isDirectory()) {
+ // The target directory does not exist, so it should be created.
+ log("mkdir " + targetPath);
+ shell.mkdir("-p", targetFullPath);
+ updated = true;
+ } else if (sourceStats.isFile()) {
+ // The target file does not exist, so it should be copied from the source.
+ log("copy " + sourcePath + " " + targetPath + (copyAll ? "" : " (new file)"));
+ shell.cp("-f", sourceFullPath, targetFullPath);
+ updated = true;
+ }
+ } else if (sourceStats.isFile() && targetStats.isFile()) {
+ // The source and target paths both exist and are files.
+ if (copyAll) {
+ // The caller specified all files should be copied.
+ log("copy " + sourcePath + " " + targetPath);
+ shell.cp("-f", sourceFullPath, targetFullPath);
+ updated = true;
+ } else {
+ // Copy if the source has been modified since it was copied to the target, or if
+ // the file sizes are different. (The latter catches most cases in which something
+ // was done to the file after copying.) Comparison is >= rather than > to allow
+ // for timestamps lacking sub-second precision in some filesystems.
+ if (sourceStats.mtime.getTime() >= targetStats.mtime.getTime() ||
+ sourceStats.size !== targetStats.size) {
+ log("copy " + sourcePath + " " + targetPath + " (updated file)");
+ shell.cp("-f", sourceFullPath, targetFullPath);
+ updated = true;
+ }
+ }
+ }
+ } else if (targetStats) {
+ // The target exists but the source is null, so the target should be deleted.
+ if (targetStats.isDirectory()) {
+ log("rmdir " + targetPath + (copyAll ? "" : " (no source)"));
+ shell.rm("-rf", targetFullPath);
+ } else {
+ log("delete " + targetPath + (copyAll ? "" : " (no source)"));
+ shell.rm("-f", targetFullPath);
+ }
+ updated = true;
+ }
+
+ return updated;
+}
+
+/**
+ * Helper for updatePath and updatePaths functions. Queries stats for source and target
+ * and ensures target directory exists before copying a file.
+ */
+function updatePathInternal(sourcePath, targetPath, options, log) {
+ var rootDir = (options && options.rootDir) || "";
+ var targetFullPath = path.join(rootDir, targetPath);
+ var targetStats = fs.existsSync(targetFullPath) ? fs.statSync(targetFullPath) : null;
+ var sourceStats = null;
+
+ if (sourcePath) {
+ // A non-null source path was specified. It should exist.
+ var sourceFullPath = path.join(rootDir, sourcePath);
+ if (!fs.existsSync(sourceFullPath)) {
+ throw new Error("Source path does not exist: " + sourcePath);
+ }
+
+ sourceStats = fs.statSync(sourceFullPath);
+
+ // Create the target's parent directory if it doesn't exist.
+ var parentDir = path.dirname(targetFullPath);
+ if (!fs.existsSync(parentDir)) {
+ shell.mkdir("-p", parentDir);
+ }
+ }
+
+ return updatePathWithStats(sourcePath, sourceStats, targetPath, targetStats, options, log);
+}
+
+/**
+ * Updates a target file or directory with a source file or directory. (Directory updates are
+ * not recursive.)
+ *
+ * @param {?string} sourcePath Source file or directory to be used to update the
+ * destination. If the source is null, then the destination is deleted if it exists.
+ * @param {string} targetPath Required destination file or directory to be updated. If it does
+ * not exist, it will be created.
+ * @param {Object} [options] Optional additional parameters for the update.
+ * @param {string} [options.rootDir] Optional root directory (such as a project) to which target
+ * and source path parameters are relative; may be omitted if the paths are absolute. The
+ * rootDir is always omitted from any logged paths, to make the logs easier to read.
+ * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times.
+ * Otherwise, a file is copied if the source's last-modified time is greather than or
+ * equal to the target's last-modified time, or if the file sizes are different.
+ * @param {loggingCallback} [log] Optional logging callback that takes a string message
+ * describing any file operations that are performed.
+ * @return {boolean} true if any changes were made, or false if the force flag is not set
+ * and everything was up to date
+ */
+function updatePath(sourcePath, targetPath, options, log) {
+ if (sourcePath !== null && typeof sourcePath !== "string") {
+ throw new Error("A source path (or null) is required.");
+ }
+
+ if (!targetPath || typeof targetPath !== "string") {
+ throw new Error("A target path is required.");
+ }
+
+ log = log || function(message) { };
+
+ return updatePathInternal(sourcePath, targetPath, options, log);
+}
+
+/**
+ * Updates files and directories based on a mapping from target paths to source paths. Targets
+ * with null sources in the map are deleted.
+ *
+ * @param {Object} pathMap A dictionary mapping from target paths to source paths.
+ * @param {Object} [options] Optional additional parameters for the update.
+ * @param {string} [options.rootDir] Optional root directory (such as a project) to which target
+ * and source path parameters are relative; may be omitted if the paths are absolute. The
+ * rootDir is always omitted from any logged paths, to make the logs easier to read.
+ * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times.
+ * Otherwise, a file is copied if the source's last-modified time is greather than or
+ * equal to the target's last-modified time, or if the file sizes are different.
+ * @param {loggingCallback} [log] Optional logging callback that takes a string message
+ * describing any file operations that are performed.
+ * @return {boolean} true if any changes were made, or false if the force flag is not set
+ * and everything was up to date
+ */
+function updatePaths(pathMap, options, log) {
+ if (!pathMap || typeof pathMap !== "object" || Array.isArray(pathMap)) {
+ throw new Error("An object mapping from target paths to source paths is required.");
+ }
+
+ log = log || function(message) { };
+
+ var updated = false;
+
+ // Iterate in sorted order to ensure directories are created before files under them.
+ Object.keys(pathMap).sort().forEach(function (targetPath) {
+ var sourcePath = pathMap[targetPath];
+ updated = updatePathInternal(sourcePath, targetPath, options, log) || updated;
+ });
+
+ return updated;
+}
+
+/**
+ * Updates a target directory with merged files and subdirectories from source directories.
+ *
+ * @param {string|string[]} sourceDirs Required source directory or array of source directories
+ * to be merged into the target. The directories are listed in order of precedence; files in
+ * directories later in the array supersede files in directories earlier in the array
+ * (regardless of timestamps).
+ * @param {string} targetDir Required destination directory to be updated. If it does not exist,
+ * it will be created. If it exists, newer files from source directories will be copied over,
+ * and files missing in the source directories will be deleted.
+ * @param {Object} [options] Optional additional parameters for the update.
+ * @param {string} [options.rootDir] Optional root directory (such as a project) to which target
+ * and source path parameters are relative; may be omitted if the paths are absolute. The
+ * rootDir is always omitted from any logged paths, to make the logs easier to read.
+ * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times.
+ * Otherwise, a file is copied if the source's last-modified time is greather than or
+ * equal to the target's last-modified time, or if the file sizes are different.
+ * @param {string|string[]} [options.include] Optional glob string or array of glob strings that
+ * are tested against both target and source relative paths to determine if they are included
+ * in the merge-and-update. If unspecified, all items are included.
+ * @param {string|string[]} [options.exclude] Optional glob string or array of glob strings that
+ * are tested against both target and source relative paths to determine if they are excluded
+ * from the merge-and-update. Exclusions override inclusions. If unspecified, no items are
+ * excluded.
+ * @param {loggingCallback} [log] Optional logging callback that takes a string message
+ * describing any file operations that are performed.
+ * @return {boolean} true if any changes were made, or false if the force flag is not set
+ * and everything was up to date
+ */
+function mergeAndUpdateDir(sourceDirs, targetDir, options, log) {
+ if (sourceDirs && typeof sourceDirs === "string") {
+ sourceDirs = [ sourceDirs ];
+ } else if (!Array.isArray(sourceDirs)) {
+ throw new Error("A source directory path or array of paths is required.");
+ }
+
+ if (!targetDir || typeof targetDir !== "string") {
+ throw new Error("A target directory path is required.");
+ }
+
+ log = log || function(message) { };
+
+ var rootDir = (options && options.rootDir) || "";
+
+ var include = (options && options.include) || [ "**" ];
+ if (typeof include === "string") {
+ include = [ include ];
+ } else if (!Array.isArray(include)) {
+ throw new Error("Include parameter must be a glob string or array of glob strings.");
+ }
+
+ var exclude = (options && options.exclude) || [];
+ if (typeof exclude === "string") {
+ exclude = [ exclude ];
+ } else if (!Array.isArray(exclude)) {
+ throw new Error("Exclude parameter must be a glob string or array of glob strings.");
+ }
+
+ // Scan the files in each of the source directories.
+ var sourceMaps = sourceDirs.map(function (sourceDir) {
+ return path.join(rootDir, sourceDir);
+ }).map(function (sourcePath) {
+ if (!fs.existsSync(sourcePath)) {
+ throw new Error("Source directory does not exist: " + sourcePath);
+ }
+ return mapDirectory(rootDir, path.relative(rootDir, sourcePath), include, exclude);
+ });
+
+ // Scan the files in the target directory, if it exists.
+ var targetMap = {};
+ var targetFullPath = path.join(rootDir, targetDir);
+ if (fs.existsSync(targetFullPath)) {
+ targetMap = mapDirectory(rootDir, targetDir, include, exclude);
+ }
+
+ var pathMap = mergePathMaps(sourceMaps, targetMap, targetDir);
+
+ var updated = false;
+
+ // Iterate in sorted order to ensure directories are created before files under them.
+ Object.keys(pathMap).sort().forEach(function (subPath) {
+ var entry = pathMap[subPath];
+ updated = updatePathWithStats(
+ entry.sourcePath,
+ entry.sourceStats,
+ entry.targetPath,
+ entry.targetStats,
+ options,
+ log) || updated;
+ });
+
+ return updated;
+}
+
+/**
+ * Creates a dictionary map of all files and directories under a path.
+ */
+function mapDirectory(rootDir, subDir, include, exclude) {
+ var dirMap = { "": { subDir: subDir, stats: fs.statSync(path.join(rootDir, subDir)) } };
+ mapSubdirectory(rootDir, subDir, "", include, exclude, dirMap);
+ return dirMap;
+
+ function mapSubdirectory(rootDir, subDir, relativeDir, include, exclude, dirMap) {
+ var itemMapped = false;
+ var items = fs.readdirSync(path.join(rootDir, subDir, relativeDir));
+
+ items.forEach(function(item) {
+ var relativePath = path.join(relativeDir, item);
+ if(!matchGlobArray(relativePath, exclude)) {
+ // Stats obtained here (required at least to know where to recurse in directories)
+ // are saved for later, where the modified times may also be used. This minimizes
+ // the number of file I/O operations performed.
+ var fullPath = path.join(rootDir, subDir, relativePath);
+ var stats = fs.statSync(fullPath);
+
+ if (stats.isDirectory()) {
+ // Directories are included if either something under them is included or they
+ // match an include glob.
+ if (mapSubdirectory(rootDir, subDir, relativePath, include, exclude, dirMap) ||
+ matchGlobArray(relativePath, include)) {
+ dirMap[relativePath] = { subDir: subDir, stats: stats };
+ itemMapped = true;
+ }
+ } else if (stats.isFile()) {
+ // Files are included only if they match an include glob.
+ if (matchGlobArray(relativePath, include)) {
+ dirMap[relativePath] = { subDir: subDir, stats: stats };
+ itemMapped = true;
+ }
+ }
+ }
+ });
+ return itemMapped;
+ }
+
+ function matchGlobArray(path, globs) {
+ return globs.some(function(elem) {
+ return minimatch(path, elem, {dot:true});
+ });
+ }
+}
+
+/**
+ * Merges together multiple source maps and a target map into a single mapping from
+ * relative paths to objects with target and source paths and stats.
+ */
+function mergePathMaps(sourceMaps, targetMap, targetDir) {
+ // Merge multiple source maps together, along with target path info.
+ // Entries in later source maps override those in earlier source maps.
+ // Target stats will be filled in below for targets that exist.
+ var pathMap = {};
+ sourceMaps.forEach(function (sourceMap) {
+ Object.keys(sourceMap).forEach(function(sourceSubPath){
+ var sourceEntry = sourceMap[sourceSubPath];
+ pathMap[sourceSubPath] = {
+ targetPath: path.join(targetDir, sourceSubPath),
+ targetStats: null,
+ sourcePath: path.join(sourceEntry.subDir, sourceSubPath),
+ sourceStats: sourceEntry.stats
+ };
+ });
+ });
+
+ // Fill in target stats for targets that exist, and create entries
+ // for targets that don't have any corresponding sources.
+ Object.keys(targetMap).forEach(function(subPath){
+ var entry = pathMap[subPath];
+ if (entry) {
+ entry.targetStats = targetMap[subPath].stats;
+ } else {
+ pathMap[subPath] = {
+ targetPath: path.join(targetDir, subPath),
+ targetStats: targetMap[subPath].stats,
+ sourcePath: null,
+ sourceStats: null
+ };
+ }
+ });
+
+ return pathMap;
+}
+
+module.exports = {
+ updatePath: updatePath,
+ updatePaths: updatePaths,
+ mergeAndUpdateDir: mergeAndUpdateDir
+};
+
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PlatformJson.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PlatformJson.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PlatformJson.js
new file mode 100644
index 0000000..ab94b5f
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PlatformJson.js
@@ -0,0 +1,278 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+/* jshint sub:true */
+
+var fs = require('fs');
+var path = require('path');
+var shelljs = require('shelljs');
+var mungeutil = require('./ConfigChanges/munge-util');
+var pluginMappernto = require('cordova-registry-mapper').newToOld;
+var pluginMapperotn = require('cordova-registry-mapper').oldToNew;
+
+function PlatformJson(filePath, platform, root) {
+ this.filePath = filePath;
+ this.platform = platform;
+ this.root = fix_munge(root || {});
+}
+
+PlatformJson.load = function(plugins_dir, platform) {
+ var filePath = path.join(plugins_dir, platform + '.json');
+ var root = null;
+ if (fs.existsSync(filePath)) {
+ root = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
+ }
+ return new PlatformJson(filePath, platform, root);
+};
+
+PlatformJson.prototype.save = function() {
+ shelljs.mkdir('-p', path.dirname(this.filePath));
+ fs.writeFileSync(this.filePath, JSON.stringify(this.root, null, 4), 'utf-8');
+};
+
+/**
+ * Indicates whether the specified plugin is installed as a top-level (not as
+ * dependency to others)
+ * @method function
+ * @param {String} pluginId A plugin id to check for.
+ * @return {Boolean} true if plugin installed as top-level, otherwise false.
+ */
+PlatformJson.prototype.isPluginTopLevel = function(pluginId) {
+ var installedPlugins = this.root.installed_plugins;
+ return installedPlugins[pluginId] ||
+ installedPlugins[pluginMappernto[pluginId]] ||
+ installedPlugins[pluginMapperotn[pluginId]];
+};
+
+/**
+ * Indicates whether the specified plugin is installed as a dependency to other
+ * plugin.
+ * @method function
+ * @param {String} pluginId A plugin id to check for.
+ * @return {Boolean} true if plugin installed as a dependency, otherwise false.
+ */
+PlatformJson.prototype.isPluginDependent = function(pluginId) {
+ var dependentPlugins = this.root.dependent_plugins;
+ return dependentPlugins[pluginId] ||
+ dependentPlugins[pluginMappernto[pluginId]] ||
+ dependentPlugins[pluginMapperotn[pluginId]];
+};
+
+/**
+ * Indicates whether plugin is installed either as top-level or as dependency.
+ * @method function
+ * @param {String} pluginId A plugin id to check for.
+ * @return {Boolean} true if plugin installed, otherwise false.
+ */
+PlatformJson.prototype.isPluginInstalled = function(pluginId) {
+ return this.isPluginTopLevel(pluginId) ||
+ this.isPluginDependent(pluginId);
+};
+
+PlatformJson.prototype.addPlugin = function(pluginId, variables, isTopLevel) {
+ var pluginsList = isTopLevel ?
+ this.root.installed_plugins :
+ this.root.dependent_plugins;
+
+ pluginsList[pluginId] = variables;
+
+ return this;
+};
+
+/**
+ * @chaining
+ * Generates and adds metadata for provided plugin into associated <platform>.json file
+ *
+ * @param {PluginInfo} pluginInfo A pluginInfo instance to add metadata from
+ * @returns {this} Current PlatformJson instance to allow calls chaining
+ */
+PlatformJson.prototype.addPluginMetadata = function (pluginInfo) {
+
+ var installedModules = this.root.modules || [];
+
+ var installedPaths = installedModules.map(function (installedModule) {
+ return installedModule.file;
+ });
+
+ var modulesToInstall = pluginInfo.getJsModules(this.platform)
+ .map(function (module) {
+ return new ModuleMetadata(pluginInfo.id, module);
+ })
+ .filter(function (metadata) {
+ // Filter out modules which are already added to metadata
+ return installedPaths.indexOf(metadata.file) === -1;
+ });
+
+ this.root.modules = installedModules.concat(modulesToInstall);
+
+ this.root.plugin_metadata = this.root.plugin_metadata || {};
+ this.root.plugin_metadata[pluginInfo.id] = pluginInfo.version;
+
+ return this;
+};
+
+PlatformJson.prototype.removePlugin = function(pluginId, isTopLevel) {
+ var pluginsList = isTopLevel ?
+ this.root.installed_plugins :
+ this.root.dependent_plugins;
+
+ delete pluginsList[pluginId];
+
+ return this;
+};
+
+/**
+ * @chaining
+ * Removes metadata for provided plugin from associated file
+ *
+ * @param {PluginInfo} pluginInfo A PluginInfo instance to which modules' metadata
+ * we need to remove
+ *
+ * @returns {this} Current PlatformJson instance to allow calls chaining
+ */
+PlatformJson.prototype.removePluginMetadata = function (pluginInfo) {
+ var modulesToRemove = pluginInfo.getJsModules(this.platform)
+ .map(function (jsModule) {
+ return ['plugins', pluginInfo.id, jsModule.src].join('/');
+ });
+
+ var installedModules = this.root.modules || [];
+ this.root.modules = installedModules
+ .filter(function (installedModule) {
+ // Leave only those metadatas which 'file' is not in removed modules
+ return (modulesToRemove.indexOf(installedModule.file) === -1);
+ });
+
+ if (this.root.plugin_metadata) {
+ delete this.root.plugin_metadata[pluginInfo.id];
+ }
+
+ return this;
+};
+
+PlatformJson.prototype.addInstalledPluginToPrepareQueue = function(pluginDirName, vars, is_top_level, force) {
+ this.root.prepare_queue.installed.push({'plugin':pluginDirName, 'vars':vars, 'topLevel':is_top_level, 'force':force});
+};
+
+PlatformJson.prototype.addUninstalledPluginToPrepareQueue = function(pluginId, is_top_level) {
+ this.root.prepare_queue.uninstalled.push({'plugin':pluginId, 'id':pluginId, 'topLevel':is_top_level});
+};
+
+/**
+ * Moves plugin, specified by id to top-level plugins. If plugin is top-level
+ * already, then does nothing.
+ * @method function
+ * @param {String} pluginId A plugin id to make top-level.
+ * @return {PlatformJson} PlatformJson instance.
+ */
+PlatformJson.prototype.makeTopLevel = function(pluginId) {
+ var plugin = this.root.dependent_plugins[pluginId];
+ if (plugin) {
+ delete this.root.dependent_plugins[pluginId];
+ this.root.installed_plugins[pluginId] = plugin;
+ }
+ return this;
+};
+
+/**
+ * Generates a metadata for all installed plugins and js modules. The resultant
+ * string is ready to be written to 'cordova_plugins.js'
+ *
+ * @returns {String} cordova_plugins.js contents
+ */
+PlatformJson.prototype.generateMetadata = function () {
+ return [
+ 'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {',
+ 'module.exports = ' + JSON.stringify(this.root.modules, null, 4) + ';',
+ 'module.exports.metadata = ',
+ '// TOP OF METADATA',
+ JSON.stringify(this.root.plugin_metadata, null, 4) + ';',
+ '// BOTTOM OF METADATA',
+ '});' // Close cordova.define.
+ ].join('\n');
+};
+
+/**
+ * @chaining
+ * Generates and then saves metadata to specified file. Doesn't check if file exists.
+ *
+ * @param {String} destination File metadata will be written to
+ * @return {PlatformJson} PlatformJson instance
+ */
+PlatformJson.prototype.generateAndSaveMetadata = function (destination) {
+ var meta = this.generateMetadata();
+ shelljs.mkdir('-p', path.dirname(destination));
+ fs.writeFileSync(destination, meta, 'utf-8');
+
+ return this;
+};
+
+// convert a munge from the old format ([file][parent][xml] = count) to the current one
+function fix_munge(root) {
+ root.prepare_queue = root.prepare_queue || {installed:[], uninstalled:[]};
+ root.config_munge = root.config_munge || {files: {}};
+ root.installed_plugins = root.installed_plugins || {};
+ root.dependent_plugins = root.dependent_plugins || {};
+
+ var munge = root.config_munge;
+ if (!munge.files) {
+ var new_munge = { files: {} };
+ for (var file in munge) {
+ for (var selector in munge[file]) {
+ for (var xml_child in munge[file][selector]) {
+ var val = parseInt(munge[file][selector][xml_child]);
+ for (var i = 0; i < val; i++) {
+ mungeutil.deep_add(new_munge, [file, selector, { xml: xml_child, count: val }]);
+ }
+ }
+ }
+ }
+ root.config_munge = new_munge;
+ }
+
+ return root;
+}
+
+/**
+ * @constructor
+ * @class ModuleMetadata
+ *
+ * Creates a ModuleMetadata object that represents module entry in 'cordova_plugins.js'
+ * file at run time
+ *
+ * @param {String} pluginId Plugin id where this module installed from
+ * @param (JsModule|Object) jsModule A js-module entry from PluginInfo class to generate metadata for
+ */
+function ModuleMetadata (pluginId, jsModule) {
+
+ if (!pluginId) throw new TypeError('pluginId argument must be a valid plugin id');
+ if (!jsModule.src && !jsModule.name) throw new TypeError('jsModule argument must contain src or/and name properties');
+
+ this.id = pluginId + '.' + ( jsModule.name || jsModule.src.match(/([^\/]+)\.js/)[1] );
+ this.file = ['plugins', pluginId, jsModule.src].join('/');
+ this.pluginId = pluginId;
+
+ if (jsModule.clobbers && jsModule.clobbers.length > 0) {
+ this.clobbers = jsModule.clobbers.map(function(o) { return o.target; });
+ }
+ if (jsModule.merges && jsModule.merges.length > 0) {
+ this.merges = jsModule.merges.map(function(o) { return o.target; });
+ }
+ if (jsModule.runs) {
+ this.runs = true;
+ }
+}
+
+module.exports = PlatformJson;
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PluginInfo/PluginInfo.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PluginInfo/PluginInfo.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PluginInfo/PluginInfo.js
new file mode 100644
index 0000000..44501fa
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PluginInfo/PluginInfo.js
@@ -0,0 +1,425 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* jshint sub:true, laxcomma:true, laxbreak:true */
+
+/*
+A class for holidng the information currently stored in plugin.xml
+It should also be able to answer questions like whether the plugin
+is compatible with a given engine version.
+
+TODO (kamrik): refactor this to not use sync functions and return promises.
+*/
+
+
+var path = require('path')
+ , fs = require('fs')
+ , xml_helpers = require('../util/xml-helpers')
+ , CordovaError = require('../CordovaError/CordovaError')
+ ;
+
+function PluginInfo(dirname) {
+ var self = this;
+
+ // METHODS
+ // Defined inside the constructor to avoid the "this" binding problems.
+
+ // <preference> tag
+ // Example: <preference name="API_KEY" />
+ // Used to require a variable to be specified via --variable when installing the plugin.
+ // returns { key : default | null}
+ self.getPreferences = getPreferences;
+ function getPreferences(platform) {
+ return _getTags(self._et, 'preference', platform, _parsePreference)
+ .reduce(function (preferences, pref) {
+ preferences[pref.preference] = pref.default;
+ return preferences;
+ }, {});
+ }
+
+ function _parsePreference(prefTag) {
+ var name = prefTag.attrib.name.toUpperCase();
+ var def = prefTag.attrib.default || null;
+ return {preference: name, default: def};
+ }
+
+ // <asset>
+ self.getAssets = getAssets;
+ function getAssets(platform) {
+ var assets = _getTags(self._et, 'asset', platform, _parseAsset);
+ return assets;
+ }
+
+ function _parseAsset(tag) {
+ var src = tag.attrib.src;
+ var target = tag.attrib.target;
+
+ if ( !src || !target) {
+ var msg =
+ 'Malformed <asset> tag. Both "src" and "target" attributes'
+ + 'must be specified in\n'
+ + self.filepath
+ ;
+ throw new Error(msg);
+ }
+
+ var asset = {
+ itemType: 'asset',
+ src: src,
+ target: target
+ };
+ return asset;
+ }
+
+
+ // <dependency>
+ // Example:
+ // <dependency id="com.plugin.id"
+ // url="https://github.com/myuser/someplugin"
+ // commit="428931ada3891801"
+ // subdir="some/path/here" />
+ self.getDependencies = getDependencies;
+ function getDependencies(platform) {
+ var deps = _getTags(
+ self._et,
+ 'dependency',
+ platform,
+ _parseDependency
+ );
+ return deps;
+ }
+
+ function _parseDependency(tag) {
+ var dep =
+ { id : tag.attrib.id
+ , url : tag.attrib.url || ''
+ , subdir : tag.attrib.subdir || ''
+ , commit : tag.attrib.commit
+ };
+
+ dep.git_ref = dep.commit;
+
+ if ( !dep.id ) {
+ var msg =
+ '<dependency> tag is missing id attribute in '
+ + self.filepath
+ ;
+ throw new CordovaError(msg);
+ }
+ return dep;
+ }
+
+
+ // <config-file> tag
+ self.getConfigFiles = getConfigFiles;
+ function getConfigFiles(platform) {
+ var configFiles = _getTags(self._et, 'config-file', platform, _parseConfigFile);
+ return configFiles;
+ }
+
+ function _parseConfigFile(tag) {
+ var configFile =
+ { target : tag.attrib['target']
+ , parent : tag.attrib['parent']
+ , after : tag.attrib['after']
+ , xmls : tag.getchildren()
+ // To support demuxing via versions
+ , versions : tag.attrib['versions']
+ , deviceTarget: tag.attrib['device-target']
+ };
+ return configFile;
+ }
+
+ self.getEditConfigs = getEditConfigs;
+ function getEditConfigs(platform) {
+ var editConfigs = _getTags(self._et, 'edit-config', platform, _parseEditConfigs);
+ return editConfigs;
+ }
+
+ function _parseEditConfigs(tag) {
+ var editConfig =
+ { file : tag.attrib['file']
+ , target : tag.attrib['target']
+ , mode : tag.attrib['mode']
+ , xmls : tag.getchildren()
+ };
+ return editConfig;
+ }
+
+ // <info> tags, both global and within a <platform>
+ // TODO (kamrik): Do we ever use <info> under <platform>? Example wanted.
+ self.getInfo = getInfo;
+ function getInfo(platform) {
+ var infos = _getTags(
+ self._et,
+ 'info',
+ platform,
+ function(elem) { return elem.text; }
+ );
+ // Filter out any undefined or empty strings.
+ infos = infos.filter(Boolean);
+ return infos;
+ }
+
+ // <source-file>
+ // Examples:
+ // <source-file src="src/ios/someLib.a" framework="true" />
+ // <source-file src="src/ios/someLib.a" compiler-flags="-fno-objc-arc" />
+ self.getSourceFiles = getSourceFiles;
+ function getSourceFiles(platform) {
+ var sourceFiles = _getTagsInPlatform(self._et, 'source-file', platform, _parseSourceFile);
+ return sourceFiles;
+ }
+
+ function _parseSourceFile(tag) {
+ return {
+ itemType: 'source-file',
+ src: tag.attrib.src,
+ framework: isStrTrue(tag.attrib.framework),
+ weak: isStrTrue(tag.attrib.weak),
+ compilerFlags: tag.attrib['compiler-flags'],
+ targetDir: tag.attrib['target-dir']
+ };
+ }
+
+ // <header-file>
+ // Example:
+ // <header-file src="CDVFoo.h" />
+ self.getHeaderFiles = getHeaderFiles;
+ function getHeaderFiles(platform) {
+ var headerFiles = _getTagsInPlatform(self._et, 'header-file', platform, function(tag) {
+ return {
+ itemType: 'header-file',
+ src: tag.attrib.src,
+ targetDir: tag.attrib['target-dir']
+ };
+ });
+ return headerFiles;
+ }
+
+ // <resource-file>
+ // Example:
+ // <resource-file src="FooPluginStrings.xml" target="res/values/FooPluginStrings.xml" device-target="win" arch="x86" versions=">=8.1" />
+ self.getResourceFiles = getResourceFiles;
+ function getResourceFiles(platform) {
+ var resourceFiles = _getTagsInPlatform(self._et, 'resource-file', platform, function(tag) {
+ return {
+ itemType: 'resource-file',
+ src: tag.attrib.src,
+ target: tag.attrib.target,
+ versions: tag.attrib.versions,
+ deviceTarget: tag.attrib['device-target'],
+ arch: tag.attrib.arch,
+ reference: tag.attrib.reference
+ };
+ });
+ return resourceFiles;
+ }
+
+ // <lib-file>
+ // Example:
+ // <lib-file src="src/BlackBerry10/native/device/libfoo.so" arch="device" />
+ self.getLibFiles = getLibFiles;
+ function getLibFiles(platform) {
+ var libFiles = _getTagsInPlatform(self._et, 'lib-file', platform, function(tag) {
+ return {
+ itemType: 'lib-file',
+ src: tag.attrib.src,
+ arch: tag.attrib.arch,
+ Include: tag.attrib.Include,
+ versions: tag.attrib.versions,
+ deviceTarget: tag.attrib['device-target'] || tag.attrib.target
+ };
+ });
+ return libFiles;
+ }
+
+ // <hook>
+ // Example:
+ // <hook type="before_build" src="scripts/beforeBuild.js" />
+ self.getHookScripts = getHookScripts;
+ function getHookScripts(hook, platforms) {
+ var scriptElements = self._et.findall('./hook');
+
+ if(platforms) {
+ platforms.forEach(function (platform) {
+ scriptElements = scriptElements.concat(self._et.findall('./platform[@name="' + platform + '"]/hook'));
+ });
+ }
+
+ function filterScriptByHookType(el) {
+ return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
+ }
+
+ return scriptElements.filter(filterScriptByHookType);
+ }
+
+ self.getJsModules = getJsModules;
+ function getJsModules(platform) {
+ var modules = _getTags(self._et, 'js-module', platform, _parseJsModule);
+ return modules;
+ }
+
+ function _parseJsModule(tag) {
+ var ret = {
+ itemType: 'js-module',
+ name: tag.attrib.name,
+ src: tag.attrib.src,
+ clobbers: tag.findall('clobbers').map(function(tag) { return { target: tag.attrib.target }; }),
+ merges: tag.findall('merges').map(function(tag) { return { target: tag.attrib.target }; }),
+ runs: tag.findall('runs').length > 0
+ };
+
+ return ret;
+ }
+
+ self.getEngines = function() {
+ return self._et.findall('engines/engine').map(function(n) {
+ return {
+ name: n.attrib.name,
+ version: n.attrib.version,
+ platform: n.attrib.platform,
+ scriptSrc: n.attrib.scriptSrc
+ };
+ });
+ };
+
+ self.getPlatforms = function() {
+ return self._et.findall('platform').map(function(n) {
+ return { name: n.attrib.name };
+ });
+ };
+
+ self.getPlatformsArray = function() {
+ return self._et.findall('platform').map(function(n) {
+ return n.attrib.name;
+ });
+ };
+ self.getFrameworks = function(platform) {
+ return _getTags(self._et, 'framework', platform, function(el) {
+ var ret = {
+ itemType: 'framework',
+ type: el.attrib.type,
+ parent: el.attrib.parent,
+ custom: isStrTrue(el.attrib.custom),
+ src: el.attrib.src,
+ spec: el.attrib.spec,
+ weak: isStrTrue(el.attrib.weak),
+ versions: el.attrib.versions,
+ targetDir: el.attrib['target-dir'],
+ deviceTarget: el.attrib['device-target'] || el.attrib.target,
+ arch: el.attrib.arch,
+ implementation: el.attrib.implementation
+ };
+ return ret;
+ });
+ };
+
+ self.getFilesAndFrameworks = getFilesAndFrameworks;
+ function getFilesAndFrameworks(platform) {
+ // Please avoid changing the order of the calls below, files will be
+ // installed in this order.
+ var items = [].concat(
+ self.getSourceFiles(platform),
+ self.getHeaderFiles(platform),
+ self.getResourceFiles(platform),
+ self.getFrameworks(platform),
+ self.getLibFiles(platform)
+ );
+ return items;
+ }
+ ///// End of PluginInfo methods /////
+
+
+ ///// PluginInfo Constructor logic /////
+ self.filepath = path.join(dirname, 'plugin.xml');
+ if (!fs.existsSync(self.filepath)) {
+ throw new CordovaError('Cannot find plugin.xml for plugin "' + path.basename(dirname) + '". Please try adding it again.');
+ }
+
+ self.dir = dirname;
+ var et = self._et = xml_helpers.parseElementtreeSync(self.filepath);
+ var pelem = et.getroot();
+ self.id = pelem.attrib.id;
+ self.version = pelem.attrib.version;
+
+ // Optional fields
+ self.name = pelem.findtext('name');
+ self.description = pelem.findtext('description');
+ self.license = pelem.findtext('license');
+ self.repo = pelem.findtext('repo');
+ self.issue = pelem.findtext('issue');
+ self.keywords = pelem.findtext('keywords');
+ self.info = pelem.findtext('info');
+ if (self.keywords) {
+ self.keywords = self.keywords.split(',').map( function(s) { return s.trim(); } );
+ }
+ self.getKeywordsAndPlatforms = function () {
+ var ret = self.keywords || [];
+ return ret.concat('ecosystem:cordova').concat(addCordova(self.getPlatformsArray()));
+ };
+} // End of PluginInfo constructor.
+
+// Helper function used to prefix every element of an array with cordova-
+// Useful when we want to modify platforms to be cordova-platform
+function addCordova(someArray) {
+ var newArray = someArray.map(function(element) {
+ return 'cordova-' + element;
+ });
+ return newArray;
+}
+
+// Helper function used by most of the getSomething methods of PluginInfo.
+// Get all elements of a given name. Both in root and in platform sections
+// for the given platform. If transform is given and is a function, it is
+// applied to each element.
+function _getTags(pelem, tag, platform, transform) {
+ var platformTag = pelem.find('./platform[@name="' + platform + '"]');
+ var tagsInRoot = pelem.findall(tag);
+ tagsInRoot = tagsInRoot || [];
+ var tagsInPlatform = platformTag ? platformTag.findall(tag) : [];
+ var tags = tagsInRoot.concat(tagsInPlatform);
+ if ( typeof transform === 'function' ) {
+ tags = tags.map(transform);
+ }
+ return tags;
+}
+
+// Same as _getTags() but only looks inside a platform section.
+function _getTagsInPlatform(pelem, tag, platform, transform) {
+ var platformTag = pelem.find('./platform[@name="' + platform + '"]');
+ var tags = platformTag ? platformTag.findall(tag) : [];
+ if ( typeof transform === 'function' ) {
+ tags = tags.map(transform);
+ }
+ return tags;
+}
+
+// Check if x is a string 'true'.
+function isStrTrue(x) {
+ return String(x).toLowerCase() == 'true';
+}
+
+module.exports = PluginInfo;
+// Backwards compat:
+PluginInfo.PluginInfo = PluginInfo;
+PluginInfo.loadPluginsDir = function(dir) {
+ var PluginInfoProvider = require('./PluginInfoProvider');
+ return new PluginInfoProvider().getAllWithinSearchPath(dir);
+};
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org