cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From agri...@apache.org
Subject [14/16] git commit: config-changes.js: Moved functions into PlatformMunger.
Date Wed, 19 Feb 2014 20:33:00 GMT
config-changes.js: Moved functions into PlatformMunger.


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/04b0e078
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/04b0e078
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/04b0e078

Branch: refs/heads/master
Commit: 04b0e0784ccd9bf39d8b0c7fe30c9c9457a7b165
Parents: c734119
Author: Mark Koudritsky <kamrik@chromium.org>
Authored: Thu Feb 13 15:28:32 2014 -0500
Committer: Andrew Grieve <agrieve@chromium.org>
Committed: Wed Feb 19 15:32:26 2014 -0500

----------------------------------------------------------------------
 spec/util/config-changes.spec.js |  45 +++---
 src/util/config-changes.js       | 279 ++++++++++++++++++----------------
 2 files changed, 174 insertions(+), 150 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/04b0e078/spec/util/config-changes.spec.js
----------------------------------------------------------------------
diff --git a/spec/util/config-changes.spec.js b/spec/util/config-changes.spec.js
index d995023..10f0af5 100644
--- a/spec/util/config-changes.spec.js
+++ b/spec/util/config-changes.spec.js
@@ -144,7 +144,8 @@ describe('config-changes module', function() {
             });
             it('should return a flat config heirarchy for simple, one-off config changes',
function() {
                 var xml;
-                var munge = configChanges.generate_plugin_config_munge(dummyplugin, 'android',
temp, {});
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused');
+                var munge = munger.generate_plugin_config_munge(dummyplugin, {});
                 expect(munge['AndroidManifest.xml']).toBeDefined();
                 expect(munge['AndroidManifest.xml']['/manifest/application']).toBeDefined();
                 xml = (new et.ElementTree(dummy_xml.find('./platform[@name="android"]/config-file[@target="AndroidManifest.xml"]'))).write({xml_declaration:false});
@@ -162,7 +163,8 @@ describe('config-changes module', function() {
                 expect(munge['res/xml/config.xml']['/cordova/plugins'][xml]).toEqual(1);
             });
             it('should split out multiple children of config-file elements into individual
leaves', function() {
-                var munge = configChanges.generate_plugin_config_munge(childrenplugin, 'android',
temp, {});
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused');
+                var munge = munger.generate_plugin_config_munge(childrenplugin, {});
                 expect(munge['AndroidManifest.xml']).toBeDefined();
                 expect(munge['AndroidManifest.xml']['/manifest']).toBeDefined();
                 expect(munge['AndroidManifest.xml']['/manifest']['<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"
/>']).toBeDefined();
@@ -175,26 +177,31 @@ describe('config-changes module', function() {
                 expect(munge['AndroidManifest.xml']['/manifest']['<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"
/>']).toBeDefined();
             });
             it('should not use xml comments as config munge leaves', function() {
-                var munge = configChanges.generate_plugin_config_munge(childrenplugin, 'android',
temp, {});
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused');
+                var munge = munger.generate_plugin_config_munge(childrenplugin, {});
                 expect(munge['AndroidManifest.xml']['/manifest']['<!--library-->']).not.toBeDefined();
                 expect(munge['AndroidManifest.xml']['/manifest']['<!-- GCM connects to
Google Services. -->']).not.toBeDefined();
             });
             it('should increment config heirarchy leaves if dfferent config-file elements
target the same file + selector + xml', function() {
-                var munge = configChanges.generate_plugin_config_munge(configplugin, 'android',
temp, {});
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused');
+                var munge = munger.generate_plugin_config_munge(configplugin, {});
                 expect(munge['res/xml/config.xml']['/widget']['<poop />']).toEqual(2);
             });
             it('should take into account interpolation variables', function() {
-                var munge = configChanges.generate_plugin_config_munge(childrenplugin, 'android',
temp, {PACKAGE_NAME:'ca.filmaj.plugins'});
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused');
+                var munge = munger.generate_plugin_config_munge(childrenplugin, {PACKAGE_NAME:'ca.filmaj.plugins'});
                 expect(munge['AndroidManifest.xml']['/manifest']['<uses-permission android:name="ca.filmaj.plugins.permission.C2D_MESSAGE"
/>']).toBeDefined();
             });
             it('should create munges for platform-agnostic config.xml changes', function()
{
-                var munge = configChanges.generate_plugin_config_munge(dummyplugin, 'android',
temp, {});
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused');
+                var munge = munger.generate_plugin_config_munge(dummyplugin, {});
                 expect(munge['config.xml']['/*']['<access origin="build.phonegap.com"
/>']).toBeDefined();
                 expect(munge['config.xml']['/*']['<access origin="s3.amazonaws.com" />']).toBeDefined();
             });
             it('should automatically add on app java identifier as PACKAGE_NAME variable
for android config munges', function() {
                 shell.cp('-rf', android_two_project, temp);
-                var munge = configChanges.generate_plugin_config_munge(varplugin, 'android',
temp, {});
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused');
+                var munge = munger.generate_plugin_config_munge(varplugin, {});
                 var expected_xml = '<package>com.alunny.childapp</package>';
                 expect(munge['AndroidManifest.xml']['/manifest'][expected_xml]).toBeDefined();
             });
@@ -205,12 +212,14 @@ describe('config-changes module', function() {
                 shell.cp('-rf', ios_config_xml, temp);
             });
             it('should automatically add on ios bundle identifier as PACKAGE_NAME variable
for ios config munges', function() {
-                var munge = configChanges.generate_plugin_config_munge(varplugin, 'ios',
temp, {});
+                var munger = new configChanges.PlatformMunger('ios', temp, 'unused');
+                var munge = munger.generate_plugin_config_munge(varplugin, {});
                 var expected_xml = '<cfbundleid>com.example.friendstring</cfbundleid>';
                 expect(munge['config.xml']['/widget'][expected_xml]).toBeDefined();
             });
             it('should special case framework elements for ios', function() {
-                var munge = configChanges.generate_plugin_config_munge(cbplugin, 'ios', temp,
{});
+                var munger = new configChanges.PlatformMunger('ios', temp, 'unused');
+                var munge = munger.generate_plugin_config_munge(cbplugin, {});
                 expect(munge['framework']).toBeDefined();
                 expect(munge['framework']['libsqlite3.dylib']['false']).toBeDefined();
                 expect(munge['framework']['social.framework']['true']).toBeDefined();
@@ -229,9 +238,10 @@ describe('config-changes module', function() {
             var cfg = configChanges.get_platform_json(plugins_dir, 'android');
             cfg.prepare_queue.installed = [{'plugin':'DummyPlugin', 'vars':{}}];
             configChanges.save_platform_json(cfg, plugins_dir, 'android');
-            var spy = spyOn(configChanges, 'generate_plugin_config_munge').andReturn({});
-            configChanges.process(plugins_dir, temp, 'android');
-            expect(spy).toHaveBeenCalledWith(path.join(plugins_dir, 'DummyPlugin'), 'android',
temp, {});
+            var munger = new configChanges.PlatformMunger('android', temp, plugins_dir);
+            var spy = spyOn(munger, 'generate_plugin_config_munge').andReturn({});
+            munger.process();
+            expect(spy).toHaveBeenCalledWith(path.join(plugins_dir, 'DummyPlugin'), {});
         });
         it('should get a reference to existing config munge by calling get_platform_json',
function() {
             shell.cp('-rf', android_two_project, temp);
@@ -363,17 +373,16 @@ describe('config-changes module', function() {
                 shell.cp('-rf', varplugin, plugins_dir);
                 // Run through an "install"
                 configChanges.add_installed_plugin_to_prepare_queue(plugins_dir, 'VariablePlugin',
'android', {"API_KEY":"canucks"});
-                configChanges.process(plugins_dir, temp, 'android');
+                var munger = new configChanges.PlatformMunger('android', temp, plugins_dir);
+                munger.process();
 
                 // Now set up an uninstall and make sure prunexml is called properly
                 configChanges.add_uninstalled_plugin_to_prepare_queue(plugins_dir, 'VariablePlugin',
'android');
-                var spy = spyOn(configChanges, 'generate_plugin_config_munge').andReturn({});
-                configChanges.process(plugins_dir, temp, 'android');
+                var spy = spyOn(munger, 'generate_plugin_config_munge').andReturn({});
+                munger.process();
                 var munge_params = spy.mostRecentCall.args;
                 expect(munge_params[0]).toEqual(path.join(plugins_dir, 'VariablePlugin'));
-                expect(munge_params[1]).toEqual('android');
-                expect(munge_params[2]).toEqual(temp);
-                expect(munge_params[3]['API_KEY']).toEqual('canucks');
+                expect(munge_params[1]['API_KEY']).toEqual('canucks');
             });
             it('should not call pruneXML for a config munge that another plugin depends on',
function() {
                 shell.cp('-rf', android_two_no_perms_project, temp);

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/04b0e078/src/util/config-changes.js
----------------------------------------------------------------------
diff --git a/src/util/config-changes.js b/src/util/config-changes.js
index ea8badd..36687ca 100644
--- a/src/util/config-changes.js
+++ b/src/util/config-changes.js
@@ -58,52 +58,31 @@ var keep_these_frameworks = [
 
 var package = module.exports = {};
 
+package.PlatformMunger = PlatformMunger;
 
 /******************************************************************************
 Adapters to keep the current refactoring effort to within this file
 ******************************************************************************/
-package.add_plugin_changes = function (platform, project_dir, plugins_dir, plugin_id, plugin_vars,
is_top_level, should_increment, cache) {
-    // TODO: Treat separately the case of should_increment = false, it's only used by cordova
cli for "cordova prepare"
+package.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.apply_plugin_changes(plugin_id, plugin_vars, is_top_level, should_increment, cache);
     munger.config_keeper.save_all();
 };
 
-package.remove_plugin_changes = function (platform, project_dir, plugins_dir, plugin_name,
plugin_id, is_top_level, should_decrement) {
+package.remove_plugin_changes = function(platform, project_dir, plugins_dir, plugin_name,
plugin_id, is_top_level, should_decrement) {
+    // TODO: should_decrement paramenter is never used, remove it here and wherever called
     var munger = new PlatformMunger(platform, project_dir, plugins_dir);
-    munger.unmerge_plugin_changes(plugin_name, plugin_id, is_top_level, should_decrement);
+    munger.unmerge_plugin_changes(plugin_name, plugin_id, is_top_level);
     munger.config_keeper.save_all();
 };
-/******************************************************************************/
-
-
-
-package.process = process_all;
-function process_all(plugins_dir, project_dir, platform) {
-    checkPlatform(platform);
-
-    var platform_config = module.exports.get_platform_json(plugins_dir, platform);
-    // Uninstallation first
-    platform_config.prepare_queue.uninstalled.forEach(function(u) {
-        module.exports.remove_plugin_changes(platform, project_dir, plugins_dir, u.plugin,
u.id, u.topLevel, true);
-    });
-
-    // Now handle installation
-    var cache = {};
-    platform_config.prepare_queue.installed.forEach(function(u) {
-        module.exports.add_plugin_changes(platform, project_dir, plugins_dir, u.plugin, u.vars,
u.topLevel, true, cache);
-    });
 
-    platform_config = module.exports.get_platform_json(plugins_dir, platform);
-
-    // Empty out uninstalled queue.
-    platform_config.prepare_queue.uninstalled = [];
-    // Empty out installed queue.
-    platform_config.prepare_queue.installed = [];
-    // save
-    module.exports.save_platform_json(platform_config, plugins_dir, platform);
-}
+package.process = function(plugins_dir, project_dir, platform) {
+    var munger = new PlatformMunger(platform, project_dir, plugins_dir);
+    munger.process();
+    munger.config_keeper.save_all();
+};
 
+/******************************************************************************/
 
 
 package.add_installed_plugin_to_prepare_queue = add_installed_plugin_to_prepare_queue;
@@ -124,98 +103,19 @@ function add_uninstalled_plugin_to_prepare_queue(plugins_dir, plugin,
platform,
     module.exports.save_platform_json(config, plugins_dir, platform);
 }
 
-package.generate_plugin_config_munge = generate_plugin_config_munge;
-function generate_plugin_config_munge(plugin_dir, platform, project_dir, vars) {
-    checkPlatform(platform);
-
-    vars = vars || {};
-    var platform_handler = platforms[platform];
-    // Add PACKAGE_NAME variable into vars
-    if (!vars['PACKAGE_NAME']) {
-        vars['PACKAGE_NAME'] = platform_handler.package_name(project_dir);
-    }
-
-    var munge = {};
-    var plugin_xml = xml_helpers.parseElementtreeSync(path.join(plugin_dir, 'plugin.xml'));
-
-    var platformTag = plugin_xml.find('platform[@name="' + 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) {
-                if (!munge['framework']) {
-                    munge['framework'] = {};
-                }
-                var file = f.attrib['src'];
-                var weak = f.attrib['weak'];
-                weak = (weak === undefined || weak === null || weak != 'true' ? 'false' :
'true');
-                if (!munge['framework'][file]) {
-                    munge['framework'][file] = {};
-                }
-                if (!munge['framework'][file][weak]) {
-                    munge['framework'][file][weak] = 0;
-                }
-                munge['framework'][file][weak] += 1;
-            }
-        });
-    }
-
-    changes.forEach(function(change) {
-        var target = change.attrib['target'];
-        var parent = change.attrib['parent'];
-        if (!munge[target]) {
-            munge[target] = {};
-        }
-        if (!munge[target][parent]) {
-            munge[target][parent] = {};
-        }
-        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
-            if (!munge[target][parent][stringified]) {
-                munge[target][parent][stringified] = 0;
-            }
-            munge[target][parent][stringified] += 1;
-        });
-    });
-    return munge;
-}
-
-/// ^^^^^^^^ NO MANS LAND ^^^^^^^
-
-
-
-
 
-/********************************************************
+/******************************************************************************
 * PlatformMunger class
 *
 * Can deal with config file of a single porject.
-********************************************************/
+******************************************************************************/
 
-// Objects of the PlatformMunger class can deal with config file of a single porject.
 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();
 }
 
@@ -260,14 +160,14 @@ function PlatformMunger_apply_file_munge(file, munge, remove) {
 
 
 PlatformMunger.prototype.unmerge_plugin_changes = unmerge_plugin_changes;
-function unmerge_plugin_changes(plugin_name, plugin_id, is_top_level, should_decrement) {
+function unmerge_plugin_changes(plugin_name, plugin_id, is_top_level) {
     var self = this;
     var platform_config = module.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 = module.exports.generate_plugin_config_munge(plugin_dir, self.platform,
self.project_dir, plugin_vars);
+    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);
@@ -302,15 +202,25 @@ function apply_plugin_changes(plugin_id, plugin_vars, is_top_level,
should_incre
     var self = this;
     var platform_config = module.exports.get_platform_json(self.plugins_dir, self.platform);
     var plugin_dir = path.join(self.plugins_dir, plugin_id);
-    debugger;
+
     plugin_id = xml_helpers.parseElementtreeSync(path.join(plugin_dir, 'plugin.xml'), 'utf-8')._root.attrib['id'];
 
     // get config munge, aka how should this plugin change various config files
-    var config_munge = module.exports.generate_plugin_config_munge(plugin_dir, self.platform,
self.project_dir, plugin_vars);
+    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 = increment_munge(global_munge, config_munge);
+    // 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) {
         // TODO: remove this warning some time after 3.4 is out.
         if (file == 'plugins-plist' && self.platform == 'ios') {
@@ -335,16 +245,119 @@ function apply_plugin_changes(plugin_id, plugin_vars, is_top_level,
should_incre
     // save
     module.exports.save_platform_json(platform_config, self.plugins_dir, self.platform);
 }
+
+// generate_plugin_config_munge
+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 = {};
+    var plugin_xml = xml_helpers.parseElementtreeSync(path.join(plugin_dir, 'plugin.xml'));
+
+    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) {
+                if (!munge['framework']) {
+                    munge['framework'] = {};
+                }
+                var file = f.attrib['src'];
+                var weak = ('true' == f.attrib['weak']);
+                if (!munge['framework'][file]) {
+                    munge['framework'][file] = {};
+                }
+                if (!munge['framework'][file][weak]) {
+                    munge['framework'][file][weak] = 0;
+                }
+                munge['framework'][file][weak] += 1;
+            }
+        });
+    }
+
+    changes.forEach(function(change) {
+        var target = change.attrib['target'];
+        var parent = change.attrib['parent'];
+        if (!munge[target]) {
+            munge[target] = {};
+        }
+        if (!munge[target][parent]) {
+            munge[target][parent] = {};
+        }
+        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
+            if (!munge[target][parent][stringified]) {
+                munge[target][parent][stringified] = 0;
+            }
+            munge[target][parent][stringified] += 1;
+        });
+    });
+    return munge;
+}
+
+// Go over the prepare queue an apply the config munges for all plugins
+// that have been (un)installed.
+PlatformMunger.prototype.process = PlatformMunger_process;
+function PlatformMunger_process() {
+    var self = this;
+
+    var platform_config = module.exports.get_platform_json(self.plugins_dir, self.platform);
+
+    // Uninstallation first
+    platform_config.prepare_queue.uninstalled.forEach(function(u) {
+        self.unmerge_plugin_changes(u.plugin, u.id, u.topLevel);
+    });
+
+    // Now handle installation
+    platform_config.prepare_queue.installed.forEach(function(u) {
+        self.apply_plugin_changes(u.plugin, u.vars, u.topLevel, true);
+    });
+
+    platform_config = module.exports.get_platform_json(self.plugins_dir, self.platform);
+
+    // Empty out uninstalled queue.
+    platform_config.prepare_queue.uninstalled = [];
+    // Empty out installed queue.
+    platform_config.prepare_queue.installed = [];
+    // save
+    module.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 reparsing
 * and writing them out multiple times.
-********************************************************/
+******************************************************************************/
 function ConfigKeeper() {
     this._cached = {};
 }
@@ -371,8 +384,10 @@ function ConfigKeeper_save_all() {
     });
 }
 
-/**** TODO move save/get_platform_json those to be part of ConfigKeeper ****/
-
+// TODO: move save/get_platform_json those to be part of ConfigKeeper
+// But they are used in many places in plugman and cordova-cli
+// and can save the file bypassing the ConfigKeeper's cache.
+// Must change in all those places as well.
 package.get_platform_json = get_platform_json;
 function get_platform_json(plugins_dir, platform) {
     checkPlatform(platform);
@@ -403,9 +418,9 @@ function save_platform_json(config, plugins_dir, platform) {
 /**** END of ConfigKeeper ****/
 
 
-/********************************************************
+/******************************************************************************
 * ConfigFile class
-********************************************************/
+******************************************************************************/
 function ConfigFile(project_dir, platform, file_tag) {
     this.project_dir = project_dir;
     this.platform = platform;
@@ -510,9 +525,9 @@ function ConfigFile_prune_child(selector, xml_child) {
 /**** END of ConfigFile ****/
 
 
-/********************************************************
+/******************************************************************************
 * Utility functions
-********************************************************/
+******************************************************************************/
 
 // Check if we know such platform
 function checkPlatform(platform) {
@@ -584,9 +599,9 @@ function resolveConfigFilePath(project_dir, platform, file) {
 }
 
 
-/********************************************************
+/******************************************************************************
 * Munge object manipulations functions
-********************************************************/
+******************************************************************************/
 
 // Increment obj[key1][key2]...[keyN] by val. If it
 // didn't exist, set it to val.
@@ -655,5 +670,5 @@ function decrement_munge(base, munge) {
 
 // For better readability where used
 function clone_munge(munge) {
-    return increment_munge({}, munge)
+    return increment_munge({}, munge);
 }


Mime
View raw message