You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ka...@apache.org on 2014/05/01 20:32:09 UTC
[24/53] [abbrv] Split out cordova-lib: move cordova-plugman files
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/src/plugman/util/config-changes.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/util/config-changes.js b/cordova-lib/src/plugman/util/config-changes.js
new file mode 100644
index 0000000..67adfb6
--- /dev/null
+++ b/cordova-lib/src/plugman/util/config-changes.js
@@ -0,0 +1,812 @@
+/*
+ *
+ * Copyright 2013 Anis Kadri
+ *
+ * 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.
+ *
+*/
+
+/*
+ * This module deals with shared configuration / dependency "stuff". That is:
+ * - XML configuration files such as config.xml, AndroidManifest.xml or WMAppManifest.xml.
+ * - plist files in iOS
+ * - pbxproj files in iOS
+ * Essentially, any type of shared resources that we need to handle with awareness
+ * of how potentially multiple plugins depend on a single shared resource, should be
+ * handled in this module.
+ *
+ * The implementation uses an object as a hash table, with "leaves" of the table tracking
+ * reference counts.
+ */
+
+/* jshint node:true, sub:true, unused:true, indent:4 */
+
+var fs = require('fs'),
+ path = require('path'),
+ glob = require('glob'),
+ plist = require('plist-with-patches'),
+ bplist = require('bplist-parser'),
+ xcode = require('xcode'),
+ et = require('elementtree'),
+ _ = require('underscore'),
+ xml_helpers = require('./../util/xml-helpers'),
+ platforms = require('./../platforms'),
+ events = require('./../events'),
+ plist_helpers = require('./../util/plist-helpers');
+
+
+// These frameworks are required by cordova-ios by default. We should never add/remove them.
+var keep_these_frameworks = [
+ 'MobileCoreServices.framework',
+ 'CoreGraphics.framework',
+ 'CoreLocation.framework',
+ 'AssetsLibrary.framework'
+];
+
+
+exports.PlatformMunger = PlatformMunger;
+
+/******************************************************************************
+Adapters to keep the current refactoring effort to within this file
+******************************************************************************/
+exports.add_plugin_changes = function(platform, project_dir, plugins_dir, plugin_id, plugin_vars, is_top_level, should_increment, cache) {
+ var munger = new PlatformMunger(platform, project_dir, plugins_dir);
+ munger.add_plugin_changes(plugin_id, plugin_vars, is_top_level, should_increment, cache);
+ munger.save_all();
+};
+
+exports.remove_plugin_changes = function(platform, project_dir, plugins_dir, plugin_name, plugin_id, is_top_level, should_decrement) {
+ // TODO: should_decrement parameter is never used, remove it here and wherever called
+ var munger = new PlatformMunger(platform, project_dir, plugins_dir);
+ munger.remove_plugin_changes(plugin_name, plugin_id, is_top_level);
+ munger.save_all();
+};
+
+exports.process = function(plugins_dir, project_dir, platform) {
+ var munger = new PlatformMunger(platform, project_dir, plugins_dir);
+ munger.process();
+ munger.save_all();
+};
+
+exports.get_munge_change = function(munge, keys) {
+ return deep_find.apply(null, arguments);
+}
+
+/******************************************************************************/
+
+
+exports.add_installed_plugin_to_prepare_queue = add_installed_plugin_to_prepare_queue;
+function add_installed_plugin_to_prepare_queue(plugins_dir, plugin, platform, vars, is_top_level) {
+ checkPlatform(platform);
+ var config = exports.get_platform_json(plugins_dir, platform);
+ config.prepare_queue.installed.push({'plugin':plugin, 'vars':vars, 'topLevel':is_top_level});
+ exports.save_platform_json(config, plugins_dir, platform);
+}
+
+exports.add_uninstalled_plugin_to_prepare_queue = add_uninstalled_plugin_to_prepare_queue;
+function add_uninstalled_plugin_to_prepare_queue(plugins_dir, plugin, platform, is_top_level) {
+ checkPlatform(platform);
+
+ var plugin_xml = xml_helpers.parseElementtreeSync(path.join(plugins_dir, plugin, 'plugin.xml'));
+ var config = exports.get_platform_json(plugins_dir, platform);
+ config.prepare_queue.uninstalled.push({'plugin':plugin, 'id':plugin_xml.getroot().attrib['id'], 'topLevel':is_top_level});
+ exports.save_platform_json(config, plugins_dir, platform);
+}
+
+
+/******************************************************************************
+* PlatformMunger class
+*
+* Can deal with config file of a single project.
+* Parsed config files are cached in a ConfigKeeper object.
+******************************************************************************/
+function PlatformMunger(platform, project_dir, plugins_dir) {
+ checkPlatform(platform);
+ this.platform = platform;
+ this.project_dir = project_dir;
+ this.plugins_dir = plugins_dir;
+ this.platform_handler = platforms[platform];
+ this.config_keeper = new ConfigKeeper();
+}
+
+// Write out all unsaved files.
+PlatformMunger.prototype.save_all = PlatformMunger_save_all;
+function PlatformMunger_save_all() {
+ this.config_keeper.save_all();
+}
+
+// Apply a munge object to a single config file.
+// The remove parameter tells whether to add the change or remove it.
+PlatformMunger.prototype.apply_file_munge = PlatformMunger_apply_file_munge;
+function PlatformMunger_apply_file_munge(file, munge, remove) {
+ var self = this;
+ var xml_child;
+
+ if ( file === 'framework' && self.platform === 'ios' ) {
+ // ios pbxproj file
+ var pbxproj = self.config_keeper.get(self.project_dir, self.platform, 'framework');
+ for (var src in munge.parents) {
+ for (xml_child in munge.parents[src]) {
+ var xml = munge.parents[src][xml_child].xml;
+ // Only add the framework if it's not a cordova-ios core framework
+ if (keep_these_frameworks.indexOf(src) == -1) {
+ // xml_child in this case is whether the framework should use weak or not
+ if (remove) {
+ pbxproj.data.removeFramework(src);
+ } else {
+ pbxproj.data.addFramework(src, {weak: (xml === 'true')});
+ }
+ pbxproj.is_changed = true;
+ }
+ }
+ }
+ } else {
+ // all other types of files
+ for (var selector in munge.parents) {
+ for (xml_child in munge.parents[selector]) {
+ // this xml child is new, graft it (only if config file exists)
+ var config_file = self.config_keeper.get(self.project_dir, self.platform, file);
+ if (config_file.exists) {
+ if (remove) config_file.prune_child(selector, munge.parents[selector][xml_child]);
+ else config_file.graft_child(selector, munge.parents[selector][xml_child]);
+ }
+ }
+ }
+ }
+}
+
+
+PlatformMunger.prototype.remove_plugin_changes = remove_plugin_changes;
+function remove_plugin_changes(plugin_name, plugin_id, is_top_level) {
+ var self = this;
+ var platform_config = exports.get_platform_json(self.plugins_dir, self.platform);
+ var plugin_dir = path.join(self.plugins_dir, plugin_name);
+ var plugin_vars = (is_top_level ? platform_config.installed_plugins[plugin_id] : platform_config.dependent_plugins[plugin_id]);
+
+ // get config munge, aka how did this plugin change various config files
+ var config_munge = self.generate_plugin_config_munge(plugin_dir, plugin_vars);
+ // global munge looks at all plugins' changes to config files
+ var global_munge = platform_config.config_munge;
+ var munge = decrement_munge(global_munge, config_munge);
+
+ for (var file in munge.files) {
+ if (file == 'plugins-plist' && self.platform == 'ios') {
+ // TODO: remove this check and <plugins-plist> sections in spec/plugins/../plugin.xml files.
+ events.emit(
+ 'warn',
+ 'WARNING: Plugin "' + plugin_id + '" uses <plugins-plist> element(s), ' +
+ 'which are no longer supported. Support has been removed as of Cordova 3.4.'
+ );
+ continue;
+ }
+ self.apply_file_munge(file, munge.files[file], /* remove = */ true);
+ }
+
+ // Remove from installed_plugins
+ if (is_top_level) {
+ delete platform_config.installed_plugins[plugin_id];
+ } else {
+ delete platform_config.dependent_plugins[plugin_id];
+ }
+
+ // save
+ exports.save_platform_json(platform_config, self.plugins_dir, self.platform);
+}
+
+
+PlatformMunger.prototype.add_plugin_changes = add_plugin_changes;
+function add_plugin_changes(plugin_id, plugin_vars, is_top_level, should_increment) {
+ var self = this;
+ var platform_config = exports.get_platform_json(self.plugins_dir, self.platform);
+ var plugin_dir = path.join(self.plugins_dir, plugin_id);
+
+ var plugin_config = self.config_keeper.get(plugin_dir, '', 'plugin.xml');
+ plugin_id = plugin_config.data.getroot().attrib.id;
+
+ // get config munge, aka how should this plugin change various config files
+ var config_munge = self.generate_plugin_config_munge(plugin_dir, plugin_vars);
+ // global munge looks at all plugins' changes to config files
+
+ // TODO: The should_increment param is only used by cordova-cli and is going away soon.
+ // If should_increment is set to false, avoid modifying the global_munge (use clone)
+ // and apply the entire config_munge because it's already a proper subset of the global_munge.
+ var munge, global_munge;
+ if (should_increment) {
+ global_munge = platform_config.config_munge;
+ munge = increment_munge(global_munge, config_munge);
+ } else {
+ global_munge = clone_munge(platform_config.config_munge);
+ munge = config_munge;
+ }
+
+ for (var file in munge.files) {
+ // TODO: remove this warning some time after 3.4 is out.
+ if (file == 'plugins-plist' && self.platform == 'ios') {
+ events.emit(
+ 'warn',
+ 'WARNING: Plugin "' + plugin_id + '" uses <plugins-plist> element(s), ' +
+ 'which are no longer supported. Support has been removed as of Cordova 3.4.'
+ );
+ continue;
+ }
+ self.apply_file_munge(file, munge.files[file]);
+ }
+
+ // Move to installed_plugins if it is a top-level plugin
+ if (is_top_level) {
+ platform_config.installed_plugins[plugin_id] = plugin_vars || {};
+ } else {
+ platform_config.dependent_plugins[plugin_id] = plugin_vars || {};
+ }
+
+ // save
+ exports.save_platform_json(platform_config, self.plugins_dir, self.platform);
+}
+
+
+// Load the global munge from platform json and apply all of it.
+// Used by cordova prepare to re-generate some config file from platform
+// defaults and the global munge.
+PlatformMunger.prototype.reapply_global_munge = reapply_global_munge ;
+function reapply_global_munge () {
+ var self = this;
+
+ var platform_config = exports.get_platform_json(self.plugins_dir, self.platform);
+ var global_munge = platform_config.config_munge;
+ for (var file in global_munge.files) {
+ // TODO: remove this warning some time after 3.4 is out.
+ if (file == 'plugins-plist' && self.platform == 'ios') {
+ events.emit(
+ 'warn',
+ 'WARNING: One of your plugins uses <plugins-plist> element(s), ' +
+ 'which are no longer supported. Support has been removed as of Cordova 3.4.'
+ );
+ continue;
+ }
+
+ self.apply_file_munge(file, global_munge.files[file]);
+ }
+}
+
+
+// generate_plugin_config_munge
+// Generate the munge object from plugin.xml + vars
+PlatformMunger.prototype.generate_plugin_config_munge = generate_plugin_config_munge;
+function generate_plugin_config_munge(plugin_dir, vars) {
+ var self = this;
+
+ vars = vars || {};
+ // Add PACKAGE_NAME variable into vars
+ if (!vars['PACKAGE_NAME']) {
+ vars['PACKAGE_NAME'] = self.platform_handler.package_name(self.project_dir);
+ }
+
+ var munge = { files: {} };
+ var plugin_config = self.config_keeper.get(plugin_dir, '', 'plugin.xml');
+ var plugin_xml = plugin_config.data;
+
+ var platformTag = plugin_xml.find('platform[@name="' + self.platform + '"]');
+ var changes = [];
+ // add platform-agnostic config changes
+ changes = changes.concat(plugin_xml.findall('config-file'));
+ if (platformTag) {
+ // add platform-specific config changes if they exist
+ changes = changes.concat(platformTag.findall('config-file'));
+
+ // note down pbxproj framework munges in special section of munge obj
+ // CB-5238 this is only for systems frameworks
+ var frameworks = platformTag.findall('framework');
+ frameworks.forEach(function(f) {
+ var custom = f.attrib['custom'];
+ if(!custom) {
+ var file = f.attrib['src'];
+ var weak = ('true' == f.attrib['weak']).toString();
+
+ deep_add(munge, 'framework', file, { xml: weak, count: 1 });
+ }
+ });
+ }
+
+ changes.forEach(function(change) {
+ var target = change.attrib['target'];
+ var parent = change.attrib['parent'];
+ var after = change.attrib['after'];
+ var xmls = change.getchildren();
+ xmls.forEach(function(xml) {
+ // 1. stringify each xml
+ var stringified = (new et.ElementTree(xml)).write({xml_declaration:false});
+ // interp vars
+ if (vars) {
+ Object.keys(vars).forEach(function(key) {
+ var regExp = new RegExp("\\$" + key, "g");
+ stringified = stringified.replace(regExp, vars[key]);
+ });
+ }
+ // 2. add into munge
+ deep_add(munge, target, parent, { xml: stringified, count: 1, after: after });
+ });
+ });
+ return munge;
+}
+
+// Go over the prepare queue an apply the config munges for each plugin
+// that has been (un)installed.
+PlatformMunger.prototype.process = PlatformMunger_process;
+function PlatformMunger_process() {
+ var self = this;
+
+ var platform_config = exports.get_platform_json(self.plugins_dir, self.platform);
+
+ // Uninstallation first
+ platform_config.prepare_queue.uninstalled.forEach(function(u) {
+ self.remove_plugin_changes(u.plugin, u.id, u.topLevel);
+ });
+
+ // Now handle installation
+ platform_config.prepare_queue.installed.forEach(function(u) {
+ self.add_plugin_changes(u.plugin, u.vars, u.topLevel, true);
+ });
+
+ platform_config = exports.get_platform_json(self.plugins_dir, self.platform);
+
+ // Empty out installed/ uninstalled queues.
+ platform_config.prepare_queue.uninstalled = [];
+ platform_config.prepare_queue.installed = [];
+ // save platform json
+ exports.save_platform_json(platform_config, self.plugins_dir, self.platform);
+}
+/**** END of PlatformMunger ****/
+
+
+/******************************************************************************
+* ConfigKeeper class
+*
+* Used to load and store config files to avoid re-parsing and writing them out
+* multiple times.
+*
+* The config files are referred to by a fake path constructed as
+* project_dir/platform/file
+* where file is the name used for the file in config munges.
+******************************************************************************/
+function ConfigKeeper() {
+ this._cached = {};
+}
+
+ConfigKeeper.prototype.get = ConfigKeeper_get;
+function ConfigKeeper_get(project_dir, platform, file) {
+ var self = this;
+
+ //This fixes a bug with older plugins - when specifying config xml instead of res/xml/config.xml
+ //https://issues.apache.org/jira/browse/CB-6414
+ if(file == 'config.xml' && platform == 'android'){
+ file = 'res/xml/config.xml';
+ }
+ var fake_path = path.join(project_dir, platform, file);
+
+ if (self._cached[fake_path]) {
+ return self._cached[fake_path];
+ }
+ // File was not cached, need to load.
+ var config_file = new ConfigFile(project_dir, platform, file);
+ self._cached[fake_path] = config_file;
+ return config_file;
+}
+
+
+ConfigKeeper.prototype.save_all = ConfigKeeper_save_all;
+function ConfigKeeper_save_all() {
+ var self = this;
+ Object.keys(self._cached).forEach(function (fake_path) {
+ var config_file = self._cached[fake_path];
+ if (config_file.is_changed) config_file.save();
+ });
+}
+/**** END of ConfigKeeper ****/
+
+// TODO: move save/get_platform_json to be part of ConfigKeeper or ConfigFile
+// For now they are used in many places in plugman and cordova-cli and can
+// save the file bypassing the ConfigKeeper's cache.
+exports.get_platform_json = get_platform_json;
+function get_platform_json(plugins_dir, platform) {
+ checkPlatform(platform);
+
+ var filepath = path.join(plugins_dir, platform + '.json');
+ if (fs.existsSync(filepath)) {
+ return fix_munge(JSON.parse(fs.readFileSync(filepath, 'utf-8')));
+ } else {
+ var config = {
+ prepare_queue:{installed:[], uninstalled:[]},
+ config_munge:{},
+ installed_plugins:{},
+ dependent_plugins:{}
+ };
+ return config;
+ }
+}
+
+exports.save_platform_json = save_platform_json;
+function save_platform_json(config, plugins_dir, platform) {
+ checkPlatform(platform);
+ var filepath = path.join(plugins_dir, platform + '.json');
+ fs.writeFileSync(filepath, JSON.stringify(config, null, 4), 'utf-8');
+}
+
+
+// convert a munge from the old format ([file][parent][xml] = count) to the current one
+function fix_munge(platform_config) {
+ var munge = platform_config.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++) {
+ deep_add(new_munge, [file, selector, { xml: xml_child, count: val }]);
+ }
+ }
+ }
+ }
+ platform_config.config_munge = new_munge;
+ }
+
+ return platform_config;
+}
+
+/**** END of ConfigKeeper ****/
+
+
+/******************************************************************************
+* ConfigFile class
+*
+* Can load and keep various types of config files. Provides some functionality
+* specific to some file types such as grafting XML children. In most cases it
+* should be instantiated by ConfigKeeper.
+*
+* For plugin.xml files use as:
+* plugin_config = self.config_keeper.get(plugin_dir, '', 'plugin.xml');
+*
+* TODO: Consider moving it out to a separate file and maybe partially with
+* overrides in platform handlers.
+******************************************************************************/
+function ConfigFile(project_dir, platform, file_tag) {
+ this.project_dir = project_dir;
+ this.platform = platform;
+ this.file_tag = file_tag;
+ this.is_changed = false;
+
+ this.load();
+}
+
+// ConfigFile.load()
+ConfigFile.prototype.load = ConfigFile_load;
+function ConfigFile_load() {
+ var self = this;
+
+ // config file may be in a place not exactly specified in the target
+ var filepath = self.filepath = resolveConfigFilePath(self.project_dir, self.platform, self.file_tag);
+
+ if ( !filepath || !fs.existsSync(filepath) ) {
+ self.exists = false;
+ return;
+ }
+ self.exists = true;
+ var ext = path.extname(filepath);
+ // Windows8 uses an appxmanifest, and wp8 will likely use
+ // the same in a future release
+ if (ext == '.xml' || ext == '.appxmanifest') {
+ self.type = 'xml';
+ self.data = xml_helpers.parseElementtreeSync(filepath);
+ } else if (ext == '.pbxproj') {
+ self.type = 'pbxproj';
+ self.data = xcode.project(filepath);
+ self.data.parseSync();
+ } else {
+ // plist file
+ self.type = 'plist';
+ // TODO: isBinaryPlist() reads the file and then parse re-reads it again.
+ // We always write out text plist, not binary.
+ // Do we still need to support binary plist?
+ // If yes, use plist.parseStringSync() and read the file once.
+ self.plist_module = (isBinaryPlist(filepath) ? bplist : plist);
+ self.data = self.plist_module.parseFileSync(filepath);
+ }
+}
+
+// ConfigFile.save()
+ConfigFile.prototype.save = ConfigFile_save;
+function ConfigFile_save() {
+ var self = this;
+ if (self.type === 'xml') {
+ fs.writeFileSync(self.filepath, self.data.write({indent: 4}), 'utf-8');
+ } else if (self.type === 'pbxproj') {
+ fs.writeFileSync(self.filepath, self.data.writeSync());
+ } else {
+ // plist
+ var regExp = new RegExp("<string>[ \t\r\n]+?</string>", "g");
+ fs.writeFileSync(self.filepath, plist.build(self.data).replace(regExp, "<string></string>"));
+ }
+ self.is_changed = false;
+}
+
+// ConfigFile.graft_child()
+ConfigFile.prototype.graft_child = ConfigFile_graft_child;
+function ConfigFile_graft_child(selector, xml_child) {
+ var self = this;
+ var filepath = self.filepath;
+ var result;
+ if (self.type === 'xml') {
+ var xml_to_graft = [et.XML(xml_child.xml)];
+ result = xml_helpers.graftXML(self.data, xml_to_graft, selector, xml_child.after);
+ if ( !result) {
+ throw new Error('grafting xml at selector "' + selector + '" from "' + filepath + '" during config install went bad :(');
+ }
+ } else {
+ // plist file
+ result = plist_helpers.graftPLIST(self.data, xml_child.xml, selector);
+ if ( !result ) {
+ throw new Error('grafting to plist "' + filepath + '" during config install went bad :(');
+ }
+ }
+ self.is_changed = true;
+}
+
+// ConfigFile.prune_child()
+ConfigFile.prototype.prune_child = ConfigFile_prune_child;
+function ConfigFile_prune_child(selector, xml_child) {
+ var self = this;
+ var filepath = self.filepath;
+ var result;
+ if (self.type === 'xml') {
+ var xml_to_graft = [et.XML(xml_child.xml)];
+ result = xml_helpers.pruneXML(self.data, xml_to_graft, selector);
+ } else {
+ // plist file
+ result = plist_helpers.prunePLIST(self.data, xml_child.xml, selector);
+ }
+ if ( !result) {
+ var err_msg = 'Pruning at selector "' + selector + '" from "' + filepath + '" went bad.';
+ throw new Error(err_msg);
+ }
+ self.is_changed = true;
+}
+/**** END of ConfigFile ****/
+
+
+/******************************************************************************
+* Utility functions
+******************************************************************************/
+
+// Check if we know such platform
+function checkPlatform(platform) {
+ if (!(platform in platforms)) throw new Error('platform "' + platform + '" not recognized.');
+}
+
+// determine if a plist file is binary
+function isBinaryPlist(filename) {
+ // I wish there was a synchronous way to read only the first 6 bytes of a
+ // file. This is wasteful :/
+ var buf = '' + fs.readFileSync(filename, 'utf8');
+ // binary plists start with a magic header, "bplist"
+ return buf.substring(0, 6) === 'bplist';
+}
+
+// Find out the real name of an iOS project
+// TODO: glob is slow, need a better way or caching, or avoid using more than once.
+function getIOSProjectname(project_dir) {
+ var matches = glob.sync(path.join(project_dir, '*.xcodeproj'));
+ var iospath;
+ if (matches.length === 1) {
+ iospath = path.basename(matches[0],'.xcodeproj');
+ } else {
+ var msg;
+ if (matches.length === 0) {
+ msg = 'Does not appear to be an xcode project, no xcode project file in ' + project_dir;
+ }
+ else {
+ msg = 'There are multiple *.xcodeproj dirs in ' + project_dir;
+ }
+ throw new Error(msg);
+ }
+ return iospath;
+}
+
+// Some config-file target attributes are not qualified with a full leading directory, or contain wildcards.
+// Resolve to a real path in this function.
+// TODO: getIOSProjectname is slow because of glob, try to avoid calling it several times per project.
+function resolveConfigFilePath(project_dir, platform, file) {
+ var filepath = path.join(project_dir, file);
+ var matches;
+
+ // .pbxproj file
+ if (file === 'framework') {
+ var proj_name = getIOSProjectname(project_dir);
+ filepath = path.join(project_dir, proj_name + '.xcodeproj', 'project.pbxproj');
+ return filepath;
+ }
+
+ if (file.indexOf('*') > -1) {
+ // handle wildcards in targets using glob.
+ matches = glob.sync(path.join(project_dir, '**', file));
+ if (matches.length) filepath = matches[0];
+ return filepath;
+ }
+
+ // special-case config.xml target that is just "config.xml". This should be resolved to the real location of the file.
+ // TODO: move the logic that contains the locations of config.xml from cordova CLI into plugman.
+ if (file == 'config.xml') {
+ if (platform == 'ubuntu') {
+ filepath = path.join(project_dir, 'config.xml');
+ } else if (platform == 'ios') {
+ var iospath = getIOSProjectname(project_dir);
+ filepath = path.join(project_dir,iospath, 'config.xml');
+ } else if (platform == 'android') {
+ filepath = path.join(project_dir, 'res', 'xml', 'config.xml');
+ } else {
+ matches = glob.sync(path.join(project_dir, '**', 'config.xml'));
+ if (matches.length) filepath = matches[0];
+ }
+ return filepath;
+ }
+
+ // None of the special cases matched, returning project_dir/file.
+ return filepath;
+}
+
+
+/******************************************************************************
+* Munge object manipulations functions
+******************************************************************************/
+
+// add the count of [key1][key2]...[keyN] to obj
+// return true if it didn't exist before
+function deep_add(obj, keys /* or key1, key2 .... */ ) {
+ if ( !Array.isArray(keys) ) {
+ keys = Array.prototype.slice.call(arguments, 1);
+ }
+
+ return process_munge(obj, true/*createParents*/, function (parentArray, k) {
+ var found = _.find(parentArray, function(element) {
+ return element.xml == k.xml;
+ });
+ if (found) {
+ found.after = found.after || k.after;
+ found.count += k.count;
+ } else {
+ parentArray.push(k);
+ }
+ return !found;
+ }, keys);
+}
+
+// decrement the count of [key1][key2]...[keyN] from obj and remove if it reaches 0
+// return true if it was removed or not found
+function deep_remove(obj, keys /* or key1, key2 .... */ ) {
+ if ( !Array.isArray(keys) ) {
+ keys = Array.prototype.slice.call(arguments, 1);
+ }
+
+ var result = process_munge(obj, false/*createParents*/, function (parentArray, k) {
+ var index = -1;
+ var found = _.find(parentArray, function (element) {
+ index++;
+ return element.xml == k.xml;
+ });
+ if (found) {
+ found.count -= k.count;
+ if (found.count > 0) {
+ return false;
+ }
+ else {
+ parentArray.splice(index, 1);
+ }
+ }
+ return undefined;
+ }, keys);
+
+ return typeof result === "undefined" ? true : result;
+}
+
+// search for [key1][key2]...[keyN]
+// return the object or undefined if not found
+function deep_find(obj, keys /* or key1, key2 .... */ ) {
+ if ( !Array.isArray(keys) ) {
+ keys = Array.prototype.slice.call(arguments, 1);
+ }
+
+ return process_munge(obj, false/*createParents?*/, function (parentArray, k) {
+ return _.find(parentArray, function (element) {
+ return element.xml == (k.xml || k);
+ });
+ }, keys);
+}
+
+// Execute func passing it the parent array and the xmlChild key.
+// When createParents is true, add the file and parent items they are missing
+// When createParents is false, stop and return undefined if the file and/or parent items are missing
+
+function process_munge(obj, createParents, func, keys /* or key1, key2 .... */ ) {
+ if ( !Array.isArray(keys) ) {
+ keys = Array.prototype.slice.call(arguments, 1);
+ }
+ var k = keys[0];
+ if (keys.length == 1) {
+ return func(obj, k);
+ } else if (keys.length == 2) {
+ if (!obj.parents[k] && !createParents) {
+ return undefined;
+ }
+ obj.parents[k] = obj.parents[k] || [];
+ return process_munge(obj.parents[k], createParents, func, keys.slice(1));
+ } else if (keys.length == 3){
+ if (!obj.files[k] && !createParents) {
+ return undefined;
+ }
+ obj.files[k] = obj.files[k] || { parents: {} };
+ return process_munge(obj.files[k], createParents, func, keys.slice(1));
+ } else {
+ throw new Error("Invalid key format. Must contain at most 3 elements (file, parent, xmlChild).");
+ }
+}
+
+// All values from munge are added to base as
+// base[file][selector][child] += base[file][selector][child]
+// Returns a munge object containing values that exist in munge
+// but not in base.
+function increment_munge(base, munge) {
+ var diff = { files: {} };
+
+ for (var file in munge.files) {
+ for (var selector in munge.files[file].parents) {
+ for (var xml_child in munge.files[file].parents[selector]) {
+ var val = munge.files[file].parents[selector][xml_child];
+ // if node not in base, add it to diff and base
+ // else increment it's value in base without adding to diff
+ var newlyAdded = deep_add(base, [file, selector, val]);
+ if (newlyAdded) {
+ deep_add(diff, file, selector, val);
+ }
+ }
+ }
+ }
+ return diff;
+}
+
+// Update the base munge object as
+// base[file][selector][child] -= base[file][selector][child]
+// nodes that reached zero value are removed from base and added to the returned munge
+// object.
+function decrement_munge(base, munge) {
+ var zeroed = { files: {} };
+
+ for (var file in munge.files) {
+ for (var selector in munge.files[file].parents) {
+ for (var xml_child in munge.files[file].parents[selector]) {
+ var val = munge.files[file].parents[selector][xml_child];
+ // if node not in base, add it to diff and base
+ // else increment it's value in base without adding to diff
+ var removed = deep_remove(base, [file, selector, val]);
+ if (removed) {
+ deep_add(zeroed, file, selector, val);
+ }
+ }
+ }
+ }
+ return zeroed;
+}
+
+// For better readability where used
+function clone_munge(munge) {
+ return increment_munge({}, munge);
+}
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/src/plugman/util/csproj.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/util/csproj.js b/cordova-lib/src/plugman/util/csproj.js
new file mode 100644
index 0000000..1024878
--- /dev/null
+++ b/cordova-lib/src/plugman/util/csproj.js
@@ -0,0 +1,124 @@
+var xml_helpers = require('./xml-helpers'),
+ et = require('elementtree'),
+ fs = require('fs'),
+ path = require('path');
+
+function csproj(location) {
+ this.location = location;
+ this.xml = xml_helpers.parseElementtreeSync(location);
+ return this;
+}
+
+csproj.prototype = {
+ write:function() {
+ fs.writeFileSync(this.location, this.xml.write({indent:4}), 'utf-8');
+ },
+
+ addReference:function(relPath) {
+ var item = new et.Element('ItemGroup');
+ var extName = path.extname(relPath);
+
+ var elem = new et.Element('Reference');
+ // add dll file name
+ elem.attrib.Include = path.basename(relPath, extName);
+ // add hint path with full path
+ var hint_path = new et.Element('HintPath');
+ hint_path.text = relPath;
+ elem.append(hint_path);
+
+ if(extName == ".winmd") {
+ var mdFileTag = new et.Element("IsWinMDFile");
+ mdFileTag.text = "true";
+ elem.append(mdFileTag);
+ }
+
+ item.append(elem);
+
+ this.xml.getroot().append(item);
+ },
+
+ removeReference:function(relPath) {
+ var item = new et.Element('ItemGroup');
+ var extName = path.extname(relPath);
+ var includeText = path.basename(relPath,extName);
+ // <ItemGroup>
+ // <Reference Include="WindowsRuntimeComponent1">
+ var item_groups = this.xml.findall('ItemGroup/Reference[@Include="' + includeText + '"]/..');
+
+ if(item_groups.length > 0 ) {
+ this.xml.getroot().remove(0, item_groups[0]);
+ }
+ },
+
+ addSourceFile:function(relative_path) {
+ relative_path = relative_path.split('/').join('\\');
+ // make ItemGroup to hold file.
+ var item = new et.Element('ItemGroup');
+
+ var extName = path.extname(relative_path);
+ // check if it's a .xaml page
+ if(extName == ".xaml") {
+ var page = new et.Element('Page');
+ var sub_type = new et.Element('SubType');
+
+ sub_type.text = "Designer";
+ page.append(sub_type);
+ page.attrib.Include = relative_path;
+
+ var gen = new et.Element('Generator');
+ gen.text = "MSBuild:Compile";
+ page.append(gen);
+
+ var item_groups = this.xml.findall('ItemGroup');
+ if(item_groups.length == 0) {
+ item.append(page);
+ } else {
+ item_groups[0].append(page);
+ }
+ }
+ else if (extName == ".cs") {
+ var compile = new et.Element('Compile');
+ compile.attrib.Include = relative_path;
+ // check if it's a .xaml.cs page that would depend on a .xaml of the same name
+ if (relative_path.indexOf('.xaml.cs', relative_path.length - 8) > -1) {
+ var dep = new et.Element('DependentUpon');
+ var parts = relative_path.split('\\');
+ var xaml_file = parts[parts.length - 1].substr(0, parts[parts.length - 1].length - 3); // Benn, really !?
+ dep.text = xaml_file;
+ compile.append(dep);
+ }
+ item.append(compile);
+ }
+ else { // otherwise add it normally
+ var compile = new et.Element('Content');
+ compile.attrib.Include = relative_path;
+ item.append(compile);
+ }
+ this.xml.getroot().append(item);
+ },
+
+ removeSourceFile:function(relative_path) {
+ relative_path = relative_path.split('/').join('\\');
+ var item_groups = this.xml.findall('ItemGroup');
+ for (var i = 0, l = item_groups.length; i < l; i++) {
+ var group = item_groups[i];
+ var files = group.findall('Compile').concat(group.findall('Page')).concat(group.findall('Content'));
+ for (var j = 0, k = files.length; j < k; j++) {
+ var file = files[j];
+ if (file.attrib.Include == relative_path) {
+ // remove file reference
+ group.remove(0, file);
+ // remove ItemGroup if empty
+ var new_group = group.findall('Compile').concat(group.findall('Page')).concat(group.findall('Content'));
+ if(new_group.length < 1) {
+ this.xml.getroot().remove(0, group);
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+};
+
+module.exports = csproj;
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/src/plugman/util/default-engines.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/util/default-engines.js b/cordova-lib/src/plugman/util/default-engines.js
new file mode 100644
index 0000000..5e4f151
--- /dev/null
+++ b/cordova-lib/src/plugman/util/default-engines.js
@@ -0,0 +1,36 @@
+var path = require('path');
+
+module.exports = function(project_dir){
+ return {
+ 'cordova':
+ { 'platform':'*', 'scriptSrc': path.join(project_dir,'cordova','version') },
+ 'cordova-plugman':
+ { 'platform':'*', 'currentVersion': require('../../package.json').version },
+ 'cordova-android':
+ { 'platform':'android', 'scriptSrc': path.join(project_dir,'cordova','version') },
+ 'cordova-ios':
+ { 'platform':'ios', 'scriptSrc': path.join(project_dir,'cordova','version') },
+ 'cordova-blackberry10':
+ { 'platform':'blackberry10', 'scriptSrc': path.join(project_dir,'cordova','version') },
+ 'cordova-wp7':
+ { 'platform':'wp7', 'scriptSrc': path.join(project_dir,'cordova','version') },
+ 'cordova-wp8':
+ { 'platform':'wp8', 'scriptSrc': path.join(project_dir,'cordova','version') },
+ 'cordova-windows8':
+ { 'platform':'windows8', 'scriptSrc': path.join(project_dir,'cordova','version') },
+ 'apple-xcode' :
+ { 'platform':'ios', 'scriptSrc': path.join(project_dir,'cordova','apple_xcode_version') },
+ 'apple-ios' :
+ { 'platform':'ios', 'scriptSrc': path.join(project_dir,'cordova','apple_ios_version') },
+ 'apple-osx' :
+ { 'platform':'ios', 'scriptSrc': path.join(project_dir,'cordova','apple_osx_version') },
+ 'blackberry-ndk' :
+ { 'platform':'blackberry10', 'scriptSrc': path.join(project_dir,'cordova','bb10-ndk-version') },
+ 'android-sdk' :
+ { 'platform':'android', 'scriptSrc': path.join(project_dir,'cordova','android_sdk_version') },
+ 'windows-os' :
+ { 'platform':'wp7|wp8|windows8', 'scriptSrc': path.join(project_dir,'cordova','win_os_version') },
+ 'windows-sdk' :
+ { 'platform':'wp7|wp8|windows8', 'scriptSrc': path.join(project_dir,'cordova','win_sdk_version') }
+ }
+};
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/src/plugman/util/dependencies.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/util/dependencies.js b/cordova-lib/src/plugman/util/dependencies.js
new file mode 100644
index 0000000..a297372
--- /dev/null
+++ b/cordova-lib/src/plugman/util/dependencies.js
@@ -0,0 +1,96 @@
+var dep_graph = require('dep-graph'),
+ path = require('path'),
+ fs = require('fs'),
+ plugman = require('../../plugman'),
+ config_changes = require('./config-changes'),
+ underscore = require('underscore'),
+ xml_helpers = require('./xml-helpers'),
+ package;
+
+module.exports = package = {
+
+ resolvePath: function(plugin_id, plugins_dir)
+ {
+ return path.join(plugins_dir, plugin_id);
+ },
+
+ resolveConfig: function(plugin_id, plugins_dir)
+ {
+ return path.join(plugins_dir, plugin_id, 'plugin.xml');
+ },
+
+ generate_dependency_info:function(plugins_dir, platform) {
+ var json = config_changes.get_platform_json(plugins_dir, platform);
+
+ // TODO: store whole dependency tree in plugins/[platform].json
+ // in case plugins are forcefully removed...
+ var tlps = [];
+ var graph = new dep_graph();
+ Object.keys(json.installed_plugins).forEach(function(plugin_id) {
+ tlps.push(plugin_id);
+
+ var xml = xml_helpers.parseElementtreeSync( package.resolveConfig(plugin_id, plugins_dir) );
+ var deps = xml.findall('dependency');
+
+ deps && deps.forEach(function(dep) {
+ graph.add(plugin_id, dep.attrib.id);
+ });
+ });
+ Object.keys(json.dependent_plugins).forEach(function(plugin_id) {
+ var xml = xml_helpers.parseElementtreeSync( package.resolveConfig(plugin_id, plugins_dir) );
+ var deps = xml.findall('dependency');
+ deps && deps.forEach(function(dep) {
+ graph.add(plugin_id, dep.attrib.id);
+ });
+ });
+
+ return {
+ graph:graph,
+ top_level_plugins:tlps
+ };
+ },
+
+ // Returns a list of top-level plugins which are (transitively) dependent on the given plugin.
+ dependents: function(plugin_id, plugins_dir, platform) {
+ if(typeof plugins_dir == 'object')
+ var depsInfo = plugins_dir;
+ else
+ var depsInfo = package.generate_dependency_info(plugins_dir, platform);
+
+ var graph = depsInfo.graph;
+ var tlps = depsInfo.top_level_plugins;
+ var dependents = tlps.filter(function(tlp) {
+ return tlp != plugin_id && graph.getChain(tlp).indexOf(plugin_id) >= 0;
+ });
+
+ return dependents;
+ },
+
+ // Returns a list of plugins which the given plugin depends on, for which it is the only dependent.
+ // In other words, if the given plugin were deleted, these dangling dependencies should be deleted too.
+ danglers: function(plugin_id, plugins_dir, platform) {
+ if(typeof plugins_dir == 'object')
+ var depsInfo = plugins_dir;
+ else
+ var depsInfo = package.generate_dependency_info(plugins_dir, platform);
+
+ var graph = depsInfo.graph;
+ var dependencies = graph.getChain(plugin_id);
+
+ var tlps = depsInfo.top_level_plugins;
+ var diff_arr = [];
+ tlps.forEach(function(tlp) {
+ if (tlp != plugin_id) {
+ diff_arr.push(graph.getChain(tlp));
+ }
+ });
+
+ // if this plugin has dependencies, do a set difference to determine which dependencies are not required by other existing plugins
+ diff_arr.unshift(dependencies);
+ var danglers = underscore.difference.apply(null, diff_arr);
+
+ // Ensure no top-level plugins are tagged as danglers.
+ danglers = danglers && danglers.filter(function(x) { return tlps.indexOf(x) < 0; });
+ return danglers;
+ }
+};
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/src/plugman/util/metadata.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/util/metadata.js b/cordova-lib/src/plugman/util/metadata.js
new file mode 100644
index 0000000..cfbb643
--- /dev/null
+++ b/cordova-lib/src/plugman/util/metadata.js
@@ -0,0 +1,19 @@
+var fs = require('fs'),
+ path = require('path');
+
+var filename = '.fetch.json';
+
+exports.get_fetch_metadata = function(plugin_dir) {
+ var filepath = path.join(plugin_dir, filename);
+ if (fs.existsSync(filepath)) {
+ return JSON.parse(fs.readFileSync(filepath, 'utf-8'));
+ } else {
+ return {};
+ }
+};
+
+exports.save_fetch_metadata = function(plugin_dir, data) {
+ var filepath = path.join(plugin_dir, '.fetch.json');
+ fs.writeFileSync(filepath, JSON.stringify(data), 'utf-8');
+};
+
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/src/plugman/util/plist-helpers.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/util/plist-helpers.js b/cordova-lib/src/plugman/util/plist-helpers.js
new file mode 100644
index 0000000..e1dfbd9
--- /dev/null
+++ b/cordova-lib/src/plugman/util/plist-helpers.js
@@ -0,0 +1,88 @@
+/*
+ *
+ * Copyright 2013 Brett Rudd
+ *
+ * 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.
+ *
+*/
+
+// contains PLIST utility functions
+
+var et = require('elementtree'),
+ plist = require('plist-with-patches');
+
+// adds node to doc at selector
+module.exports = {
+ graftPLIST:function (doc, xml, selector) {
+ var obj = plist.parseStringSync("<plist>"+xml+"</plist>");
+
+ var node = doc[selector];
+ if (node && Array.isArray(node) && Array.isArray(obj))
+ doc[selector] = node.concat(obj);
+ else
+ doc[selector] = obj;
+
+ return true;
+ },
+ // removes node from doc at selector
+ prunePLIST:function(doc, xml, selector) {
+ var obj = plist.parseStringSync("<plist>"+xml+"</plist>");
+
+ pruneOBJECT(doc, selector, obj);
+
+ return true;
+ }
+}
+
+function pruneOBJECT(doc, selector, fragment) {
+ if (Array.isArray(fragment) && Array.isArray(doc[selector])) {
+ var empty = true;
+ for (i in fragment) {
+ for (j in doc[selector]) {
+ empty = pruneOBJECT(doc[selector], j, fragment[i]) && empty;
+ }
+ }
+ if (empty)
+ {
+ delete doc[selector];
+ return true;
+ }
+ }
+ else if (nodeEqual(doc[selector], fragment)) {
+ delete doc[selector];
+ return true;
+ }
+
+ return false;
+}
+
+function nodeEqual(node1, node2) {
+ if (typeof node1 != typeof node2)
+ return false;
+ else if (typeof node1 == 'string') {
+ node2 = escapeRE(node2).replace(new RegExp("\\$[a-zA-Z0-9-_]+","gm"),"(.*?)");
+ return new RegExp('^' + node2 + '$').test(node1);
+ }
+ else {
+ for (var key in node2) {
+ if (!nodeEqual(node1[key], node2[key])) return false;
+ }
+ return true;
+ }
+}
+
+// escape string for use in regex
+function escapeRE(str) {
+ return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\$&");
+};
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/src/plugman/util/plugins.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/util/plugins.js b/cordova-lib/src/plugman/util/plugins.js
new file mode 100644
index 0000000..5a5d14a
--- /dev/null
+++ b/cordova-lib/src/plugman/util/plugins.js
@@ -0,0 +1,100 @@
+/*
+ *
+ * Copyright 2013 Anis Kadri
+ *
+ * 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.
+ *
+*/
+
+var http = require('http'),
+ os = require('os'),
+ path = require('path'),
+ fs = require('fs'),
+ util = require('util'),
+ shell = require('shelljs'),
+ child_process = require('child_process'),
+ Q = require('q'),
+ xml_helpers = require('./xml-helpers'),
+ events = require('../events'),
+ tmp_dir;
+
+module.exports = {
+ searchAndReplace:require('./search-and-replace'),
+
+ clonePluginGit:function(plugin_git_url, plugins_dir, options) {
+ return module.exports.clonePluginGitRepo(plugin_git_url, plugins_dir, options.subdir, options.git_ref).then(
+ function(dst){
+ // Keep location where we checked out git repo
+ options.plugin_src_dir = tmp_dir;
+ return dst;
+ }
+ );
+ },
+
+ // Fetches plugin information from remote server.
+ // Returns a promise.
+ clonePluginGitRepo:function(plugin_git_url, plugins_dir, subdir, git_ref) {
+
+ if(!shell.which('git')) {
+ return Q.reject(new Error('"git" command line tool is not installed: make sure it is accessible on your PATH.'));
+ }
+ tmp_dir = path.join(os.tmpdir(), 'plugman', 'git', String((new Date).valueOf()));
+
+ shell.rm('-rf', tmp_dir);
+
+ var cmd = util.format('git clone "%s" "%s"', plugin_git_url, tmp_dir);
+ events.emit('verbose', 'Fetching plugin via git-clone command: ' + cmd);
+ var d = Q.defer();
+
+ child_process.exec(cmd, function(err, stdout, stderr) {
+ if (err) {
+ d.reject(err);
+ } else {
+ d.resolve();
+ }
+ });
+ return d.promise.then(function() {
+ events.emit('verbose', 'Plugin "' + plugin_git_url + '" fetched.');
+ // Check out the specified revision, if provided.
+ if (git_ref) {
+ var cmd = util.format('git checkout "%s"', git_ref);
+ var d2 = Q.defer();
+ child_process.exec(cmd, { cwd: tmp_dir }, function(err, stdout, stderr) {
+ if (err) d2.reject(err);
+ else d2.resolve();
+ });
+ return d2.promise.then(function() {
+ events.emit('log', 'Plugin "' + plugin_git_url + '" checked out to git ref "' + git_ref + '".');
+ });
+ }
+ }).then(function() {
+ // Read the plugin.xml file and extract the plugin's ID.
+ tmp_dir = path.join(tmp_dir, subdir);
+ // TODO: what if plugin.xml does not exist?
+ var xml_file = path.join(tmp_dir, 'plugin.xml');
+ var xml = xml_helpers.parseElementtreeSync(xml_file);
+ var plugin_id = xml.getroot().attrib.id;
+
+ // TODO: what if a plugin depended on different subdirectories of the same plugin? this would fail.
+ // should probably copy over entire plugin git repo contents into plugins_dir and handle subdir separately during install.
+ var plugin_dir = path.join(plugins_dir, plugin_id);
+ events.emit('verbose', 'Copying fetched plugin over "' + plugin_dir + '"...');
+ shell.cp('-R', path.join(tmp_dir, '*'), plugin_dir);
+
+ events.emit('verbose', 'Plugin "' + plugin_id + '" fetched.');
+ return plugin_dir;
+ });
+ }
+};
+
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/src/plugman/util/search-and-replace.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/util/search-and-replace.js b/cordova-lib/src/plugman/util/search-and-replace.js
new file mode 100644
index 0000000..39bd9c3
--- /dev/null
+++ b/cordova-lib/src/plugman/util/search-and-replace.js
@@ -0,0 +1,37 @@
+#!/usr/bin/env node
+/*
+ *
+ * Copyright 2013 Brett Rudd
+ *
+ * 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.
+ *
+*/
+
+var glob = require('glob'),
+ fs = require('fs');
+
+module.exports = function searchAndReplace(srcGlob, variables) {
+ var files = glob.sync(srcGlob);
+ for (var i in files) {
+ var file = files[i];
+ if (fs.lstatSync(file).isFile()) {
+ var contents = fs.readFileSync(file, "utf-8");
+ for (var key in variables) {
+ var regExp = new RegExp("\\$" + key, "g");
+ contents = contents.replace(regExp, variables[key]);
+ }
+ fs.writeFileSync(file, contents);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/src/plugman/util/w8jsproj.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/util/w8jsproj.js b/cordova-lib/src/plugman/util/w8jsproj.js
new file mode 100644
index 0000000..903a214
--- /dev/null
+++ b/cordova-lib/src/plugman/util/w8jsproj.js
@@ -0,0 +1,239 @@
+/*
+ Helper for dealing with Windows Store JS app .jsproj files
+*/
+
+
+var xml_helpers = require('./xml-helpers'),
+ et = require('elementtree'),
+ fs = require('fs'),
+ shell = require('shelljs'),
+ events = require('../events'),
+ path = require('path');
+
+var WindowsStoreProjectTypeGUID = "{BC8A1FFA-BEE3-4634-8014-F334798102B3}"; // any of the below, subtype
+var WinCSharpProjectTypeGUID = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"; // .csproj
+var WinVBnetProjectTypeGUID = "{F184B08F-C81C-45F6-A57F-5ABD9991F28F}"; // who the ef cares?
+var WinCplusplusProjectTypeGUID = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"; // .vcxproj
+
+
+function jsproj(location) {
+ events.emit('verbose','creating jsproj from project at : ' + location);
+ this.location = location;
+ this.xml = xml_helpers.parseElementtreeSync(location);
+ return this;
+}
+
+jsproj.prototype = {
+ location:null,
+ xml:null,
+ plugins_dir:"Plugins",
+ write:function() {
+ fs.writeFileSync(this.location, this.xml.write({indent:4}), 'utf-8');
+ },
+ // add/remove the item group for SDKReference
+ // example :
+ // <ItemGroup><SDKReference Include="Microsoft.VCLibs, version=12.0" /></ItemGroup>
+ addSDKRef:function(incText) {
+ var item_group = new et.Element('ItemGroup');
+ var elem = new et.Element('SDKReference');
+ elem.attrib.Include = incText;
+
+ item_group.append(elem);
+ this.xml.getroot().append(item_group);
+ },
+
+ removeSDKRef:function(incText) {
+ var item_group = this.xml.find('ItemGroup/SDKReference[@Include="' + incText + '"]/..');
+ if(item_group) { // TODO: error handling
+ this.xml.getroot().remove(0, item_group);
+ }
+ },
+
+ addReference:function(relPath,src) {
+
+ events.emit('verbose','addReference::' + relPath);
+
+ var item = new et.Element('ItemGroup');
+ var extName = path.extname(relPath);
+
+ var elem = new et.Element('Reference');
+ // add file name
+ elem.attrib.Include = path.basename(relPath, extName);
+
+ // add hint path with full path
+ var hint_path = new et.Element('HintPath');
+ hint_path.text = relPath;
+
+ elem.append(hint_path);
+
+ if(extName == ".winmd") {
+ var mdFileTag = new et.Element("IsWinMDFile");
+ mdFileTag.text = "true";
+ elem.append(mdFileTag);
+ }
+
+ item.append(elem);
+ this.xml.getroot().append(item);
+ },
+
+ removeReference:function(relPath) {
+ events.emit('verbose','removeReference::' + relPath);
+
+ var extName = path.extname(relPath);
+ var includeText = path.basename(relPath,extName);
+ // <ItemGroup>
+ // <Reference Include="WindowsRuntimeComponent1">
+ var item_group = this.xml.find('ItemGroup/Reference[@Include="' + includeText + '"]/..');
+
+ if(item_group) { // TODO: erro handling
+ this.xml.getroot().remove(0, item_group);
+ }
+ },
+
+ addSourceFile:function(relative_path) {
+
+ relative_path = relative_path.split('/').join('\\');
+ // make ItemGroup to hold file.
+ var item = new et.Element('ItemGroup');
+
+ var content = new et.Element('Content');
+ content.attrib.Include = relative_path;
+ item.append(content);
+
+ this.xml.getroot().append(item);
+ },
+
+ removeSourceFile:function(relative_path) {
+
+ // path.normalize(relative_path);// ??
+ relative_path = relative_path.split('/').join('\\');
+ // var oneStep = this.xml.findall('ItemGroup/Content[@Include="' + relative_path + '""]/..');
+
+ var item_groups = this.xml.findall('ItemGroup');
+ for (var i = 0, l = item_groups.length; i < l; i++) {
+ var group = item_groups[i];
+ var files = group.findall('Content');
+ for (var j = 0, k = files.length; j < k; j++) {
+ var file = files[j];
+ if (file.attrib.Include == relative_path) {
+ // remove file reference
+ group.remove(0, file);
+ // remove ItemGroup if empty
+ var new_group = group.findall('Content');
+ if(new_group.length < 1) {
+ this.xml.getroot().remove(0, group);
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+ // relative path must include the project file, so we can determine .csproj, .jsproj, .vcxproj...
+ addProjectReference:function(relative_path) {
+ events.emit('verbose','adding project reference to ' + relative_path);
+
+ relative_path = relative_path.split('/').join('\\');
+ // read the solution path from the base directory
+ var solutionPath = shell.ls(path.join(path.dirname(this.location),"*.sln"))[0];// TODO:error handling
+ // note we may not have a solution, in which case just add a project reference, I guess ..
+ // get the project extension to figure out project type
+ var projectExt = path.extname(relative_path);
+
+ var pluginProjectXML = xml_helpers.parseElementtreeSync(relative_path);
+ // find the guid + name of the referenced project
+ var projectGuid = pluginProjectXML.find("PropertyGroup/ProjectGuid").text;
+ var projName = pluginProjectXML.find("PropertyGroup/ProjectName").text;
+
+ var preInsertText = "ProjectSection(ProjectDependencies) = postProject\n\r" +
+ projectGuid + "=" + projectGuid + "\n\r" +
+ "EndProjectSection\n\r";
+
+ // read in the solution file
+ var solText = fs.readFileSync(solutionPath,{encoding:"utf8"});
+ var splitText = solText.split("EndProject");
+ if(splitText.length != 2) {
+ throw new Error("too many projects in solution.");
+ }
+
+ var projectTypeGuid = null;
+ if(projectExt == ".vcxproj") {
+ projectTypeGuid = WinCplusplusProjectTypeGUID;
+ }
+ else if(projectExt == ".csproj") {
+ projectTypeGuid = WinCSharpProjectTypeGUID;
+ }
+
+ if(!projectTypeGuid) {
+ throw new Error("unrecognized project type");
+ }
+
+ var postInsertText = 'Project("' + projectTypeGuid + '") = "' +
+ projName + '", "' + relative_path + '",' +
+ '"' + projectGuid + '"\n\r EndProject\n\r';
+
+ solText = splitText[0] + preInsertText + "EndProject\n\r" + postInsertText + splitText[1];
+ fs.writeFileSync(solutionPath,solText,{encoding:"utf8"});
+
+
+ // Add the ItemGroup/ProjectReference to the cordova project :
+ // <ItemGroup><ProjectReference Include="blahblah.csproj"/></ItemGroup>
+ var item = new et.Element('ItemGroup');
+
+ var projRef = new et.Element('ProjectReference');
+ projRef.attrib.Include = relative_path;
+ item.append(projRef);
+ this.xml.getroot().append(item);
+
+ },
+ removeProjectReference:function(relative_path) {
+ events.emit('verbose','removing project reference to ' + relative_path);
+
+ // find the guid + name of the referenced project
+ var pluginProjectXML = xml_helpers.parseElementtreeSync(relative_path);
+ var projectGuid = pluginProjectXML.find("PropertyGroup/ProjectGuid").text;
+ var projName = pluginProjectXML.find("PropertyGroup/ProjectName").text;
+
+ // get the project extension to figure out project type
+ var projectExt = path.extname(relative_path);
+ // get the project type
+ var projectTypeGuid = null;
+ if(projectExt == ".vcxproj") {
+ projectTypeGuid = WinCplusplusProjectTypeGUID;
+ }
+ else if(projectExt == ".csproj") {
+ projectTypeGuid = WinCSharpProjectTypeGUID;
+ }
+
+ if(!projectTypeGuid) {
+ throw new Error("unrecognized project type");
+ }
+
+ var preInsertText = "ProjectSection(ProjectDependencies) = postProject\n\r" +
+ projectGuid + "=" + projectGuid + "\n\r" +
+ "EndProjectSection\n\r";
+
+ var postInsertText = 'Project("' + projectTypeGuid + '") = "' +
+ projName + '", "' + relative_path + '",' +
+ '"' + projectGuid + '"\n\r EndProject\n\r';
+
+ // find and read in the solution file
+ var solutionPath = shell.ls(path.join(path.dirname(this.location),"*.sln"))[0]; // TODO:error handling
+ var solText = fs.readFileSync(solutionPath,{encoding:"utf8"});
+ var splitText = solText.split(preInsertText);
+
+ solText = splitText.join("").split(postInsertText);
+ solText = solText.join("");
+
+ fs.writeFileSync(solutionPath,solText,{encoding:"utf8"});
+
+ // select first ItemsGroups with a ChildNode ProjectReference
+ // ideally select all, and look for @attrib 'Include'= projectFullPath
+ var projectRefNodesPar = this.xml.find("ItemGroup/ProjectReference[@Include='" + relative_path + "']/..");
+ if(projectRefNodesPar) {
+ this.xml.getroot().remove(0, projectRefNodesPar);
+ }
+ }
+};
+
+module.exports = jsproj;
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/src/plugman/util/xml-helpers.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/util/xml-helpers.js b/cordova-lib/src/plugman/util/xml-helpers.js
new file mode 100644
index 0000000..601ed4d
--- /dev/null
+++ b/cordova-lib/src/plugman/util/xml-helpers.js
@@ -0,0 +1,196 @@
+/*
+ *
+ * Copyright 2013 Anis Kadri
+ *
+ * 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.
+ *
+*/
+
+/**
+ * contains XML utility functions, some of which are specific to elementtree
+ */
+
+var fs = require('fs')
+ , path = require('path')
+ , _ = require('underscore')
+ , et = require('elementtree');
+
+module.exports = {
+ moveProjFile: function(origFile, projPath, callback) {
+ var src = path.resolve(projPath, origFile)
+ , dest = src.replace('.orig', '');
+
+ fs.createReadStream(src)
+ .pipe(fs.createWriteStream(dest))
+ .on('close', callback);
+ },
+
+ // compare two et.XML nodes, see if they match
+ // compares tagName, text, attributes and children (recursively)
+ equalNodes: function(one, two) {
+ if (one.tag != two.tag) {
+ return false;
+ } else if (one.text.trim() != two.text.trim()) {
+ return false;
+ } else if (one._children.length != two._children.length) {
+ return false;
+ }
+
+ var oneAttribKeys = Object.keys(one.attrib),
+ twoAttribKeys = Object.keys(two.attrib),
+ i = 0, attribName;
+
+ if (oneAttribKeys.length != twoAttribKeys.length) {
+ return false;
+ }
+
+ for (i; i < oneAttribKeys.length; i++) {
+ attribName = oneAttribKeys[i];
+
+ if (one.attrib[attribName] != two.attrib[attribName]) {
+ return false;
+ }
+ }
+
+ for (i; i < one._children.length; i++) {
+ if (!module.exports.equalNodes(one._children[i], two._children[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ // adds node to doc at selector, creating parent if it doesn't exist
+ graftXML: function(doc, nodes, selector, after) {
+ var parent = resolveParent(doc, selector);
+ if (!parent) {
+ //Try to create the parent recursively if necessary
+ try {
+ var parentToCreate = et.XML("<" + path.basename(selector) + ">"),
+ parentSelector = path.dirname(selector);
+
+ this.graftXML(doc, [parentToCreate], parentSelector);
+ } catch (e) {
+ return false;
+ }
+ parent = resolveParent(doc, selector);
+ if (!parent) return false;
+ }
+
+ nodes.forEach(function (node) {
+ // check if child is unique first
+ if (uniqueChild(node, parent)) {
+ var children = parent.getchildren();
+ var insertIdx = after ? findInsertIdx(children, after) : children.length;
+
+ //TODO: replace with parent.insert after the bug in ElementTree is fixed
+ parent.getchildren().splice(insertIdx, 0, node);
+ }
+ });
+
+ return true;
+ },
+
+ // removes node from doc at selector
+ pruneXML: function(doc, nodes, selector) {
+ var parent = resolveParent(doc, selector);
+ if (!parent) return false;
+
+ nodes.forEach(function (node) {
+ var matchingKid = null;
+ if ((matchingKid = findChild(node, parent)) != null) {
+ // stupid elementtree takes an index argument it doesn't use
+ // and does not conform to the python lib
+ parent.remove(0, matchingKid);
+ }
+ });
+
+ return true;
+ },
+
+ parseElementtreeSync: function (filename) {
+ var contents = fs.readFileSync(filename, 'utf-8').replace("\ufeff", "");;
+ return new et.ElementTree(et.XML(contents));
+ }
+};
+
+function findChild(node, parent) {
+ var matchingKids = parent.findall(node.tag)
+ , i, j;
+
+ for (i = 0, j = matchingKids.length ; i < j ; i++) {
+ if (module.exports.equalNodes(node, matchingKids[i])) {
+ return matchingKids[i];
+ }
+ }
+ return null;
+}
+
+function uniqueChild(node, parent) {
+ var matchingKids = parent.findall(node.tag)
+ , i = 0;
+
+ if (matchingKids.length == 0) {
+ return true;
+ } else {
+ for (i; i < matchingKids.length; i++) {
+ if (module.exports.equalNodes(node, matchingKids[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+var ROOT = /^\/([^\/]*)/,
+ ABSOLUTE = /^\/([^\/]*)\/(.*)/;
+function resolveParent(doc, selector) {
+ var parent, tagName, subSelector;
+
+ // handle absolute selector (which elementtree doesn't like)
+ if (ROOT.test(selector)) {
+ tagName = selector.match(ROOT)[1];
+ // test for wildcard "any-tag" root selector
+ if (tagName == '*' || tagName === doc._root.tag) {
+ parent = doc._root;
+
+ // could be an absolute path, but not selecting the root
+ if (ABSOLUTE.test(selector)) {
+ subSelector = selector.match(ABSOLUTE)[2];
+ parent = parent.find(subSelector)
+ }
+ } else {
+ return false;
+ }
+ } else {
+ parent = doc.find(selector)
+ }
+ return parent;
+}
+
+// Find the index at which to insert an entry. After is a ;-separated priority list
+// of tags after which the insertion should be made. E.g. If we need to
+// insert an element C, and the rule is that the order of children has to be
+// As, Bs, Cs. After will be equal to "C;B;A".
+
+function findInsertIdx(children, after) {
+ var childrenTags = children.map(function(child) { return child.tag; });
+ var afters = after.split(";");
+ var afterIndexes = afters.map(function(current) { return childrenTags.lastIndexOf(current); });
+ var foundIndex = _.find(afterIndexes, function(index) { return index != -1; });
+
+ //add to the beginning if no matching nodes are found
+ return typeof foundIndex === 'undefined' ? 0 : foundIndex+1;
+}
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/templates/base.js
----------------------------------------------------------------------
diff --git a/cordova-lib/templates/base.js b/cordova-lib/templates/base.js
new file mode 100644
index 0000000..20af664
--- /dev/null
+++ b/cordova-lib/templates/base.js
@@ -0,0 +1,5 @@
+var exec = require('cordova/exec');
+
+exports.coolMethod = function(arg0, success, error) {
+ exec(success, error, "%pluginName%", "coolMethod", [arg0]);
+};
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/templates/platforms/android/android.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/templates/platforms/android/android.xml b/cordova-lib/templates/platforms/android/android.xml
new file mode 100644
index 0000000..c3dc1b7
--- /dev/null
+++ b/cordova-lib/templates/platforms/android/android.xml
@@ -0,0 +1,12 @@
+<platform name="android">
+ <config-file target="res/xml/config.xml" parent="/*">
+ <feature name="%pluginName%">
+ <param name="android-package" value="%pluginID%.%pluginName%"/>
+ </feature>
+ </config-file>
+ <config-file target="AndroidManifest.xml" parent="/*">
+ <!--add permissions -->
+ </config-file>
+
+ <source-file src="src/android/%pluginName%.java" target-dir="src/%packageName%/%pluginName%" />
+</platform>
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/templates/platforms/android/base.java
----------------------------------------------------------------------
diff --git a/cordova-lib/templates/platforms/android/base.java b/cordova-lib/templates/platforms/android/base.java
new file mode 100644
index 0000000..3136c1c
--- /dev/null
+++ b/cordova-lib/templates/platforms/android/base.java
@@ -0,0 +1,32 @@
+package %pluginID%;
+
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CallbackContext;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * This class echoes a string called from JavaScript.
+ */
+public class CDV%pluginName% extends CordovaPlugin {
+
+ @Override
+ public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
+ if (action.equals("coolMethod")) {
+ String message = args.getString(0);
+ this.coolMethod(message, callbackContext);
+ return true;
+ }
+ return false;
+ }
+
+ private void coolMethod(String message, CallbackContext callbackContext) {
+ if (message != null && message.length() > 0) {
+ callbackContext.success(message);
+ } else {
+ callbackContext.error("Expected one non-empty string argument.");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/templates/platforms/ios/base.m
----------------------------------------------------------------------
diff --git a/cordova-lib/templates/platforms/ios/base.m b/cordova-lib/templates/platforms/ios/base.m
new file mode 100644
index 0000000..cb25117
--- /dev/null
+++ b/cordova-lib/templates/platforms/ios/base.m
@@ -0,0 +1,28 @@
+/********* CDV%pluginName%.m Cordova Plugin Implementation *******/
+
+#import <Cordova/CDV.h>
+
+@interface CDV%pluginName% : CDVPlugin {
+ // Member variables go here.
+}
+
+- (void)coolMethod:(CDVInvokedUrlCommand*)command;
+@end
+
+@implementation CDV%pluginName%
+
+- (void)coolMethod:(CDVInvokedUrlCommand*)command
+{
+ CDVPluginResult* pluginResult = nil;
+ NSString* echo = [command.arguments objectAtIndex:0];
+
+ if (echo != nil && [echo length] > 0) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:echo];
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
+ }
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/cordova-lib/templates/platforms/ios/ios.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/templates/platforms/ios/ios.xml b/cordova-lib/templates/platforms/ios/ios.xml
new file mode 100644
index 0000000..6600567
--- /dev/null
+++ b/cordova-lib/templates/platforms/ios/ios.xml
@@ -0,0 +1,9 @@
+<platform name="ios">
+ <config-file target="config.xml" parent="/*">
+ <feature name="%pluginName%">
+ <param name="ios-package" value="%pluginName%" />
+ </feature>
+ </config-file>
+
+ <source-file src="src/ios/CDV%pluginName%.m" />
+</platform>
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/doc/base.js
----------------------------------------------------------------------
diff --git a/doc/base.js b/doc/base.js
deleted file mode 100644
index 20af664..0000000
--- a/doc/base.js
+++ /dev/null
@@ -1,5 +0,0 @@
-var exec = require('cordova/exec');
-
-exports.coolMethod = function(arg0, success, error) {
- exec(success, error, "%pluginName%", "coolMethod", [arg0]);
-};
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/doc/platforms/android/android.xml
----------------------------------------------------------------------
diff --git a/doc/platforms/android/android.xml b/doc/platforms/android/android.xml
deleted file mode 100644
index c3dc1b7..0000000
--- a/doc/platforms/android/android.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<platform name="android">
- <config-file target="res/xml/config.xml" parent="/*">
- <feature name="%pluginName%">
- <param name="android-package" value="%pluginID%.%pluginName%"/>
- </feature>
- </config-file>
- <config-file target="AndroidManifest.xml" parent="/*">
- <!--add permissions -->
- </config-file>
-
- <source-file src="src/android/%pluginName%.java" target-dir="src/%packageName%/%pluginName%" />
-</platform>
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/doc/platforms/android/base.java
----------------------------------------------------------------------
diff --git a/doc/platforms/android/base.java b/doc/platforms/android/base.java
deleted file mode 100644
index 3136c1c..0000000
--- a/doc/platforms/android/base.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package %pluginID%;
-
-import org.apache.cordova.CordovaPlugin;
-import org.apache.cordova.CallbackContext;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-/**
- * This class echoes a string called from JavaScript.
- */
-public class CDV%pluginName% extends CordovaPlugin {
-
- @Override
- public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
- if (action.equals("coolMethod")) {
- String message = args.getString(0);
- this.coolMethod(message, callbackContext);
- return true;
- }
- return false;
- }
-
- private void coolMethod(String message, CallbackContext callbackContext) {
- if (message != null && message.length() > 0) {
- callbackContext.success(message);
- } else {
- callbackContext.error("Expected one non-empty string argument.");
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/doc/platforms/ios/base.m
----------------------------------------------------------------------
diff --git a/doc/platforms/ios/base.m b/doc/platforms/ios/base.m
deleted file mode 100644
index cb25117..0000000
--- a/doc/platforms/ios/base.m
+++ /dev/null
@@ -1,28 +0,0 @@
-/********* CDV%pluginName%.m Cordova Plugin Implementation *******/
-
-#import <Cordova/CDV.h>
-
-@interface CDV%pluginName% : CDVPlugin {
- // Member variables go here.
-}
-
-- (void)coolMethod:(CDVInvokedUrlCommand*)command;
-@end
-
-@implementation CDV%pluginName%
-
-- (void)coolMethod:(CDVInvokedUrlCommand*)command
-{
- CDVPluginResult* pluginResult = nil;
- NSString* echo = [command.arguments objectAtIndex:0];
-
- if (echo != nil && [echo length] > 0) {
- pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:echo];
- } else {
- pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
- }
-
- [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
-}
-
-@end
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/doc/platforms/ios/ios.xml
----------------------------------------------------------------------
diff --git a/doc/platforms/ios/ios.xml b/doc/platforms/ios/ios.xml
deleted file mode 100644
index 6600567..0000000
--- a/doc/platforms/ios/ios.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<platform name="ios">
- <config-file target="config.xml" parent="/*">
- <feature name="%pluginName%">
- <param name="ios-package" value="%pluginName%" />
- </feature>
- </config-file>
-
- <source-file src="src/ios/CDV%pluginName%.m" />
-</platform>
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/plugman.js
----------------------------------------------------------------------
diff --git a/plugman.js b/plugman.js
deleted file mode 100644
index 4aa1843..0000000
--- a/plugman.js
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- *
- * Copyright 2013 Anis Kadri
- *
- * 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.
- *
-*/
-
-// copyright (c) 2013 Andrew Lunny, Adobe Systems
-
-var events = require('./src/events');
-var Q = require('q');
-
-function addProperty(o, symbol, modulePath, doWrap) {
- var val = null;
-
- if (doWrap) {
- o[symbol] = function() {
- val = val || require(modulePath);
- if (arguments.length && typeof arguments[arguments.length - 1] === 'function') {
- // If args exist and the last one is a function, it's the callback.
- var args = Array.prototype.slice.call(arguments);
- var cb = args.pop();
- val.apply(o, args).done(function(result) {cb(undefined, result)}, cb);
- } else {
- val.apply(o, arguments).done(null, function(err){ throw err; });
- }
- };
- } else {
- // The top-level plugman.foo
- Object.defineProperty(o, symbol, {
- get : function() { return val = val || require(modulePath); },
- set : function(v) { val = v; }
- });
- }
-
- // The plugman.raw.foo
- Object.defineProperty(o.raw, symbol, {
- get : function() { return val = val || require(modulePath); },
- set : function(v) { val = v; }
- });
-}
-
-plugman = {
- on: events.on.bind(events),
- off: events.removeListener.bind(events),
- removeAllListeners: events.removeAllListeners.bind(events),
- emit: events.emit.bind(events),
- raw: {}
-};
-
-addProperty(plugman, 'help', './src/help');
-addProperty(plugman, 'install', './src/install', true);
-addProperty(plugman, 'uninstall', './src/uninstall', true);
-addProperty(plugman, 'fetch', './src/fetch', true);
-addProperty(plugman, 'prepare', './src/prepare');
-addProperty(plugman, 'config', './src/config', true);
-addProperty(plugman, 'owner', './src/owner', true);
-addProperty(plugman, 'adduser', './src/adduser', true);
-addProperty(plugman, 'publish', './src/publish', true);
-addProperty(plugman, 'unpublish', './src/unpublish', true);
-addProperty(plugman, 'search', './src/search', true);
-addProperty(plugman, 'info', './src/info', true);
-addProperty(plugman, 'create', './src/create', true);
-addProperty(plugman, 'platform', './src/platform_operation', true);
-addProperty(plugman, 'config_changes', './src/util/config-changes');
-
-plugman.commands = {
- 'config' : function(cli_opts) {
- plugman.config(cli_opts.argv.remain, function(err) {
- if (err) throw err;
- else console.log('done');
- });
- },
- 'owner' : function(cli_opts) {
- plugman.owner(cli_opts.argv.remain);
- },
- 'install' : function(cli_opts) {
- if(!cli_opts.platform || !cli_opts.project || !cli_opts.plugin) {
- return console.log(plugman.help());
- }
- var cli_variables = {}
- if (cli_opts.variable) {
- cli_opts.variable.forEach(function (variable) {
- var tokens = variable.split('=');
- var key = tokens.shift().toUpperCase();
- if (/^[\w-_]+$/.test(key)) cli_variables[key] = tokens.join('=');
- });
- }
- var opts = {
- subdir: '.',
- cli_variables: cli_variables,
- www_dir: cli_opts.www,
- searchpath: cli_opts.searchpath
- };
-
- var p = Q();
- cli_opts.plugin.forEach(function (pluginSrc) {
- p = p.then(function () {
- return plugman.raw.install(cli_opts.platform, cli_opts.project, pluginSrc, cli_opts.plugins_dir, opts);
- })
- });
-
- return p;
- },
- 'uninstall': function(cli_opts) {
- if(!cli_opts.platform || !cli_opts.project || !cli_opts.plugin) {
- return console.log(plugman.help());
- }
-
- var p = Q();
- cli_opts.plugin.forEach(function (pluginSrc) {
- p = p.then(function () {
- return plugman.raw.uninstall(cli_opts.platform, cli_opts.project, pluginSrc, cli_opts.plugins_dir, { www_dir: cli_opts.www });
- });
- });
-
- return p;
- },
- 'adduser' : function(cli_opts) {
- plugman.adduser(function(err) {
- if (err) throw err;
- else console.log('user added');
- });
- },
-
- 'search' : function(cli_opts) {
- plugman.search(cli_opts.argv.remain, function(err, plugins) {
- if (err) throw err;
- else {
- for(var plugin in plugins) {
- console.log(plugins[plugin].name, '-', plugins[plugin].description || 'no description provided');
- }
- }
- });
- },
- 'info' : function(cli_opts) {
- plugman.info(cli_opts.argv.remain, function(err, plugin_info) {
- if (err) throw err;
- else {
- console.log('name:', plugin_info.name);
- console.log('version:', plugin_info.version);
- if (plugin_info.engines) {
- for(var i = 0, j = plugin_info.engines.length ; i < j ; i++) {
- console.log(plugin_info.engines[i].name, 'version:', plugin_info.engines[i].version);
- }
- }
- }
- });
- },
-
- 'publish' : function(cli_opts) {
- var plugin_path = cli_opts.argv.remain;
- if(!plugin_path) {
- return console.log(plugman.help());
- }
- plugman.publish(plugin_path, function(err) {
- if (err) throw err;
- else console.log('Plugin published');
- });
- },
-
- 'unpublish': function(cli_opts) {
- var plugin = cli_opts.argv.remain;
- if(!plugin) {
- return console.log(plugman.help());
- }
- plugman.unpublish(plugin, function(err) {
- if (err) throw err;
- else console.log('Plugin unpublished');
- });
- },
- 'create': function(cli_opts) {
- if( !cli_opts.name || !cli_opts.plugin_id || !cli_opts.plugin_version) {
- return console.log( plugman.help() );
- }
- var cli_variables = {};
- if (cli_opts.variable) {
- cli_opts.variable.forEach(function (variable) {
- var tokens = variable.split('=');
- var key = tokens.shift().toUpperCase();
- if (/^[\w-_]+$/.test(key)) cli_variables[key] = tokens.join('=');
- });
- }
- plugman.create( cli_opts.name, cli_opts.plugin_id, cli_opts.plugin_version, cli_opts.path || ".", cli_variables );
- },
- 'platform': function(cli_opts) {
- var operation = cli_opts.argv.remain[ 0 ] || "";
- if( ( operation !== 'add' && operation !== 'remove' ) || !cli_opts.platform_name ) {
- return console.log( plugman.help() );
- }
- plugman.platform( { operation: operation, platform_name: cli_opts.platform_name } );
- }
-};
-
-module.exports = plugman;
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/0318d8cd/spec/add_platform.spec.js
----------------------------------------------------------------------
diff --git a/spec/add_platform.spec.js b/spec/add_platform.spec.js
deleted file mode 100644
index 18bad02..0000000
--- a/spec/add_platform.spec.js
+++ /dev/null
@@ -1,64 +0,0 @@
-var platform = require('../src/platform'),
- Q = require('q'),
- fs = require('fs'),
- shell = require('shelljs'),
- plugman = require('../plugman');
-
-describe( 'platform add/remove', function() {
- it( 'should call platform add', function() {
- var sPlatformA = spyOn( platform, 'add' ).andReturn(Q()),
- sPlatformR = spyOn( platform, 'remove' ).andReturn(Q());
- platform.add();
- expect(sPlatformA).toHaveBeenCalled();
- platform.remove();
- expect(sPlatformR).toHaveBeenCalled();
- });
-});
-
-
-describe( 'platform add', function() {
- var done = false,
- existsSync,
- mkdir,
- writeFileSync;
- function platformPromise( f ) {
- f.then( function() { done = true; }, function(err) { done = err; } );
- }
- beforeEach( function() {
- existsSync = spyOn( fs, 'existsSync' ).andReturn( false );
- done = false;
- });
- it( 'should error on non existing plugin.xml', function() {
- runs(function() {
- platformPromise( platform.add() );
- });
- waitsFor(function() { return done; }, 'platform promise never resolved', 500);
- runs(function() {
- expect(''+ done ).toContain( "can't find a plugin.xml. Are you in the plugin?" );
- });
- });
-});
-
-
-describe( 'platform remove', function() {
- var done = false,
- existsSync,
- mkdir,
- writeFileSync;
- function platformPromise( f ) {
- f.then( function() { done = true; }, function(err) { done = err; } );
- }
- beforeEach( function() {
- existsSync = spyOn( fs, 'existsSync' ).andReturn( false );
- done = false;
- });
- it( 'should error on non existing plugin.xml', function() {
- runs(function() {
- platformPromise( platform.remove() );
- });
- waitsFor(function() { return done; }, 'platform promise never resolved', 500);
- runs(function() {
- expect(''+ done ).toContain( "can't find a plugin.xml. Are you in the plugin?" );
- });
- });
-});