2 * @license r.js 2.1.8+ Fri, 30 Aug 2013 03:19:39 GMT Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
3 * Available via the MIT or new BSD license.
4 * see: http://github.com/jrburke/requirejs for details
8 * This is a bootstrap script to allow running RequireJS in the command line
9 * in either a Java/Rhino or Node environment. It is modified by the top-level
10 * dist.js file to inject other files to completely enable this file. It is
11 * the shell of the r.js file.
14 /*jslint evil: true, nomen: true, sloppy: true */
15 /*global readFile: true, process: false, Packages: false, print: false,
16 console: false, java: false, module: false, requirejsVars, navigator,
17 document, importScripts, self, location, Components, FileUtils */
19 var requirejs, require, define, xpcUtil;
20 (function (console, args, readFileFunc) {
21 var fileName, env, fs, vm, path, exec, rhinoContext, dir, nodeRequire,
22 nodeDefine, exists, reqMain, loadedOptimizedLib, existsForNode, Cc, Ci,
23 version = '2.1.8+ Fri, 30 Aug 2013 03:19:39 GMT',
24 jsSuffixRegExp = /\.js$/,
27 //Used by jslib/rhino/args.js
29 //Used by jslib/xpconnect/args.js
31 readFile = typeof readFileFunc !== 'undefined' ? readFileFunc : null;
34 console.log('See https://github.com/jrburke/r.js for usage.');
37 if ((typeof navigator !== 'undefined' && typeof document !== 'undefined') ||
38 (typeof importScripts !== 'undefined' && typeof self !== 'undefined')) {
41 readFile = function (path) {
42 return fs.readFileSync(path, 'utf8');
45 exec = function (string) {
49 exists = function () {
50 console.log('x.js exists not applicable in browser env');
54 } else if (typeof Packages !== 'undefined') {
59 if (fileName && fileName.indexOf('-') === 0) {
60 commandOption = fileName.substring(1);
64 //Set up execution context.
65 rhinoContext = Packages.org.mozilla.javascript.ContextFactory.getGlobal().enterContext();
67 exec = function (string, name) {
68 return rhinoContext.evaluateString(this, string, name, 0, null);
71 exists = function (fileName) {
72 return (new java.io.File(fileName)).exists();
75 //Define a console.log for easier logging. Don't
77 if (typeof console === 'undefined') {
80 print.apply(undefined, arguments);
84 } else if (typeof process !== 'undefined' && process.versions && !!process.versions.node) {
87 //Get the fs module via Node's require before it
88 //gets replaced. Used in require/node.js
91 path = require('path');
92 //In Node 0.7+ existsSync is on fs.
93 existsForNode = fs.existsSync || path.existsSync;
95 nodeRequire = require;
97 reqMain = require.main;
99 //Temporarily hide require and define to allow require.js to define
104 readFile = function (path) {
105 return fs.readFileSync(path, 'utf8');
108 exec = function (string, name) {
109 return vm.runInThisContext(this.requirejsVars.require.makeNodeWrapper(string),
110 name ? fs.realpathSync(name) : '');
113 exists = function (fileName) {
114 return existsForNode(fileName);
118 fileName = process.argv[2];
120 if (fileName && fileName.indexOf('-') === 0) {
121 commandOption = fileName.substring(1);
122 fileName = process.argv[3];
124 } else if (typeof Components !== 'undefined' && Components.classes && Components.interfaces) {
127 Components.utils['import']('resource://gre/modules/FileUtils.jsm');
128 Cc = Components.classes;
129 Ci = Components.interfaces;
133 if (fileName && fileName.indexOf('-') === 0) {
134 commandOption = fileName.substring(1);
139 isWindows: ('@mozilla.org/windows-registry-key;1' in Cc),
141 return FileUtils.getFile("CurWorkD", []).path;
144 //Remove . and .. from paths, normalize on front slashes
145 normalize: function (path) {
146 //There has to be an easier way to do this.
148 firstChar = path.charAt(0);
150 if (firstChar !== '/' &&
151 firstChar !== '\\' &&
152 path.indexOf(':') === -1) {
153 //A relative path. Use the current working directory.
154 path = xpcUtil.cwd() + '/' + path;
157 ary = path.replace(/\\/g, '/').split('/');
159 for (i = 0; i < ary.length; i += 1) {
164 } else if (part === '..') {
165 ary.splice(i - 1, 2);
169 return ary.join('/');
172 xpfile: function (path) {
175 fullPath = xpcUtil.normalize(path);
176 if (xpcUtil.isWindows) {
177 fullPath = fullPath.replace(/\//g, '\\');
179 return new FileUtils.File(fullPath);
181 throw new Error((fullPath || path) + ' failed: ' + e);
185 readFile: function (/*String*/path, /*String?*/encoding) {
186 //A file read function that can deal with BOMs
187 encoding = encoding || "utf-8";
189 var inStream, convertStream,
191 fileObj = xpcUtil.xpfile(path);
193 //XPCOM, you so crazy
195 inStream = Cc['@mozilla.org/network/file-input-stream;1']
196 .createInstance(Ci.nsIFileInputStream);
197 inStream.init(fileObj, 1, 0, false);
199 convertStream = Cc['@mozilla.org/intl/converter-input-stream;1']
200 .createInstance(Ci.nsIConverterInputStream);
201 convertStream.init(inStream, encoding, inStream.available(),
202 Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
204 convertStream.readString(inStream.available(), readData);
205 return readData.value;
207 throw new Error((fileObj && fileObj.path || '') + ': ' + e);
210 convertStream.close();
219 readFile = xpcUtil.readFile;
221 exec = function (string) {
225 exists = function (fileName) {
226 return xpcUtil.xpfile(fileName).exists();
229 //Define a console.log for easier logging. Don't
231 if (typeof console === 'undefined') {
234 print.apply(undefined, arguments);
240 /** vim: et:ts=4:sw=4:sts=4
241 * @license RequireJS 2.1.8 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
242 * Available via the MIT or new BSD license.
243 * see: http://github.com/jrburke/requirejs for details
245 //Not using strict: uneven strict support in browsers, #392, and causes
246 //problems with requirejs.exec()/transpiler plugins that may not be strict.
247 /*jslint regexp: true, nomen: true, sloppy: true */
248 /*global window, navigator, document, importScripts, setTimeout, opera */
252 var req, s, head, baseElement, dataMain, src,
253 interactiveScript, currentlyAddingScript, mainScript, subPath,
255 commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
256 cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
257 jsSuffixRegExp = /\.js$/,
258 currDirRegExp = /^\.\//,
259 op = Object.prototype,
260 ostring = op.toString,
261 hasOwn = op.hasOwnProperty,
262 ap = Array.prototype,
264 isBrowser = !!(typeof window !== 'undefined' && navigator && window.document),
265 isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
266 //PS3 indicates loaded and complete, but need to wait for complete
267 //specifically. Sequence is 'loading', 'loaded', execution,
268 // then 'complete'. The UA check is unfortunate, but not sure how
269 //to feature test w/o causing perf issues.
270 readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ?
271 /^complete$/ : /^(complete|loaded)$/,
272 defContextName = '_',
273 //Oh the tragedy, detecting opera. See the usage of isOpera for reason.
274 isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]',
278 useInteractive = false;
280 function isFunction(it) {
281 return ostring.call(it) === '[object Function]';
284 function isArray(it) {
285 return ostring.call(it) === '[object Array]';
289 * Helper function for iterating over an array. If the func returns
290 * a true value, it will break out of the loop.
292 function each(ary, func) {
295 for (i = 0; i < ary.length; i += 1) {
296 if (ary[i] && func(ary[i], i, ary)) {
304 * Helper function for iterating over an array backwards. If the func
305 * returns a true value, it will break out of the loop.
307 function eachReverse(ary, func) {
310 for (i = ary.length - 1; i > -1; i -= 1) {
311 if (ary[i] && func(ary[i], i, ary)) {
318 function hasProp(obj, prop) {
319 return hasOwn.call(obj, prop);
322 function getOwn(obj, prop) {
323 return hasProp(obj, prop) && obj[prop];
327 * Cycles over properties in an object and calls a function for each
328 * property value. If the function returns a truthy value, then the
329 * iteration is stopped.
331 function eachProp(obj, func) {
334 if (hasProp(obj, prop)) {
335 if (func(obj[prop], prop)) {
343 * Simple function to mix in properties from source into target,
344 * but only if target does not already have a property of the same name.
346 function mixin(target, source, force, deepStringMixin) {
348 eachProp(source, function (value, prop) {
349 if (force || !hasProp(target, prop)) {
350 if (deepStringMixin && typeof value !== 'string') {
354 mixin(target[prop], value, force, deepStringMixin);
356 target[prop] = value;
364 //Similar to Function.prototype.bind, but the 'this' object is specified
365 //first, since it is easier to read/figure out what 'this' will be.
366 function bind(obj, fn) {
368 return fn.apply(obj, arguments);
373 return document.getElementsByTagName('script');
376 function defaultOnError(err) {
380 //Allow getting a global that expressed in
381 //dot notation, like 'a.b.c'.
382 function getGlobal(value) {
387 each(value.split('.'), function (part) {
394 * Constructs an error with a pointer to an URL with more information.
395 * @param {String} id the error ID that maps to an ID on a web page.
396 * @param {String} message human readable error.
397 * @param {Error} [err] the original error, if there is one.
401 function makeError(id, msg, err, requireModules) {
402 var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);
404 e.requireModules = requireModules;
406 e.originalError = err;
411 if (typeof define !== 'undefined') {
412 //If a define is already in play via another AMD loader,
417 if (typeof requirejs !== 'undefined') {
418 if (isFunction(requirejs)) {
419 //Do not overwrite and existing requirejs instance.
423 requirejs = undefined;
426 //Allow for a require config object
427 if (typeof require !== 'undefined' && !isFunction(require)) {
428 //assume it is a config object.
433 function newContext(contextName) {
434 var inCheckLoaded, Module, context, handlers,
435 checkLoadedTimeoutId,
437 //Defaults. Do not set a default for map
438 //config to speed up normalize(), which
439 //will run faster if there is no default.
448 //registry of just enabled modules, to speed
449 //cycle breaking code when lots of modules
450 //are registered, but not activated.
451 enabledRegistry = {},
457 unnormalizedCounter = 1;
460 * Trims the . and .. from an array of path segments.
461 * It will keep a leading path segment if a .. will become
462 * the first path segment, to help with module name lookups,
463 * which act like paths, but can be remapped. But the end result,
464 * all paths that use this function should look normalized.
465 * NOTE: this method MODIFIES the input array.
466 * @param {Array} ary the array of path segments.
468 function trimDots(ary) {
470 for (i = 0; ary[i]; i += 1) {
475 } else if (part === '..') {
476 if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
477 //End of the line. Keep at least one non-dot
478 //path segment at the front so it can be mapped
479 //correctly to disk. Otherwise, there is likely
480 //no path mapping for a path starting with '..'.
481 //This can still fail, but catches the most reasonable
485 ary.splice(i - 1, 2);
493 * Given a relative module name, like ./something, normalize it to
494 * a real name that can be mapped to a path.
495 * @param {String} name the relative name
496 * @param {String} baseName a real name that the name arg is relative
498 * @param {Boolean} applyMap apply the map config to the value. Should
499 * only be done if this normalization is for a dependency ID.
500 * @returns {String} normalized name
502 function normalize(name, baseName, applyMap) {
503 var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment,
504 foundMap, foundI, foundStarMap, starI,
505 baseParts = baseName && baseName.split('/'),
506 normalizedBaseParts = baseParts,
508 starMap = map && map['*'];
510 //Adjust any relative paths.
511 if (name && name.charAt(0) === '.') {
512 //If have a base name, try to normalize against it,
513 //otherwise, assume it is a top-level require that will
514 //be relative to baseUrl in the end.
516 if (getOwn(config.pkgs, baseName)) {
517 //If the baseName is a package name, then just treat it as one
518 //name to concat the name with.
519 normalizedBaseParts = baseParts = [baseName];
521 //Convert baseName to array, and lop off the last part,
522 //so that . matches that 'directory' and not name of the baseName's
523 //module. For instance, baseName of 'one/two/three', maps to
524 //'one/two/three.js', but we want the directory, 'one/two' for
525 //this normalization.
526 normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
529 name = normalizedBaseParts.concat(name.split('/'));
532 //Some use of packages may use a . path to reference the
533 //'main' module name, so normalize for that.
534 pkgConfig = getOwn(config.pkgs, (pkgName = name[0]));
535 name = name.join('/');
536 if (pkgConfig && name === pkgName + '/' + pkgConfig.main) {
539 } else if (name.indexOf('./') === 0) {
540 // No baseName, so this is ID is resolved relative
541 // to baseUrl, pull off the leading dot.
542 name = name.substring(2);
546 //Apply map config if available.
547 if (applyMap && map && (baseParts || starMap)) {
548 nameParts = name.split('/');
550 for (i = nameParts.length; i > 0; i -= 1) {
551 nameSegment = nameParts.slice(0, i).join('/');
554 //Find the longest baseName segment match in the config.
555 //So, do joins on the biggest to smallest lengths of baseParts.
556 for (j = baseParts.length; j > 0; j -= 1) {
557 mapValue = getOwn(map, baseParts.slice(0, j).join('/'));
559 //baseName segment has config, find if it has one for
562 mapValue = getOwn(mapValue, nameSegment);
564 //Match, update name to the new value.
577 //Check for a star map match, but just hold on to it,
578 //if there is a shorter segment match later in a matching
579 //config, then favor over this star map.
580 if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) {
581 foundStarMap = getOwn(starMap, nameSegment);
586 if (!foundMap && foundStarMap) {
587 foundMap = foundStarMap;
592 nameParts.splice(0, foundI, foundMap);
593 name = nameParts.join('/');
600 function removeScript(name) {
602 each(scripts(), function (scriptNode) {
603 if (scriptNode.getAttribute('data-requiremodule') === name &&
604 scriptNode.getAttribute('data-requirecontext') === context.contextName) {
605 scriptNode.parentNode.removeChild(scriptNode);
612 function hasPathFallback(id) {
613 var pathConfig = getOwn(config.paths, id);
614 if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {
616 //Pop off the first array value, since it failed, and
619 context.require.undef(id);
620 context.require([id]);
625 //Turns a plugin!resource to [plugin, resource]
626 //with the plugin being undefined if the name
627 //did not have a plugin prefix.
628 function splitPrefix(name) {
630 index = name ? name.indexOf('!') : -1;
632 prefix = name.substring(0, index);
633 name = name.substring(index + 1, name.length);
635 return [prefix, name];
639 * Creates a module mapping that includes plugin prefix, module
640 * name, and path. If parentModuleMap is provided it will
641 * also normalize the name via require.normalize()
643 * @param {String} name the module name
644 * @param {String} [parentModuleMap] parent module map
645 * for the module name, used to resolve relative names.
646 * @param {Boolean} isNormalized: is the ID already normalized.
647 * This is true if this call is done for a define() module ID.
648 * @param {Boolean} applyMap: apply the map config to the ID.
649 * Should only be true if this map is for a dependency.
653 function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {
654 var url, pluginModule, suffix, nameParts,
656 parentName = parentModuleMap ? parentModuleMap.name : null,
661 //If no name, then it means it is a require call, generate an
665 name = '_@r' + (requireCounter += 1);
668 nameParts = splitPrefix(name);
669 prefix = nameParts[0];
673 prefix = normalize(prefix, parentName, applyMap);
674 pluginModule = getOwn(defined, prefix);
677 //Account for relative paths if there is a base name.
680 if (pluginModule && pluginModule.normalize) {
681 //Plugin is loaded, use its normalize method.
682 normalizedName = pluginModule.normalize(name, function (name) {
683 return normalize(name, parentName, applyMap);
686 normalizedName = normalize(name, parentName, applyMap);
690 normalizedName = normalize(name, parentName, applyMap);
692 //Normalized name may be a plugin ID due to map config
693 //application in normalize. The map config values must
694 //already be normalized, so do not need to redo that part.
695 nameParts = splitPrefix(normalizedName);
696 prefix = nameParts[0];
697 normalizedName = nameParts[1];
700 url = context.nameToUrl(normalizedName);
704 //If the id is a plugin id that cannot be determined if it needs
705 //normalization, stamp it with a unique ID so two matching relative
706 //ids that may conflict can be separate.
707 suffix = prefix && !pluginModule && !isNormalized ?
708 '_unnormalized' + (unnormalizedCounter += 1) :
713 name: normalizedName,
714 parentMap: parentModuleMap,
715 unnormalized: !!suffix,
717 originalName: originalName,
720 prefix + '!' + normalizedName :
721 normalizedName) + suffix
725 function getModule(depMap) {
727 mod = getOwn(registry, id);
730 mod = registry[id] = new context.Module(depMap);
736 function on(depMap, name, fn) {
738 mod = getOwn(registry, id);
740 if (hasProp(defined, id) &&
741 (!mod || mod.defineEmitComplete)) {
742 if (name === 'defined') {
746 mod = getModule(depMap);
747 if (mod.error && name === 'error') {
755 function onError(err, errback) {
756 var ids = err.requireModules,
762 each(ids, function (id) {
763 var mod = getOwn(registry, id);
765 //Set error on module, so it skips timeout checks.
767 if (mod.events.error) {
769 mod.emit('error', err);
781 * Internal method to transfer globalQueue items to this context's
784 function takeGlobalQueue() {
785 //Push all the globalDefQueue items into the context's defQueue
786 if (globalDefQueue.length) {
787 //Array splice in the values since the context code has a
788 //local var ref to defQueue, so cannot just reassign the one
791 [defQueue.length - 1, 0].concat(globalDefQueue));
797 'require': function (mod) {
801 return (mod.require = context.makeRequire(mod.map));
804 'exports': function (mod) {
805 mod.usingExports = true;
806 if (mod.map.isDefine) {
810 return (mod.exports = defined[mod.map.id] = {});
814 'module': function (mod) {
818 return (mod.module = {
821 config: function () {
823 pkg = getOwn(config.pkgs, mod.map.id);
824 // For packages, only support config targeted
825 // at the main module.
826 c = pkg ? getOwn(config.config, mod.map.id + '/' + pkg.main) :
827 getOwn(config.config, mod.map.id);
830 exports: defined[mod.map.id]
836 function cleanRegistry(id) {
837 //Clean up machinery used for waiting modules.
839 delete enabledRegistry[id];
842 function breakCycle(mod, traced, processed) {
846 mod.emit('error', mod.error);
849 each(mod.depMaps, function (depMap, i) {
850 var depId = depMap.id,
851 dep = getOwn(registry, depId);
853 //Only force things that have not completed
854 //being defined, so still in the registry,
855 //and only if it has not been matched up
856 //in the module already.
857 if (dep && !mod.depMatched[i] && !processed[depId]) {
858 if (getOwn(traced, depId)) {
859 mod.defineDep(i, defined[depId]);
860 mod.check(); //pass false?
862 breakCycle(dep, traced, processed);
866 processed[id] = true;
870 function checkLoaded() {
871 var map, modId, err, usingPathFallback,
872 waitInterval = config.waitSeconds * 1000,
873 //It is possible to disable the wait interval by using waitSeconds of 0.
874 expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
877 stillLoading = false,
878 needCycleCheck = true;
880 //Do not bother if this call was a result of a cycle break.
885 inCheckLoaded = true;
887 //Figure out the state of all the modules.
888 eachProp(enabledRegistry, function (mod) {
892 //Skip things that are not enabled or in error state.
902 //If the module should be executed, and it has not
903 //been inited and time is up, remember it.
904 if (!mod.inited && expired) {
905 if (hasPathFallback(modId)) {
906 usingPathFallback = true;
912 } else if (!mod.inited && mod.fetched && map.isDefine) {
915 //No reason to keep looking for unfinished
916 //loading. If the only stillLoading is a
917 //plugin resource though, keep going,
918 //because it may be that a plugin resource
919 //is waiting on a non-plugin cycle.
920 return (needCycleCheck = false);
926 if (expired && noLoads.length) {
927 //If wait time expired, throw error of unloaded modules.
928 err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads);
929 err.contextName = context.contextName;
933 //Not expired, check for a cycle.
934 if (needCycleCheck) {
935 each(reqCalls, function (mod) {
936 breakCycle(mod, {}, {});
940 //If still waiting on loads, and the waiting load is something
941 //other than a plugin resource, or there are still outstanding
942 //scripts, then just try back later.
943 if ((!expired || usingPathFallback) && stillLoading) {
944 //Something is still waiting to load. Wait for it, but only
945 //if a timeout is not already in effect.
946 if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {
947 checkLoadedTimeoutId = setTimeout(function () {
948 checkLoadedTimeoutId = 0;
954 inCheckLoaded = false;
957 Module = function (map) {
958 this.events = getOwn(undefEvents, map.id) || {};
960 this.shim = getOwn(config.shim, map.id);
961 this.depExports = [];
963 this.depMatched = [];
964 this.pluginMaps = {};
967 /* this.exports this.factory
969 this.enabled, this.fetched
974 init: function (depMaps, factory, errback, options) {
975 options = options || {};
977 //Do not do more inits if already done. Can happen if there
978 //are multiple define calls for the same module. That is not
979 //a normal, common case, but it is also not unexpected.
984 this.factory = factory;
987 //Register for errors on this module.
988 this.on('error', errback);
989 } else if (this.events.error) {
990 //If no errback already, but there are error listeners
991 //on this module, set up an errback to pass to the deps.
992 errback = bind(this, function (err) {
993 this.emit('error', err);
997 //Do a copy of the dependency array, so that
998 //source inputs are not modified. For example
999 //"shim" deps are passed in here directly, and
1000 //doing a direct modification of the depMaps array
1001 //would affect that config.
1002 this.depMaps = depMaps && depMaps.slice(0);
1004 this.errback = errback;
1006 //Indicate this module has be initialized
1009 this.ignore = options.ignore;
1011 //Could have option to init this module in enabled mode,
1012 //or could have been previously marked as enabled. However,
1013 //the dependencies are not known until init is called. So
1014 //if enabled previously, now trigger dependencies as enabled.
1015 if (options.enabled || this.enabled) {
1016 //Enable this module and dependencies.
1017 //Will call this.check()
1024 defineDep: function (i, depExports) {
1025 //Because of cycles, defined callback for a given
1026 //export can be called more than once.
1027 if (!this.depMatched[i]) {
1028 this.depMatched[i] = true;
1030 this.depExports[i] = depExports;
1034 fetch: function () {
1038 this.fetched = true;
1040 context.startTime = (new Date()).getTime();
1044 //If the manager is for a plugin managed resource,
1045 //ask the plugin to load it now.
1047 context.makeRequire(this.map, {
1048 enableBuildCallback: true
1049 })(this.shim.deps || [], bind(this, function () {
1050 return map.prefix ? this.callPlugin() : this.load();
1053 //Regular dependency.
1054 return map.prefix ? this.callPlugin() : this.load();
1059 var url = this.map.url;
1061 //Regular dependency.
1062 if (!urlFetched[url]) {
1063 urlFetched[url] = true;
1064 context.load(this.map.id, url);
1069 * Checks if the module is ready to define itself, and if so,
1072 check: function () {
1073 if (!this.enabled || this.enabling) {
1079 depExports = this.depExports,
1080 exports = this.exports,
1081 factory = this.factory;
1085 } else if (this.error) {
1086 this.emit('error', this.error);
1087 } else if (!this.defining) {
1088 //The factory could trigger another require call
1089 //that would result in checking this module to
1090 //define itself again. If already in the process
1091 //of doing that, skip this work.
1092 this.defining = true;
1094 if (this.depCount < 1 && !this.defined) {
1095 if (isFunction(factory)) {
1096 //If there is an error listener, favor passing
1097 //to that instead of throwing an error. However,
1098 //only do it for define()'d modules. require
1099 //errbacks should not be called for failures in
1100 //their callbacks (#699). However if a global
1101 //onError is set, use that.
1102 if ((this.events.error && this.map.isDefine) ||
1103 req.onError !== defaultOnError) {
1105 exports = context.execCb(id, factory, depExports, exports);
1110 exports = context.execCb(id, factory, depExports, exports);
1113 if (this.map.isDefine) {
1114 //If setting exports via 'module' is in play,
1115 //favor that over return value and exports. After that,
1116 //favor a non-undefined return value over exports use.
1117 cjsModule = this.module;
1119 cjsModule.exports !== undefined &&
1120 //Make sure it is not already the exports value
1121 cjsModule.exports !== this.exports) {
1122 exports = cjsModule.exports;
1123 } else if (exports === undefined && this.usingExports) {
1124 //exports already set the defined value.
1125 exports = this.exports;
1130 err.requireMap = this.map;
1131 err.requireModules = this.map.isDefine ? [this.map.id] : null;
1132 err.requireType = this.map.isDefine ? 'define' : 'require';
1133 return onError((this.error = err));
1137 //Just a literal value
1141 this.exports = exports;
1143 if (this.map.isDefine && !this.ignore) {
1144 defined[id] = exports;
1146 if (req.onResourceLoad) {
1147 req.onResourceLoad(context, this.map, this.depMaps);
1154 this.defined = true;
1157 //Finished the define stage. Allow calling check again
1158 //to allow define notifications below in the case of a
1160 this.defining = false;
1162 if (this.defined && !this.defineEmitted) {
1163 this.defineEmitted = true;
1164 this.emit('defined', this.exports);
1165 this.defineEmitComplete = true;
1171 callPlugin: function () {
1174 //Map already normalized the prefix.
1175 pluginMap = makeModuleMap(map.prefix);
1177 //Mark this as a dependency for this plugin, so it
1178 //can be traced for cycles.
1179 this.depMaps.push(pluginMap);
1181 on(pluginMap, 'defined', bind(this, function (plugin) {
1182 var load, normalizedMap, normalizedMod,
1183 name = this.map.name,
1184 parentName = this.map.parentMap ? this.map.parentMap.name : null,
1185 localRequire = context.makeRequire(map.parentMap, {
1186 enableBuildCallback: true
1189 //If current map is not normalized, wait for that
1190 //normalized name to load instead of continuing.
1191 if (this.map.unnormalized) {
1192 //Normalize the ID if the plugin allows it.
1193 if (plugin.normalize) {
1194 name = plugin.normalize(name, function (name) {
1195 return normalize(name, parentName, true);
1199 //prefix and name should already be normalized, no need
1200 //for applying map config again either.
1201 normalizedMap = makeModuleMap(map.prefix + '!' + name,
1202 this.map.parentMap);
1204 'defined', bind(this, function (value) {
1205 this.init([], function () { return value; }, null, {
1211 normalizedMod = getOwn(registry, normalizedMap.id);
1212 if (normalizedMod) {
1213 //Mark this as a dependency for this plugin, so it
1214 //can be traced for cycles.
1215 this.depMaps.push(normalizedMap);
1217 if (this.events.error) {
1218 normalizedMod.on('error', bind(this, function (err) {
1219 this.emit('error', err);
1222 normalizedMod.enable();
1228 load = bind(this, function (value) {
1229 this.init([], function () { return value; }, null, {
1234 load.error = bind(this, function (err) {
1237 err.requireModules = [id];
1239 //Remove temp unnormalized modules for this module,
1240 //since they will never be resolved otherwise now.
1241 eachProp(registry, function (mod) {
1242 if (mod.map.id.indexOf(id + '_unnormalized') === 0) {
1243 cleanRegistry(mod.map.id);
1250 //Allow plugins to load other code without having to know the
1251 //context or how to 'complete' the load.
1252 load.fromText = bind(this, function (text, textAlt) {
1253 /*jslint evil: true */
1254 var moduleName = map.name,
1255 moduleMap = makeModuleMap(moduleName),
1256 hasInteractive = useInteractive;
1258 //As of 2.1.0, support just passing the text, to reinforce
1259 //fromText only being called once per resource. Still
1260 //support old style of passing moduleName but discard
1261 //that moduleName in favor of the internal ref.
1266 //Turn off interactive script matching for IE for any define
1267 //calls in the text, then turn it back on at the end.
1268 if (hasInteractive) {
1269 useInteractive = false;
1272 //Prime the system by creating a module instance for
1274 getModule(moduleMap);
1276 //Transfer any config to this other module.
1277 if (hasProp(config.config, id)) {
1278 config.config[moduleName] = config.config[id];
1284 return onError(makeError('fromtexteval',
1285 'fromText eval for ' + id +
1291 if (hasInteractive) {
1292 useInteractive = true;
1295 //Mark this as a dependency for the plugin
1297 this.depMaps.push(moduleMap);
1299 //Support anonymous modules.
1300 context.completeLoad(moduleName);
1302 //Bind the value of that module to the value for this
1304 localRequire([moduleName], load);
1307 //Use parentName here since the plugin's name is not reliable,
1308 //could be some weird string with no path that actually wants to
1309 //reference the parentName's path.
1310 plugin.load(map.name, localRequire, load, config);
1313 context.enable(pluginMap, this);
1314 this.pluginMaps[pluginMap.id] = pluginMap;
1317 enable: function () {
1318 enabledRegistry[this.map.id] = this;
1319 this.enabled = true;
1321 //Set flag mentioning that the module is enabling,
1322 //so that immediate calls to the defined callbacks
1323 //for dependencies do not trigger inadvertent load
1324 //with the depCount still being zero.
1325 this.enabling = true;
1327 //Enable each dependency
1328 each(this.depMaps, bind(this, function (depMap, i) {
1329 var id, mod, handler;
1331 if (typeof depMap === 'string') {
1332 //Dependency needs to be converted to a depMap
1333 //and wired up to this module.
1334 depMap = makeModuleMap(depMap,
1335 (this.map.isDefine ? this.map : this.map.parentMap),
1338 this.depMaps[i] = depMap;
1340 handler = getOwn(handlers, depMap.id);
1343 this.depExports[i] = handler(this);
1349 on(depMap, 'defined', bind(this, function (depExports) {
1350 this.defineDep(i, depExports);
1355 on(depMap, 'error', bind(this, this.errback));
1362 //Skip special modules like 'require', 'exports', 'module'
1363 //Also, don't call enable if it is already enabled,
1364 //important in circular dependency cases.
1365 if (!hasProp(handlers, id) && mod && !mod.enabled) {
1366 context.enable(depMap, this);
1370 //Enable each plugin that is used in
1372 eachProp(this.pluginMaps, bind(this, function (pluginMap) {
1373 var mod = getOwn(registry, pluginMap.id);
1374 if (mod && !mod.enabled) {
1375 context.enable(pluginMap, this);
1379 this.enabling = false;
1384 on: function (name, cb) {
1385 var cbs = this.events[name];
1387 cbs = this.events[name] = [];
1392 emit: function (name, evt) {
1393 each(this.events[name], function (cb) {
1396 if (name === 'error') {
1397 //Now that the error handler was triggered, remove
1398 //the listeners, since this broken Module instance
1399 //can stay around for a while in the registry.
1400 delete this.events[name];
1405 function callGetModule(args) {
1406 //Skip modules already defined.
1407 if (!hasProp(defined, args[0])) {
1408 getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]);
1412 function removeListener(node, func, name, ieName) {
1413 //Favor detachEvent because of IE9
1414 //issue, see attachEvent/addEventListener comment elsewhere
1416 if (node.detachEvent && !isOpera) {
1417 //Probably IE. If not it will throw an error, which will be
1420 node.detachEvent(ieName, func);
1423 node.removeEventListener(name, func, false);
1428 * Given an event from a script node, get the requirejs info from it,
1429 * and then removes the event listeners on the node.
1430 * @param {Event} evt
1433 function getScriptData(evt) {
1434 //Using currentTarget instead of target for Firefox 2.0's sake. Not
1435 //all old browsers will be supported, but this one was easy enough
1436 //to support and still makes sense.
1437 var node = evt.currentTarget || evt.srcElement;
1439 //Remove the listeners once here.
1440 removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange');
1441 removeListener(node, context.onScriptError, 'error');
1445 id: node && node.getAttribute('data-requiremodule')
1449 function intakeDefines() {
1452 //Any defined modules in the global queue, intake them now.
1455 //Make sure any remaining defQueue items get properly processed.
1456 while (defQueue.length) {
1457 args = defQueue.shift();
1458 if (args[0] === null) {
1459 return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1]));
1461 //args are id, deps, factory. Should be normalized by the
1462 //define() function.
1463 callGetModule(args);
1470 contextName: contextName,
1473 urlFetched: urlFetched,
1476 makeModuleMap: makeModuleMap,
1477 nextTick: req.nextTick,
1481 * Set a configuration for the context.
1482 * @param {Object} cfg config object to integrate.
1484 configure: function (cfg) {
1485 //Make sure the baseUrl ends in a slash.
1487 if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') {
1492 //Save off the paths and packages since they require special processing,
1493 //they are additive.
1494 var pkgs = config.pkgs,
1502 eachProp(cfg, function (value, prop) {
1504 if (prop === 'map') {
1508 mixin(config[prop], value, true, true);
1510 mixin(config[prop], value, true);
1513 config[prop] = value;
1519 eachProp(cfg.shim, function (value, id) {
1520 //Normalize the structure
1521 if (isArray(value)) {
1526 if ((value.exports || value.init) && !value.exportsFn) {
1527 value.exportsFn = context.makeShimExports(value);
1534 //Adjust packages if necessary.
1536 each(cfg.packages, function (pkgObj) {
1539 pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
1540 location = pkgObj.location;
1542 //Create a brand new object on pkgs, since currentPackages can
1543 //be passed in again, and config.pkgs is the internal transformed
1544 //state for all package configs.
1545 pkgs[pkgObj.name] = {
1547 location: location || pkgObj.name,
1548 //Remove leading dot in main, so main paths are normalized,
1549 //and remove any trailing .js, since different package
1550 //envs have different conventions: some use a module name,
1551 //some use a file name.
1552 main: (pkgObj.main || 'main')
1553 .replace(currDirRegExp, '')
1554 .replace(jsSuffixRegExp, '')
1558 //Done with modifications, assing packages back to context config
1562 //If there are any "waiting to execute" modules in the registry,
1563 //update the maps for them, since their info, like URLs to load,
1565 eachProp(registry, function (mod, id) {
1566 //If module already has init called, since it is too
1567 //late to modify them, and ignore unnormalized ones
1568 //since they are transient.
1569 if (!mod.inited && !mod.map.unnormalized) {
1570 mod.map = makeModuleMap(id);
1574 //If a deps array or a config callback is specified, then call
1575 //require with those args. This is useful when require is defined as a
1576 //config object before require.js is loaded.
1577 if (cfg.deps || cfg.callback) {
1578 context.require(cfg.deps || [], cfg.callback);
1582 makeShimExports: function (value) {
1586 ret = value.init.apply(global, arguments);
1588 return ret || (value.exports && getGlobal(value.exports));
1593 makeRequire: function (relMap, options) {
1594 options = options || {};
1596 function localRequire(deps, callback, errback) {
1597 var id, map, requireMod;
1599 if (options.enableBuildCallback && callback && isFunction(callback)) {
1600 callback.__requireJsBuild = true;
1603 if (typeof deps === 'string') {
1604 if (isFunction(callback)) {
1606 return onError(makeError('requireargs', 'Invalid require call'), errback);
1609 //If require|exports|module are requested, get the
1610 //value for them from the special handlers. Caveat:
1611 //this only works while module is being defined.
1612 if (relMap && hasProp(handlers, deps)) {
1613 return handlers[deps](registry[relMap.id]);
1616 //Synchronous access to one module. If require.get is
1617 //available (as in the Node adapter), prefer that.
1619 return req.get(context, deps, relMap, localRequire);
1622 //Normalize module name, if it contains . or ..
1623 map = makeModuleMap(deps, relMap, false, true);
1626 if (!hasProp(defined, id)) {
1627 return onError(makeError('notloaded', 'Module name "' +
1629 '" has not been loaded yet for context: ' +
1631 (relMap ? '' : '. Use require([])')));
1636 //Grab defines waiting in the global queue.
1639 //Mark all the dependencies as needing to be loaded.
1640 context.nextTick(function () {
1641 //Some defines could have been added since the
1642 //require call, collect them.
1645 requireMod = getModule(makeModuleMap(null, relMap));
1647 //Store if map config should be applied to this require
1648 //call for dependencies.
1649 requireMod.skipMap = options.skipMap;
1651 requireMod.init(deps, callback, errback, {
1658 return localRequire;
1661 mixin(localRequire, {
1662 isBrowser: isBrowser,
1665 * Converts a module name + .extension into an URL path.
1666 * *Requires* the use of a module name. It does not support using
1667 * plain URLs like nameToUrl.
1669 toUrl: function (moduleNamePlusExt) {
1671 index = moduleNamePlusExt.lastIndexOf('.'),
1672 segment = moduleNamePlusExt.split('/')[0],
1673 isRelative = segment === '.' || segment === '..';
1675 //Have a file extension alias, and it is not the
1676 //dots from a relative path.
1677 if (index !== -1 && (!isRelative || index > 1)) {
1678 ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
1679 moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
1682 return context.nameToUrl(normalize(moduleNamePlusExt,
1683 relMap && relMap.id, true), ext, true);
1686 defined: function (id) {
1687 return hasProp(defined, makeModuleMap(id, relMap, false, true).id);
1690 specified: function (id) {
1691 id = makeModuleMap(id, relMap, false, true).id;
1692 return hasProp(defined, id) || hasProp(registry, id);
1696 //Only allow undef on top level require calls
1698 localRequire.undef = function (id) {
1699 //Bind any waiting define() calls to this context,
1703 var map = makeModuleMap(id, relMap, true),
1704 mod = getOwn(registry, id);
1707 delete urlFetched[map.url];
1708 delete undefEvents[id];
1711 //Hold on to listeners in case the
1712 //module will be attempted to be reloaded
1713 //using a different config.
1714 if (mod.events.defined) {
1715 undefEvents[id] = mod.events;
1723 return localRequire;
1727 * Called to enable a module if it is still in the registry
1728 * awaiting enablement. A second arg, parent, the parent module,
1729 * is passed in for context, when this method is overriden by
1730 * the optimizer. Not shown here to keep code compact.
1732 enable: function (depMap) {
1733 var mod = getOwn(registry, depMap.id);
1735 getModule(depMap).enable();
1740 * Internal method used by environment adapters to complete a load event.
1741 * A load event could be a script load or just a load pass from a synchronous
1743 * @param {String} moduleName the name of the module to potentially complete.
1745 completeLoad: function (moduleName) {
1746 var found, args, mod,
1747 shim = getOwn(config.shim, moduleName) || {},
1748 shExports = shim.exports;
1752 while (defQueue.length) {
1753 args = defQueue.shift();
1754 if (args[0] === null) {
1755 args[0] = moduleName;
1756 //If already found an anonymous module and bound it
1757 //to this name, then this is some other anon module
1758 //waiting for its completeLoad to fire.
1763 } else if (args[0] === moduleName) {
1764 //Found matching define call for this script!
1768 callGetModule(args);
1771 //Do this after the cycle of callGetModule in case the result
1772 //of those calls/init calls changes the registry.
1773 mod = getOwn(registry, moduleName);
1775 if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) {
1776 if (config.enforceDefine && (!shExports || !getGlobal(shExports))) {
1777 if (hasPathFallback(moduleName)) {
1780 return onError(makeError('nodefine',
1781 'No define call for ' + moduleName,
1786 //A script that does not call define(), so just simulate
1788 callGetModule([moduleName, (shim.deps || []), shim.exportsFn]);
1796 * Converts a module name to a file path. Supports cases where
1797 * moduleName may actually be just an URL.
1798 * Note that it **does not** call normalize on the moduleName,
1799 * it is assumed to have already been normalized. This is an
1800 * internal API, not a public one. Use toUrl for the public API.
1802 nameToUrl: function (moduleName, ext, skipExt) {
1803 var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url,
1806 //If a colon is in the URL, it indicates a protocol is used and it is just
1807 //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?)
1808 //or ends with .js, then assume the user meant to use an url and not a module id.
1809 //The slash is important for protocol-less URLs as well as full paths.
1810 if (req.jsExtRegExp.test(moduleName)) {
1811 //Just a plain path, not module name lookup, so just return it.
1812 //Add extension if it is included. This is a bit wonky, only non-.js things pass
1813 //an extension, this method probably needs to be reworked.
1814 url = moduleName + (ext || '');
1816 //A module that needs to be converted to a path.
1817 paths = config.paths;
1820 syms = moduleName.split('/');
1821 //For each module name segment, see if there is a path
1822 //registered for it. Start with most specific name
1823 //and work up from it.
1824 for (i = syms.length; i > 0; i -= 1) {
1825 parentModule = syms.slice(0, i).join('/');
1826 pkg = getOwn(pkgs, parentModule);
1827 parentPath = getOwn(paths, parentModule);
1829 //If an array, it means there are a few choices,
1830 //Choose the one that is desired
1831 if (isArray(parentPath)) {
1832 parentPath = parentPath[0];
1834 syms.splice(0, i, parentPath);
1837 //If module name is just the package name, then looking
1838 //for the main module.
1839 if (moduleName === pkg.name) {
1840 pkgPath = pkg.location + '/' + pkg.main;
1842 pkgPath = pkg.location;
1844 syms.splice(0, i, pkgPath);
1849 //Join the path parts together, then figure out if baseUrl is needed.
1850 url = syms.join('/');
1851 url += (ext || (/\?/.test(url) || skipExt ? '' : '.js'));
1852 url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
1855 return config.urlArgs ? url +
1856 ((url.indexOf('?') === -1 ? '?' : '&') +
1857 config.urlArgs) : url;
1860 //Delegates to req.load. Broken out as a separate function to
1861 //allow overriding in the optimizer.
1862 load: function (id, url) {
1863 req.load(context, id, url);
1867 * Executes a module callback function. Broken out as a separate function
1868 * solely to allow the build system to sequence the files in the built
1869 * layer in the right sequence.
1873 execCb: function (name, callback, args, exports) {
1874 return callback.apply(exports, args);
1878 * callback for script loads, used to check status of loading.
1880 * @param {Event} evt the event from the browser for the script
1883 onScriptLoad: function (evt) {
1884 //Using currentTarget instead of target for Firefox 2.0's sake. Not
1885 //all old browsers will be supported, but this one was easy enough
1886 //to support and still makes sense.
1887 if (evt.type === 'load' ||
1888 (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) {
1889 //Reset interactive script so a script node is not held onto for
1891 interactiveScript = null;
1893 //Pull out the name of the module and the context.
1894 var data = getScriptData(evt);
1895 context.completeLoad(data.id);
1900 * Callback for script errors.
1902 onScriptError: function (evt) {
1903 var data = getScriptData(evt);
1904 if (!hasPathFallback(data.id)) {
1905 return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id]));
1910 context.require = context.makeRequire();
1917 * If the only argument to require is a string, then the module that
1918 * is represented by that string is fetched for the appropriate context.
1920 * If the first argument is an array, then it will be treated as an array
1921 * of dependency string names to fetch. An optional function callback can
1922 * be specified to execute when all of those dependencies are available.
1924 * Make a local req variable to help Caja compliance (it assumes things
1925 * on a require that are not standardized), and to give a short
1926 * name for minification/local scope use.
1928 req = requirejs = function (deps, callback, errback, optional) {
1930 //Find the right context, use default
1931 var context, config,
1932 contextName = defContextName;
1934 // Determine if have config object in the call.
1935 if (!isArray(deps) && typeof deps !== 'string') {
1936 // deps is a config object
1938 if (isArray(callback)) {
1939 // Adjust args if there are dependencies
1948 if (config && config.context) {
1949 contextName = config.context;
1952 context = getOwn(contexts, contextName);
1954 context = contexts[contextName] = req.s.newContext(contextName);
1958 context.configure(config);
1961 return context.require(deps, callback, errback);
1965 * Support require.config() to make it easier to cooperate with other
1966 * AMD loaders on globally agreed names.
1968 req.config = function (config) {
1973 * Execute something after the current tick
1974 * of the event loop. Override for other envs
1975 * that have a better solution than setTimeout.
1976 * @param {Function} fn function to execute later.
1978 req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) {
1980 } : function (fn) { fn(); };
1983 * Export require as a global, but only if it does not already exist.
1989 req.version = version;
1991 //Used to filter out dependencies that are already paths.
1992 req.jsExtRegExp = /^\/|:|\?|\.js$/;
1993 req.isBrowser = isBrowser;
1996 newContext: newContext
1999 //Create default context.
2002 //Exports some context-sensitive methods on global require.
2008 ], function (prop) {
2009 //Reference from contexts instead of early binding to default context,
2010 //so that during builds, the latest instance of the default context
2011 //with its config gets used.
2012 req[prop] = function () {
2013 var ctx = contexts[defContextName];
2014 return ctx.require[prop].apply(ctx, arguments);
2019 head = s.head = document.getElementsByTagName('head')[0];
2020 //If BASE tag is in play, using appendChild is a problem for IE6.
2021 //When that browser dies, this can be removed. Details in this jQuery bug:
2022 //http://dev.jquery.com/ticket/2709
2023 baseElement = document.getElementsByTagName('base')[0];
2025 head = s.head = baseElement.parentNode;
2030 * Any errors that require explicitly generates will be passed to this
2031 * function. Intercept/override it if you want custom error handling.
2032 * @param {Error} err the error object.
2034 req.onError = defaultOnError;
2037 * Creates the node for the load command. Only used in browser envs.
2039 req.createNode = function (config, moduleName, url) {
2040 var node = config.xhtml ?
2041 document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
2042 document.createElement('script');
2043 node.type = config.scriptType || 'text/javascript';
2044 node.charset = 'utf-8';
2050 * Does the request to load a module for the browser case.
2051 * Make this a separate function to allow other environments
2054 * @param {Object} context the require context to find state.
2055 * @param {String} moduleName the name of the module.
2056 * @param {Object} url the URL to the module.
2058 req.load = function (context, moduleName, url) {
2059 var config = (context && context.config) || {},
2062 //In the browser so use a script tag
2063 node = req.createNode(config, moduleName, url);
2065 node.setAttribute('data-requirecontext', context.contextName);
2066 node.setAttribute('data-requiremodule', moduleName);
2068 //Set up load listener. Test attachEvent first because IE9 has
2069 //a subtle issue in its addEventListener and script onload firings
2070 //that do not match the behavior of all other browsers with
2071 //addEventListener support, which fire the onload event for a
2072 //script right after the script execution. See:
2073 //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution
2074 //UNFORTUNATELY Opera implements attachEvent but does not follow the script
2075 //script execution mode.
2076 if (node.attachEvent &&
2077 //Check if node.attachEvent is artificially added by custom script or
2078 //natively supported by browser
2079 //read https://github.com/jrburke/requirejs/issues/187
2080 //if we can NOT find [native code] then it must NOT natively supported.
2081 //in IE8, node.attachEvent does not have toString()
2082 //Note the test for "[native code" with no closing brace, see:
2083 //https://github.com/jrburke/requirejs/issues/273
2084 !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) &&
2086 //Probably IE. IE (at least 6-8) do not fire
2087 //script onload right after executing the script, so
2088 //we cannot tie the anonymous define call to a name.
2089 //However, IE reports the script as being in 'interactive'
2090 //readyState at the time of the define call.
2091 useInteractive = true;
2093 node.attachEvent('onreadystatechange', context.onScriptLoad);
2094 //It would be great to add an error handler here to catch
2095 //404s in IE9+. However, onreadystatechange will fire before
2096 //the error handler, so that does not help. If addEventListener
2097 //is used, then IE will fire error before load, but we cannot
2098 //use that pathway given the connect.microsoft.com issue
2099 //mentioned above about not doing the 'script execute,
2100 //then fire the script load event listener before execute
2101 //next script' that other browsers do.
2102 //Best hope: IE10 fixes the issues,
2103 //and then destroys all installs of IE 6-9.
2104 //node.attachEvent('onerror', context.onScriptError);
2106 node.addEventListener('load', context.onScriptLoad, false);
2107 node.addEventListener('error', context.onScriptError, false);
2111 //For some cache cases in IE 6-8, the script executes before the end
2112 //of the appendChild execution, so to tie an anonymous define
2113 //call to the module name (which is stored on the node), hold on
2114 //to a reference to this node, but clear after the DOM insertion.
2115 currentlyAddingScript = node;
2117 head.insertBefore(node, baseElement);
2119 head.appendChild(node);
2121 currentlyAddingScript = null;
2124 } else if (isWebWorker) {
2126 //In a web worker, use importScripts. This is not a very
2127 //efficient use of importScripts, importScripts will block until
2128 //its script is downloaded and evaluated. However, if web workers
2129 //are in play, the expectation that a build has been done so that
2130 //only one script needs to be loaded anyway. This may need to be
2131 //reevaluated if other use cases become common.
2134 //Account for anonymous modules
2135 context.completeLoad(moduleName);
2137 context.onError(makeError('importscripts',
2138 'importScripts failed for ' +
2139 moduleName + ' at ' + url,
2146 function getInteractiveScript() {
2147 if (interactiveScript && interactiveScript.readyState === 'interactive') {
2148 return interactiveScript;
2151 eachReverse(scripts(), function (script) {
2152 if (script.readyState === 'interactive') {
2153 return (interactiveScript = script);
2156 return interactiveScript;
2159 //Look for a data-main script attribute, which could also adjust the baseUrl.
2161 //Figure out baseUrl. Get it from the script tag with require.js in it.
2162 eachReverse(scripts(), function (script) {
2163 //Set the 'head' where we can append children by
2164 //using the script's parent.
2166 head = script.parentNode;
2169 //Look for a data-main attribute to set main script for the page
2170 //to load. If it is there, the path to data main becomes the
2171 //baseUrl, if it is not already set.
2172 dataMain = script.getAttribute('data-main');
2174 //Preserve dataMain in case it is a path (i.e. contains '?')
2175 mainScript = dataMain;
2177 //Set final baseUrl if there is not already an explicit one.
2179 //Pull off the directory of data-main for use as the
2181 src = mainScript.split('/');
2182 mainScript = src.pop();
2183 subPath = src.length ? src.join('/') + '/' : './';
2185 cfg.baseUrl = subPath;
2188 //Strip off any trailing .js since mainScript is now
2189 //like a module name.
2190 mainScript = mainScript.replace(jsSuffixRegExp, '');
2192 //If mainScript is still a path, fall back to dataMain
2193 if (req.jsExtRegExp.test(mainScript)) {
2194 mainScript = dataMain;
2197 //Put the data-main script in the files to load.
2198 cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];
2206 * The function that handles definitions of modules. Differs from
2207 * require() in that a string for the module should be the first argument,
2208 * and the function to execute after dependencies are loaded should
2209 * return a value to define the module corresponding to the first argument's
2212 define = function (name, deps, callback) {
2215 //Allow for anonymous modules
2216 if (typeof name !== 'string') {
2217 //Adjust args appropriately
2223 //This module may not have dependencies
2224 if (!isArray(deps)) {
2229 //If no name, and callback is a function, then figure out if it a
2230 //CommonJS thing with dependencies.
2231 if (!deps && isFunction(callback)) {
2233 //Remove comments from the callback string,
2234 //look for require calls, and pull them into the dependencies,
2235 //but only if there are function args.
2236 if (callback.length) {
2239 .replace(commentRegExp, '')
2240 .replace(cjsRequireRegExp, function (match, dep) {
2244 //May be a CommonJS thing even without require calls, but still
2245 //could use exports, and module. Avoid doing exports and module
2246 //work though if it just needs require.
2247 //REQUIRES the function to expect the CommonJS variables in the
2248 //order listed below.
2249 deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps);
2253 //If in IE 6-8 and hit an anonymous define() call, do the interactive
2255 if (useInteractive) {
2256 node = currentlyAddingScript || getInteractiveScript();
2259 name = node.getAttribute('data-requiremodule');
2261 context = contexts[node.getAttribute('data-requirecontext')];
2265 //Always save off evaluating the def call until the script onload handler.
2266 //This allows multiple modules to be in a file without prematurely
2267 //tracing dependencies, and allows for anonymous module support,
2268 //where the module name is not known until the script onload event
2269 //occurs. If no context, use the global queue, and get it processed
2270 //in the onscript load callback.
2271 (context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
2280 * Executes the text. Normally just uses eval, but can be modified
2281 * to use a better, environment-specific call. Only used for transpiling
2282 * loader plugins, not for plain JS modules.
2283 * @param {String} text the text to execute/evaluate.
2285 req.exec = function (text) {
2286 /*jslint evil: true */
2290 //Set up with config info.
2296 this.requirejsVars = {
2302 if (env === 'browser') {
2304 * @license RequireJS rhino Copyright (c) 2012, The Dojo Foundation All Rights Reserved.
2305 * Available via the MIT or new BSD license.
2306 * see: http://github.com/jrburke/requirejs for details
2309 //sloppy since eval enclosed with use strict causes problems if the source
2310 //text is not strict-compliant.
2311 /*jslint sloppy: true, evil: true */
2312 /*global require, XMLHttpRequest */
2315 require.load = function (context, moduleName, url) {
2316 var xhr = new XMLHttpRequest();
2318 xhr.open('GET', url, true);
2321 xhr.onreadystatechange = function () {
2322 if (xhr.readyState === 4) {
2323 eval(xhr.responseText);
2325 //Support anonymous modules.
2326 context.completeLoad(moduleName);
2331 } else if (env === 'rhino') {
2333 * @license RequireJS rhino Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
2334 * Available via the MIT or new BSD license.
2335 * see: http://github.com/jrburke/requirejs for details
2339 /*global require: false, java: false, load: false */
2343 require.load = function (context, moduleName, url) {
2347 //Support anonymous modules.
2348 context.completeLoad(moduleName);
2352 } else if (env === 'node') {
2353 this.requirejsVars.nodeRequire = nodeRequire;
2354 require.nodeRequire = nodeRequire;
2357 * @license RequireJS node Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
2358 * Available via the MIT or new BSD license.
2359 * see: http://github.com/jrburke/requirejs for details
2362 /*jslint regexp: false */
2363 /*global require: false, define: false, requirejsVars: false, process: false */
2366 * This adapter assumes that x.js has loaded it and set up
2367 * some variables. This adapter just allows limited RequireJS
2368 * usage from within the requirejs directory. The general
2369 * node adapater is r.js.
2375 var nodeReq = requirejsVars.nodeRequire,
2376 req = requirejsVars.require,
2377 def = requirejsVars.define,
2379 path = nodeReq('path'),
2381 //In Node 0.7+ existsSync is on fs.
2382 exists = fs.existsSync || path.existsSync,
2383 hasOwn = Object.prototype.hasOwnProperty;
2385 function hasProp(obj, prop) {
2386 return hasOwn.call(obj, prop);
2389 function syncTick(fn) {
2393 //Supply an implementation that allows synchronous get of a module.
2394 req.get = function (context, moduleName, relModuleMap, localRequire) {
2395 if (moduleName === "require" || moduleName === "exports" || moduleName === "module") {
2396 req.onError(new Error("Explicit require of " + moduleName + " is not allowed."));
2400 moduleMap = context.makeModuleMap(moduleName, relModuleMap, false, true);
2402 //Normalize module name, if it contains . or ..
2403 moduleName = moduleMap.id;
2405 if (hasProp(context.defined, moduleName)) {
2406 ret = context.defined[moduleName];
2408 if (ret === undefined) {
2409 //Make sure nextTick for this type of call is sync-based.
2410 oldTick = context.nextTick;
2411 context.nextTick = syncTick;
2413 if (moduleMap.prefix) {
2414 //A plugin, call requirejs to handle it. Now that
2415 //nextTick is syncTick, the require will complete
2417 localRequire([moduleMap.originalName]);
2419 //Now that plugin is loaded, can regenerate the moduleMap
2420 //to get the final, normalized ID.
2421 moduleMap = context.makeModuleMap(moduleMap.originalName, relModuleMap, false, true);
2422 moduleName = moduleMap.id;
2424 //Try to dynamically fetch it.
2425 req.load(context, moduleName, moduleMap.url);
2428 context.enable(moduleMap, relModuleMap);
2431 //Break any cycles by requiring it normally, but this will
2432 //finish synchronously
2433 require([moduleName]);
2435 //The above calls are sync, so can do the next thing safely.
2436 ret = context.defined[moduleName];
2438 context.nextTick = oldTick;
2446 req.nextTick = function (fn) {
2447 process.nextTick(fn);
2450 //Add wrapper around the code so that it gets the requirejs
2451 //API instead of the Node API, and it is done lexically so
2452 //that it survives later execution.
2453 req.makeNodeWrapper = function (contents) {
2454 return '(function (require, requirejs, define) { ' +
2456 '\n}(requirejsVars.require, requirejsVars.requirejs, requirejsVars.define));';
2459 req.load = function (context, moduleName, url) {
2461 config = context.config;
2463 if (config.shim[moduleName] && (!config.suppress || !config.suppress.nodeShim)) {
2464 console.warn('Shim config not supported in Node, may or may not work. Detected ' +
2465 'for module: ' + moduleName);
2469 contents = fs.readFileSync(url, 'utf8');
2471 contents = req.makeNodeWrapper(contents);
2473 vm.runInThisContext(contents, fs.realpathSync(url));
2475 err = new Error('Evaluating ' + url + ' as module "' +
2476 moduleName + '" failed with error: ' + e);
2477 err.originalError = e;
2478 err.moduleName = moduleName;
2480 return req.onError(err);
2483 def(moduleName, function () {
2484 //Get the original name, since relative requires may be
2485 //resolved differently in node (issue #202). Also, if relative,
2486 //make it relative to the URL of the item requesting it
2489 map = hasProp(context.registry, moduleName) &&
2490 context.registry[moduleName].map,
2491 parentMap = map && map.parentMap,
2492 originalName = map && map.originalName;
2494 if (originalName.charAt(0) === '.' && parentMap) {
2495 dirName = parentMap.url.split('/');
2497 originalName = dirName.join('/') + '/' + originalName;
2501 return (context.config.nodeRequire || req.nodeRequire)(originalName);
2503 err = new Error('Tried loading "' + moduleName + '" at ' +
2504 url + ' then tried node\'s require("' +
2505 originalName + '") and it failed ' +
2506 'with error: ' + e);
2507 err.originalError = e;
2508 err.moduleName = originalName;
2509 return req.onError(err);
2514 //Support anonymous modules.
2515 context.completeLoad(moduleName);
2518 //Override to provide the function wrapper for define/require.
2519 req.exec = function (text) {
2520 /*jslint evil: true */
2521 text = req.makeNodeWrapper(text);
2526 } else if (env === 'xpconnect') {
2528 * @license RequireJS xpconnect Copyright (c) 2013, The Dojo Foundation All Rights Reserved.
2529 * Available via the MIT or new BSD license.
2530 * see: http://github.com/jrburke/requirejs for details
2534 /*global require, load */
2538 require.load = function (context, moduleName, url) {
2542 //Support anonymous modules.
2543 context.completeLoad(moduleName);
2550 //Support a default file name to execute. Useful for hosted envs
2551 //like Joyent where it defaults to a server.js as the only executed
2552 //script. But only do it if this is not an optimization run.
2553 if (commandOption !== 'o' && (!fileName || !jsSuffixRegExp.test(fileName))) {
2554 fileName = 'main.js';
2558 * Loads the library files that can be used for the optimizer, or for other
2561 function loadLib() {
2563 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
2564 * Available via the MIT or new BSD license.
2565 * see: http://github.com/jrburke/requirejs for details
2568 /*jslint strict: false */
2569 /*global Packages: false, process: false, window: false, navigator: false,
2570 document: false, define: false */
2573 * A plugin that modifies any /env/ path to be the right path based on
2574 * the host environment. Right now only works for Node, Rhino and browser.
2577 var pathRegExp = /(\/|^)env\/|\{env\}/,
2580 if (typeof Packages !== 'undefined') {
2582 } else if (typeof process !== 'undefined' && process.versions && !!process.versions.node) {
2584 } else if ((typeof navigator !== 'undefined' && typeof document !== 'undefined') ||
2585 (typeof importScripts !== 'undefined' && typeof self !== 'undefined')) {
2587 } else if (typeof Components !== 'undefined' && Components.classes && Components.interfaces) {
2596 load: function (name, req, load, config) {
2597 //Allow override in the config.
2602 name = name.replace(pathRegExp, function (match, prefix) {
2603 if (match.indexOf('{') === -1) {
2604 return prefix + env + '/';
2610 req([name], function (mod) {
2616 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
2617 * Available via the MIT or new BSD license.
2618 * see: http://github.com/jrburke/requirejs for details
2621 /*jslint plusplus: true */
2624 define('lang', function () {
2628 hasOwn = Object.prototype.hasOwnProperty;
2630 function hasProp(obj, prop) {
2631 return hasOwn.call(obj, prop);
2635 backSlashRegExp: /\\/g,
2636 ostring: Object.prototype.toString,
2638 isArray: Array.isArray || function (it) {
2639 return lang.ostring.call(it) === "[object Array]";
2642 isFunction: function(it) {
2643 return lang.ostring.call(it) === "[object Function]";
2646 isRegExp: function(it) {
2647 return it && it instanceof RegExp;
2652 //returns true if the object does not have an own property prop,
2653 //or if it does, it is a falsy value.
2654 falseProp: function (obj, prop) {
2655 return !hasProp(obj, prop) || !obj[prop];
2658 //gets own property value for given prop on object
2659 getOwn: function (obj, prop) {
2660 return hasProp(obj, prop) && obj[prop];
2663 _mixin: function(dest, source, override){
2665 for (name in source) {
2666 if(source.hasOwnProperty(name) &&
2667 (override || !dest.hasOwnProperty(name))) {
2668 dest[name] = source[name];
2672 return dest; // Object
2676 * mixin({}, obj1, obj2) is allowed. If the last argument is a boolean,
2677 * then the source objects properties are force copied over to dest.
2679 mixin: function(dest){
2680 var parameters = Array.prototype.slice.call(arguments),
2683 if (!dest) { dest = {}; }
2685 if (parameters.length > 2 && typeof arguments[parameters.length-1] === 'boolean') {
2686 override = parameters.pop();
2689 for (i = 1, l = parameters.length; i < l; i++) {
2690 lang._mixin(dest, parameters[i], override);
2692 return dest; // Object
2697 * Does a type of deep copy. Do not give it anything fancy, best
2698 * for basic object copies of objects that also work well as
2699 * JSON-serialized things, or has properties pointing to functions.
2700 * For non-array/object values, just returns the same object.
2701 * @param {Object} obj copy properties from this object
2702 * @param {Object} [result] optional result object to use
2705 deeplikeCopy: function (obj) {
2708 if (lang.isArray(obj)) {
2710 obj.forEach(function(value) {
2711 result.push(lang.deeplikeCopy(value));
2717 if (obj === null || obj === undefined || type === 'boolean' ||
2718 type === 'string' || type === 'number' || lang.isFunction(obj) ||
2719 lang.isRegExp(obj)) {
2723 //Anything else is an object, hopefully.
2725 lang.eachProp(obj, function(value, key) {
2726 result[key] = lang.deeplikeCopy(value);
2731 delegate: (function () {
2732 // boodman/crockford delegation w/ cornford optimization
2734 return function (obj, props) {
2735 TMP.prototype = obj;
2736 var tmp = new TMP();
2737 TMP.prototype = null;
2739 lang.mixin(tmp, props);
2741 return tmp; // Object
2746 * Helper function for iterating over an array. If the func returns
2747 * a true value, it will break out of the loop.
2749 each: function each(ary, func) {
2752 for (i = 0; i < ary.length; i += 1) {
2753 if (func(ary[i], i, ary)) {
2761 * Cycles over properties in an object and calls a function for each
2762 * property value. If the function returns a truthy value, then the
2763 * iteration is stopped.
2765 eachProp: function eachProp(obj, func) {
2768 if (hasProp(obj, prop)) {
2769 if (func(obj[prop], prop)) {
2776 //Similar to Function.prototype.bind, but the "this" object is specified
2777 //first, since it is easier to read/figure out what "this" will be.
2778 bind: function bind(obj, fn) {
2779 return function () {
2780 return fn.apply(obj, arguments);
2784 //Escapes a content string to be be a string that has characters escaped
2785 //for inclusion as part of a JS string.
2786 jsEscape: function (content) {
2787 return content.replace(/(["'\\])/g, '\\$1')
2788 .replace(/[\f]/g, "\\f")
2789 .replace(/[\b]/g, "\\b")
2790 .replace(/[\n]/g, "\\n")
2791 .replace(/[\t]/g, "\\t")
2792 .replace(/[\r]/g, "\\r");
2798 * prim 0.0.1 Copyright (c) 2012-2013, The Dojo Foundation All Rights Reserved.
2799 * Available via the MIT or new BSD license.
2800 * see: http://github.com/requirejs/prim for details
2803 /*global setImmediate, process, setTimeout, define, module */
2805 //Set prime.hideResolutionConflict = true to allow "resolution-races"
2806 //in promise-tests to pass.
2807 //Since the goal of prim is to be a small impl for trusted code, it is
2808 //more important to normally throw in this case so that we can find
2809 //logic errors quicker.
2814 var op = Object.prototype,
2815 hasOwn = op.hasOwnProperty;
2817 function hasProp(obj, prop) {
2818 return hasOwn.call(obj, prop);
2822 * Helper function for iterating over an array. If the func returns
2823 * a true value, it will break out of the loop.
2825 function each(ary, func) {
2828 for (i = 0; i < ary.length; i += 1) {
2830 func(ary[i], i, ary);
2837 if (hasProp(p, 'e') || hasProp(p, 'v')) {
2838 if (!prim.hideResolutionConflict) {
2839 throw new Error('nope');
2846 function notify(ary, value) {
2847 prim.nextTick(function () {
2848 each(ary, function (item) {
2854 prim = function prim() {
2860 callback: function (yes, no) {
2865 if (hasProp(p, 'v')) {
2866 prim.nextTick(function () {
2874 errback: function (no) {
2875 if (hasProp(p, 'e')) {
2876 prim.nextTick(function () {
2884 finished: function () {
2885 return hasProp(p, 'e') || hasProp(p, 'v');
2888 rejected: function () {
2889 return hasProp(p, 'e');
2892 resolve: function (v) {
2899 reject: function (e) {
2907 start: function (fn) {
2909 return p.promise.then(fn);
2913 then: function (yes, no) {
2916 p.callback(function (v) {
2918 if (yes && typeof yes === 'function') {
2923 v.then(next.resolve, next.reject);
2934 if (!no || typeof no !== 'function') {
2939 if (err && err.then) {
2940 err.then(next.resolve, next.reject);
2950 return next.promise;
2953 fail: function (no) {
2954 return p.promise.then(null, no);
2958 p.errback(function (e) {
2966 prim.serial = function (ary) {
2967 var result = prim().resolve().promise;
2968 each(ary, function (item) {
2969 result = result.then(function () {
2976 prim.nextTick = typeof setImmediate === 'function' ? setImmediate :
2977 (typeof process !== 'undefined' && process.nextTick ?
2978 process.nextTick : (typeof setTimeout !== 'undefined' ?
2985 if (typeof define === 'function' && define.amd) {
2986 define('prim', function () { return prim; });
2987 } else if (typeof module !== 'undefined' && module.exports) {
2988 module.exports = prim;
2991 if(env === 'browser') {
2993 * @license RequireJS Copyright (c) 2012, The Dojo Foundation All Rights Reserved.
2994 * Available via the MIT or new BSD license.
2995 * see: http://github.com/jrburke/requirejs for details
2998 /*jslint strict: false */
2999 /*global define: false, load: false */
3001 //Just a stub for use with uglify's consolidator.js
3002 define('browser/assert', function () {
3008 if(env === 'node') {
3010 * @license RequireJS Copyright (c) 2012, The Dojo Foundation All Rights Reserved.
3011 * Available via the MIT or new BSD license.
3012 * see: http://github.com/jrburke/requirejs for details
3015 /*jslint strict: false */
3016 /*global define: false, load: false */
3018 //Needed so that rhino/assert can return a stub for uglify's consolidator.js
3019 define('node/assert', ['assert'], function (assert) {
3025 if(env === 'rhino') {
3027 * @license RequireJS Copyright (c) 2012, The Dojo Foundation All Rights Reserved.
3028 * Available via the MIT or new BSD license.
3029 * see: http://github.com/jrburke/requirejs for details
3032 /*jslint strict: false */
3033 /*global define: false, load: false */
3035 //Just a stub for use with uglify's consolidator.js
3036 define('rhino/assert', function () {
3042 if(env === 'xpconnect') {
3044 * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved.
3045 * Available via the MIT or new BSD license.
3046 * see: http://github.com/jrburke/requirejs for details
3049 /*jslint strict: false */
3050 /*global define: false, load: false */
3052 //Just a stub for use with uglify's consolidator.js
3053 define('xpconnect/assert', function () {
3059 if(env === 'browser') {
3061 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
3062 * Available via the MIT or new BSD license.
3063 * see: http://github.com/jrburke/requirejs for details
3066 /*jslint strict: false */
3067 /*global define: false, process: false */
3069 define('browser/args', function () {
3070 //Always expect config via an API call
3076 if(env === 'node') {
3078 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
3079 * Available via the MIT or new BSD license.
3080 * see: http://github.com/jrburke/requirejs for details
3083 /*jslint strict: false */
3084 /*global define: false, process: false */
3086 define('node/args', function () {
3087 //Do not return the "node" or "r.js" arguments
3088 var args = process.argv.slice(2);
3090 //Ignore any command option used for main x.js branching
3091 if (args[0] && args[0].indexOf('-') === 0) {
3092 args = args.slice(1);
3100 if(env === 'rhino') {
3102 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
3103 * Available via the MIT or new BSD license.
3104 * see: http://github.com/jrburke/requirejs for details
3107 /*jslint strict: false */
3108 /*global define: false, process: false */
3110 var jsLibRhinoArgs = (typeof rhinoArgs !== 'undefined' && rhinoArgs) || [].concat(Array.prototype.slice.call(arguments, 0));
3112 define('rhino/args', function () {
3113 var args = jsLibRhinoArgs;
3115 //Ignore any command option used for main x.js branching
3116 if (args[0] && args[0].indexOf('-') === 0) {
3117 args = args.slice(1);
3125 if(env === 'xpconnect') {
3127 * @license Copyright (c) 2013, The Dojo Foundation All Rights Reserved.
3128 * Available via the MIT or new BSD license.
3129 * see: http://github.com/jrburke/requirejs for details
3132 /*jslint strict: false */
3133 /*global define, xpconnectArgs */
3135 var jsLibXpConnectArgs = (typeof xpconnectArgs !== 'undefined' && xpconnectArgs) || [].concat(Array.prototype.slice.call(arguments, 0));
3137 define('xpconnect/args', function () {
3138 var args = jsLibXpConnectArgs;
3140 //Ignore any command option used for main x.js branching
3141 if (args[0] && args[0].indexOf('-') === 0) {
3142 args = args.slice(1);
3150 if(env === 'browser') {
3152 * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
3153 * Available via the MIT or new BSD license.
3154 * see: http://github.com/jrburke/requirejs for details
3157 /*jslint strict: false */
3158 /*global define: false, console: false */
3160 define('browser/load', ['./file'], function (file) {
3161 function load(fileName) {
3162 eval(file.readFile(fileName));
3170 if(env === 'node') {
3172 * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
3173 * Available via the MIT or new BSD license.
3174 * see: http://github.com/jrburke/requirejs for details
3177 /*jslint strict: false */
3178 /*global define: false, console: false */
3180 define('node/load', ['fs'], function (fs) {
3181 function load(fileName) {
3182 var contents = fs.readFileSync(fileName, 'utf8');
3183 process.compile(contents, fileName);
3191 if(env === 'rhino') {
3193 * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
3194 * Available via the MIT or new BSD license.
3195 * see: http://github.com/jrburke/requirejs for details
3198 /*jslint strict: false */
3199 /*global define: false, load: false */
3201 define('rhino/load', function () {
3207 if(env === 'xpconnect') {
3209 * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved.
3210 * Available via the MIT or new BSD license.
3211 * see: http://github.com/jrburke/requirejs for details
3214 /*jslint strict: false */
3215 /*global define: false, load: false */
3217 define('xpconnect/load', function () {
3223 if(env === 'browser') {
3225 * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved.
3226 * Available via the MIT or new BSD license.
3227 * see: http://github.com/jrburke/requirejs for details
3230 /*jslint sloppy: true, nomen: true */
3231 /*global require, define, console, XMLHttpRequest, requirejs, location */
3233 define('browser/file', ['prim'], function (prim) {
3236 currDirRegExp = /^\.(\/|$)/;
3238 function frontSlash(path) {
3239 return path.replace(/\\/g, '/');
3242 function exists(path) {
3243 var status, xhr = new XMLHttpRequest();
3245 //Oh yeah, that is right SYNC IO. Behold its glory
3246 //and horrible blocking behavior.
3247 xhr.open('HEAD', path, false);
3249 status = xhr.status;
3251 return status === 200 || status === 304;
3254 function mkDir(dir) {
3255 console.log('mkDir is no-op in browser');
3258 function mkFullDir(dir) {
3259 console.log('mkFullDir is no-op in browser');
3263 backSlashRegExp: /\\/g,
3264 exclusionRegExp: /^\./,
3265 getLineSeparator: function () {
3269 exists: function (fileName) {
3270 return exists(fileName);
3273 parent: function (fileName) {
3274 var parts = fileName.split('/');
3276 return parts.join('/');
3280 * Gets the absolute file path as a string, normalized
3281 * to using front slashes for path separators.
3282 * @param {String} fileName
3284 absPath: function (fileName) {
3286 if (currDirRegExp.test(fileName)) {
3287 dir = frontSlash(location.href);
3288 if (dir.indexOf('/') !== -1) {
3289 dir = dir.split('/');
3291 //Pull off protocol and host, just want
3292 //to allow paths (other build parts, like
3293 //require._isSupportedBuildUrl do not support
3294 //full URLs), but a full path from
3299 dir = '/' + dir.join('/');
3302 fileName = dir + fileName.substring(1);
3308 normalize: function (fileName) {
3312 isFile: function (path) {
3316 isDirectory: function (path) {
3320 getFilteredFileList: function (startDir, regExpFilters, makeUnixPaths) {
3321 console.log('file.getFilteredFileList is no-op in browser');
3324 copyDir: function (srcDir, destDir, regExpFilter, onlyCopyNew) {
3325 console.log('file.copyDir is no-op in browser');
3329 copyFile: function (srcFileName, destFileName, onlyCopyNew) {
3330 console.log('file.copyFile is no-op in browser');
3334 * Renames a file. May fail if "to" already exists or is on another drive.
3336 renameFile: function (from, to) {
3337 console.log('file.renameFile is no-op in browser');
3341 * Reads a *text* file.
3343 readFile: function (path, encoding) {
3344 var xhr = new XMLHttpRequest();
3346 //Oh yeah, that is right SYNC IO. Behold its glory
3347 //and horrible blocking behavior.
3348 xhr.open('GET', path, false);
3351 return xhr.responseText;
3354 readFileAsync: function (path, encoding) {
3355 var xhr = new XMLHttpRequest(),
3358 xhr.open('GET', path, true);
3361 xhr.onreadystatechange = function () {
3362 if (xhr.readyState === 4) {
3363 if (xhr.status > 400) {
3364 d.reject(new Error('Status: ' + xhr.status + ': ' + xhr.statusText));
3366 d.resolve(xhr.responseText);
3374 saveUtf8File: function (fileName, fileContents) {
3375 //summary: saves a *text* file using UTF-8 encoding.
3376 file.saveFile(fileName, fileContents, "utf8");
3379 saveFile: function (fileName, fileContents, encoding) {
3380 requirejs.browser.saveFile(fileName, fileContents, encoding);
3383 deleteFile: function (fileName) {
3384 console.log('file.deleteFile is no-op in browser');
3388 * Deletes any empty directories under the given directory.
3390 deleteEmptyDirs: function (startDir) {
3391 console.log('file.deleteEmptyDirs is no-op in browser');
3401 if(env === 'node') {
3403 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
3404 * Available via the MIT or new BSD license.
3405 * see: http://github.com/jrburke/requirejs for details
3408 /*jslint plusplus: false, octal:false, strict: false */
3409 /*global define: false, process: false */
3411 define('node/file', ['fs', 'path', 'prim'], function (fs, path, prim) {
3413 var isWindows = process.platform === 'win32',
3414 windowsDriveRegExp = /^[a-zA-Z]\:\/$/,
3417 function frontSlash(path) {
3418 return path.replace(/\\/g, '/');
3421 function exists(path) {
3422 if (isWindows && path.charAt(path.length - 1) === '/' &&
3423 path.charAt(path.length - 2) !== ':') {
3424 path = path.substring(0, path.length - 1);
3435 function mkDir(dir) {
3436 if (!exists(dir) && (!isWindows || !windowsDriveRegExp.test(dir))) {
3437 fs.mkdirSync(dir, 511);
3441 function mkFullDir(dir) {
3442 var parts = dir.split('/'),
3446 parts.forEach(function (part) {
3447 //First part may be empty string if path starts with a slash.
3448 currDir += part + '/';
3458 backSlashRegExp: /\\/g,
3459 exclusionRegExp: /^\./,
3460 getLineSeparator: function () {
3464 exists: function (fileName) {
3465 return exists(fileName);
3468 parent: function (fileName) {
3469 var parts = fileName.split('/');
3471 return parts.join('/');
3475 * Gets the absolute file path as a string, normalized
3476 * to using front slashes for path separators.
3477 * @param {String} fileName
3479 absPath: function (fileName) {
3480 return frontSlash(path.normalize(frontSlash(fs.realpathSync(fileName))));
3483 normalize: function (fileName) {
3484 return frontSlash(path.normalize(fileName));
3487 isFile: function (path) {
3488 return fs.statSync(path).isFile();
3491 isDirectory: function (path) {
3492 return fs.statSync(path).isDirectory();
3495 getFilteredFileList: function (/*String*/startDir, /*RegExp*/regExpFilters, /*boolean?*/makeUnixPaths) {
3496 //summary: Recurses startDir and finds matches to the files that match regExpFilters.include
3497 //and do not match regExpFilters.exclude. Or just one regexp can be passed in for regExpFilters,
3498 //and it will be treated as the "include" case.
3499 //Ignores files/directories that start with a period (.) unless exclusionRegExp
3500 //is set to another value.
3501 var files = [], topDir, regExpInclude, regExpExclude, dirFileArray,
3502 i, stat, filePath, ok, dirFiles, fileName;
3506 regExpInclude = regExpFilters.include || regExpFilters;
3507 regExpExclude = regExpFilters.exclude || null;
3509 if (file.exists(topDir)) {
3510 dirFileArray = fs.readdirSync(topDir);
3511 for (i = 0; i < dirFileArray.length; i++) {
3512 fileName = dirFileArray[i];
3513 filePath = path.join(topDir, fileName);
3514 stat = fs.statSync(filePath);
3515 if (stat.isFile()) {
3516 if (makeUnixPaths) {
3517 //Make sure we have a JS string.
3518 if (filePath.indexOf("/") === -1) {
3519 filePath = frontSlash(filePath);
3524 if (regExpInclude) {
3525 ok = filePath.match(regExpInclude);
3527 if (ok && regExpExclude) {
3528 ok = !filePath.match(regExpExclude);
3531 if (ok && (!file.exclusionRegExp ||
3532 !file.exclusionRegExp.test(fileName))) {
3533 files.push(filePath);
3535 } else if (stat.isDirectory() &&
3536 (!file.exclusionRegExp || !file.exclusionRegExp.test(fileName))) {
3537 dirFiles = this.getFilteredFileList(filePath, regExpFilters, makeUnixPaths);
3538 files.push.apply(files, dirFiles);
3543 return files; //Array
3546 copyDir: function (/*String*/srcDir, /*String*/destDir, /*RegExp?*/regExpFilter, /*boolean?*/onlyCopyNew) {
3547 //summary: copies files from srcDir to destDir using the regExpFilter to determine if the
3548 //file should be copied. Returns a list file name strings of the destinations that were copied.
3549 regExpFilter = regExpFilter || /\w/;
3551 //Normalize th directory names, but keep front slashes.
3552 //path module on windows now returns backslashed paths.
3553 srcDir = frontSlash(path.normalize(srcDir));
3554 destDir = frontSlash(path.normalize(destDir));
3556 var fileNames = file.getFilteredFileList(srcDir, regExpFilter, true),
3557 copiedFiles = [], i, srcFileName, destFileName;
3559 for (i = 0; i < fileNames.length; i++) {
3560 srcFileName = fileNames[i];
3561 destFileName = srcFileName.replace(srcDir, destDir);
3563 if (file.copyFile(srcFileName, destFileName, onlyCopyNew)) {
3564 copiedFiles.push(destFileName);
3568 return copiedFiles.length ? copiedFiles : null; //Array or null
3571 copyFile: function (/*String*/srcFileName, /*String*/destFileName, /*boolean?*/onlyCopyNew) {
3572 //summary: copies srcFileName to destFileName. If onlyCopyNew is set, it only copies the file if
3573 //srcFileName is newer than destFileName. Returns a boolean indicating if the copy occurred.
3576 //logger.trace("Src filename: " + srcFileName);
3577 //logger.trace("Dest filename: " + destFileName);
3579 //If onlyCopyNew is true, then compare dates and only copy if the src is newer
3582 if (file.exists(destFileName) && fs.statSync(destFileName).mtime.getTime() >= fs.statSync(srcFileName).mtime.getTime()) {
3583 return false; //Boolean
3587 //Make sure destination dir exists.
3588 parentDir = path.dirname(destFileName);
3589 if (!file.exists(parentDir)) {
3590 mkFullDir(parentDir);
3593 fs.writeFileSync(destFileName, fs.readFileSync(srcFileName, 'binary'), 'binary');
3595 return true; //Boolean
3599 * Renames a file. May fail if "to" already exists or is on another drive.
3601 renameFile: function (from, to) {
3602 return fs.renameSync(from, to);
3606 * Reads a *text* file.
3608 readFile: function (/*String*/path, /*String?*/encoding) {
3609 if (encoding === 'utf-8') {
3616 var text = fs.readFileSync(path, encoding);
3618 //Hmm, would not expect to get A BOM, but it seems to happen,
3619 //remove it just in case.
3620 if (text.indexOf('\uFEFF') === 0) {
3621 text = text.substring(1, text.length);
3627 readFileAsync: function (path, encoding) {
3630 d.resolve(file.readFile(path, encoding));
3637 saveUtf8File: function (/*String*/fileName, /*String*/fileContents) {
3638 //summary: saves a *text* file using UTF-8 encoding.
3639 file.saveFile(fileName, fileContents, "utf8");
3642 saveFile: function (/*String*/fileName, /*String*/fileContents, /*String?*/encoding) {
3643 //summary: saves a *text* file.
3646 if (encoding === 'utf-8') {
3653 //Make sure destination directories exist.
3654 parentDir = path.dirname(fileName);
3655 if (!file.exists(parentDir)) {
3656 mkFullDir(parentDir);
3659 fs.writeFileSync(fileName, fileContents, encoding);
3662 deleteFile: function (/*String*/fileName) {
3663 //summary: deletes a file or directory if it exists.
3665 if (file.exists(fileName)) {
3666 stat = fs.statSync(fileName);
3667 if (stat.isDirectory()) {
3668 files = fs.readdirSync(fileName);
3669 for (i = 0; i < files.length; i++) {
3670 this.deleteFile(path.join(fileName, files[i]));
3672 fs.rmdirSync(fileName);
3674 fs.unlinkSync(fileName);
3681 * Deletes any empty directories under the given directory.
3683 deleteEmptyDirs: function (startDir) {
3684 var dirFileArray, i, fileName, filePath, stat;
3686 if (file.exists(startDir)) {
3687 dirFileArray = fs.readdirSync(startDir);
3688 for (i = 0; i < dirFileArray.length; i++) {
3689 fileName = dirFileArray[i];
3690 filePath = path.join(startDir, fileName);
3691 stat = fs.statSync(filePath);
3692 if (stat.isDirectory()) {
3693 file.deleteEmptyDirs(filePath);
3697 //If directory is now empty, remove it.
3698 if (fs.readdirSync(startDir).length === 0) {
3699 file.deleteFile(startDir);
3711 if(env === 'rhino') {
3713 * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
3714 * Available via the MIT or new BSD license.
3715 * see: http://github.com/jrburke/requirejs for details
3717 //Helper functions to deal with file I/O.
3719 /*jslint plusplus: false */
3720 /*global java: false, define: false */
3722 define('rhino/file', ['prim'], function (prim) {
3724 backSlashRegExp: /\\/g,
3726 exclusionRegExp: /^\./,
3728 getLineSeparator: function () {
3729 return file.lineSeparator;
3732 lineSeparator: java.lang.System.getProperty("line.separator"), //Java String
3734 exists: function (fileName) {
3735 return (new java.io.File(fileName)).exists();
3738 parent: function (fileName) {
3739 return file.absPath((new java.io.File(fileName)).getParentFile());
3742 normalize: function (fileName) {
3743 return file.absPath(fileName);
3746 isFile: function (path) {
3747 return (new java.io.File(path)).isFile();
3750 isDirectory: function (path) {
3751 return (new java.io.File(path)).isDirectory();
3755 * Gets the absolute file path as a string, normalized
3756 * to using front slashes for path separators.
3757 * @param {java.io.File||String} file
3759 absPath: function (fileObj) {
3760 if (typeof fileObj === "string") {
3761 fileObj = new java.io.File(fileObj);
3763 return (fileObj.getCanonicalPath() + "").replace(file.backSlashRegExp, "/");
3766 getFilteredFileList: function (/*String*/startDir, /*RegExp*/regExpFilters, /*boolean?*/makeUnixPaths, /*boolean?*/startDirIsJavaObject) {
3767 //summary: Recurses startDir and finds matches to the files that match regExpFilters.include
3768 //and do not match regExpFilters.exclude. Or just one regexp can be passed in for regExpFilters,
3769 //and it will be treated as the "include" case.
3770 //Ignores files/directories that start with a period (.) unless exclusionRegExp
3771 //is set to another value.
3772 var files = [], topDir, regExpInclude, regExpExclude, dirFileArray,
3773 i, fileObj, filePath, ok, dirFiles;
3776 if (!startDirIsJavaObject) {
3777 topDir = new java.io.File(startDir);
3780 regExpInclude = regExpFilters.include || regExpFilters;
3781 regExpExclude = regExpFilters.exclude || null;
3783 if (topDir.exists()) {
3784 dirFileArray = topDir.listFiles();
3785 for (i = 0; i < dirFileArray.length; i++) {
3786 fileObj = dirFileArray[i];
3787 if (fileObj.isFile()) {
3788 filePath = fileObj.getPath();
3789 if (makeUnixPaths) {
3790 //Make sure we have a JS string.
3791 filePath = String(filePath);
3792 if (filePath.indexOf("/") === -1) {
3793 filePath = filePath.replace(/\\/g, "/");
3798 if (regExpInclude) {
3799 ok = filePath.match(regExpInclude);
3801 if (ok && regExpExclude) {
3802 ok = !filePath.match(regExpExclude);
3805 if (ok && (!file.exclusionRegExp ||
3806 !file.exclusionRegExp.test(fileObj.getName()))) {
3807 files.push(filePath);
3809 } else if (fileObj.isDirectory() &&
3810 (!file.exclusionRegExp || !file.exclusionRegExp.test(fileObj.getName()))) {
3811 dirFiles = this.getFilteredFileList(fileObj, regExpFilters, makeUnixPaths, true);
3812 files.push.apply(files, dirFiles);
3817 return files; //Array
3820 copyDir: function (/*String*/srcDir, /*String*/destDir, /*RegExp?*/regExpFilter, /*boolean?*/onlyCopyNew) {
3821 //summary: copies files from srcDir to destDir using the regExpFilter to determine if the
3822 //file should be copied. Returns a list file name strings of the destinations that were copied.
3823 regExpFilter = regExpFilter || /\w/;
3825 var fileNames = file.getFilteredFileList(srcDir, regExpFilter, true),
3826 copiedFiles = [], i, srcFileName, destFileName;
3828 for (i = 0; i < fileNames.length; i++) {
3829 srcFileName = fileNames[i];
3830 destFileName = srcFileName.replace(srcDir, destDir);
3832 if (file.copyFile(srcFileName, destFileName, onlyCopyNew)) {
3833 copiedFiles.push(destFileName);
3837 return copiedFiles.length ? copiedFiles : null; //Array or null
3840 copyFile: function (/*String*/srcFileName, /*String*/destFileName, /*boolean?*/onlyCopyNew) {
3841 //summary: copies srcFileName to destFileName. If onlyCopyNew is set, it only copies the file if
3842 //srcFileName is newer than destFileName. Returns a boolean indicating if the copy occurred.
3843 var destFile = new java.io.File(destFileName), srcFile, parentDir,
3844 srcChannel, destChannel;
3846 //logger.trace("Src filename: " + srcFileName);
3847 //logger.trace("Dest filename: " + destFileName);
3849 //If onlyCopyNew is true, then compare dates and only copy if the src is newer
3852 srcFile = new java.io.File(srcFileName);
3853 if (destFile.exists() && destFile.lastModified() >= srcFile.lastModified()) {
3854 return false; //Boolean
3858 //Make sure destination dir exists.
3859 parentDir = destFile.getParentFile();
3860 if (!parentDir.exists()) {
3861 if (!parentDir.mkdirs()) {
3862 throw "Could not create directory: " + parentDir.getCanonicalPath();
3866 //Java's version of copy file.
3867 srcChannel = new java.io.FileInputStream(srcFileName).getChannel();
3868 destChannel = new java.io.FileOutputStream(destFileName).getChannel();
3869 destChannel.transferFrom(srcChannel, 0, srcChannel.size());
3871 destChannel.close();
3873 return true; //Boolean
3877 * Renames a file. May fail if "to" already exists or is on another drive.
3879 renameFile: function (from, to) {
3880 return (new java.io.File(from)).renameTo((new java.io.File(to)));
3883 readFile: function (/*String*/path, /*String?*/encoding) {
3884 //A file read function that can deal with BOMs
3885 encoding = encoding || "utf-8";
3886 var fileObj = new java.io.File(path),
3887 input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(fileObj), encoding)),
3890 stringBuffer = new java.lang.StringBuffer();
3891 line = input.readLine();
3893 // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
3894 // http://www.unicode.org/faq/utf_bom.html
3896 // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
3897 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
3898 if (line && line.length() && line.charAt(0) === 0xfeff) {
3899 // Eat the BOM, since we've already found the encoding on this file,
3900 // and we plan to concatenating this buffer with others; the BOM should
3901 // only appear at the top of a file.
3902 line = line.substring(1);
3904 while (line !== null) {
3905 stringBuffer.append(line);
3906 stringBuffer.append(file.lineSeparator);
3907 line = input.readLine();
3909 //Make sure we return a JavaScript string and not a Java string.
3910 return String(stringBuffer.toString()); //String
3916 readFileAsync: function (path, encoding) {
3919 d.resolve(file.readFile(path, encoding));
3926 saveUtf8File: function (/*String*/fileName, /*String*/fileContents) {
3927 //summary: saves a file using UTF-8 encoding.
3928 file.saveFile(fileName, fileContents, "utf-8");
3931 saveFile: function (/*String*/fileName, /*String*/fileContents, /*String?*/encoding) {
3932 //summary: saves a file.
3933 var outFile = new java.io.File(fileName), outWriter, parentDir, os;
3935 parentDir = outFile.getAbsoluteFile().getParentFile();
3936 if (!parentDir.exists()) {
3937 if (!parentDir.mkdirs()) {
3938 throw "Could not create directory: " + parentDir.getAbsolutePath();
3943 outWriter = new java.io.OutputStreamWriter(new java.io.FileOutputStream(outFile), encoding);
3945 outWriter = new java.io.OutputStreamWriter(new java.io.FileOutputStream(outFile));
3948 os = new java.io.BufferedWriter(outWriter);
3950 os.write(fileContents);
3956 deleteFile: function (/*String*/fileName) {
3957 //summary: deletes a file or directory if it exists.
3958 var fileObj = new java.io.File(fileName), files, i;
3959 if (fileObj.exists()) {
3960 if (fileObj.isDirectory()) {
3961 files = fileObj.listFiles();
3962 for (i = 0; i < files.length; i++) {
3963 this.deleteFile(files[i]);
3966 fileObj["delete"]();
3971 * Deletes any empty directories under the given directory.
3972 * The startDirIsJavaObject is private to this implementation's
3975 deleteEmptyDirs: function (startDir, startDirIsJavaObject) {
3976 var topDir = startDir,
3977 dirFileArray, i, fileObj;
3979 if (!startDirIsJavaObject) {
3980 topDir = new java.io.File(startDir);
3983 if (topDir.exists()) {
3984 dirFileArray = topDir.listFiles();
3985 for (i = 0; i < dirFileArray.length; i++) {
3986 fileObj = dirFileArray[i];
3987 if (fileObj.isDirectory()) {
3988 file.deleteEmptyDirs(fileObj, true);
3992 //If the directory is empty now, delete it.
3993 if (topDir.listFiles().length === 0) {
3994 file.deleteFile(String(topDir.getPath()));
4005 if(env === 'xpconnect') {
4007 * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved.
4008 * Available via the MIT or new BSD license.
4009 * see: http://github.com/jrburke/requirejs for details
4011 //Helper functions to deal with file I/O.
4013 /*jslint plusplus: false */
4014 /*global define, Components, xpcUtil */
4016 define('xpconnect/file', ['prim'], function (prim) {
4018 Cc = Components.classes,
4019 Ci = Components.interfaces,
4020 //Depends on xpcUtil which is set up in x.js
4021 xpfile = xpcUtil.xpfile;
4023 function mkFullDir(dirObj) {
4024 //1 is DIRECTORY_TYPE, 511 is 0777 permissions
4025 if (!dirObj.exists()) {
4026 dirObj.create(1, 511);
4031 backSlashRegExp: /\\/g,
4033 exclusionRegExp: /^\./,
4035 getLineSeparator: function () {
4036 return file.lineSeparator;
4039 lineSeparator: ('@mozilla.org/windows-registry-key;1' in Cc) ?
4042 exists: function (fileName) {
4043 return xpfile(fileName).exists();
4046 parent: function (fileName) {
4047 return xpfile(fileName).parent;
4050 normalize: function (fileName) {
4051 return file.absPath(fileName);
4054 isFile: function (path) {
4055 return xpfile(path).isFile();
4058 isDirectory: function (path) {
4059 return xpfile(path).isDirectory();
4063 * Gets the absolute file path as a string, normalized
4064 * to using front slashes for path separators.
4065 * @param {java.io.File||String} file
4067 absPath: function (fileObj) {
4068 if (typeof fileObj === "string") {
4069 fileObj = xpfile(fileObj);
4071 return fileObj.path;
4074 getFilteredFileList: function (/*String*/startDir, /*RegExp*/regExpFilters, /*boolean?*/makeUnixPaths, /*boolean?*/startDirIsObject) {
4075 //summary: Recurses startDir and finds matches to the files that match regExpFilters.include
4076 //and do not match regExpFilters.exclude. Or just one regexp can be passed in for regExpFilters,
4077 //and it will be treated as the "include" case.
4078 //Ignores files/directories that start with a period (.) unless exclusionRegExp
4079 //is set to another value.
4080 var files = [], topDir, regExpInclude, regExpExclude, dirFileArray,
4081 fileObj, filePath, ok, dirFiles;
4084 if (!startDirIsObject) {
4085 topDir = xpfile(startDir);
4088 regExpInclude = regExpFilters.include || regExpFilters;
4089 regExpExclude = regExpFilters.exclude || null;
4091 if (topDir.exists()) {
4092 dirFileArray = topDir.directoryEntries;
4093 while (dirFileArray.hasMoreElements()) {
4094 fileObj = dirFileArray.getNext().QueryInterface(Ci.nsILocalFile);
4095 if (fileObj.isFile()) {
4096 filePath = fileObj.path;
4097 if (makeUnixPaths) {
4098 if (filePath.indexOf("/") === -1) {
4099 filePath = filePath.replace(/\\/g, "/");
4104 if (regExpInclude) {
4105 ok = filePath.match(regExpInclude);
4107 if (ok && regExpExclude) {
4108 ok = !filePath.match(regExpExclude);
4111 if (ok && (!file.exclusionRegExp ||
4112 !file.exclusionRegExp.test(fileObj.leafName))) {
4113 files.push(filePath);
4115 } else if (fileObj.isDirectory() &&
4116 (!file.exclusionRegExp || !file.exclusionRegExp.test(fileObj.leafName))) {
4117 dirFiles = this.getFilteredFileList(fileObj, regExpFilters, makeUnixPaths, true);
4118 files.push.apply(files, dirFiles);
4123 return files; //Array
4126 copyDir: function (/*String*/srcDir, /*String*/destDir, /*RegExp?*/regExpFilter, /*boolean?*/onlyCopyNew) {
4127 //summary: copies files from srcDir to destDir using the regExpFilter to determine if the
4128 //file should be copied. Returns a list file name strings of the destinations that were copied.
4129 regExpFilter = regExpFilter || /\w/;
4131 var fileNames = file.getFilteredFileList(srcDir, regExpFilter, true),
4132 copiedFiles = [], i, srcFileName, destFileName;
4134 for (i = 0; i < fileNames.length; i += 1) {
4135 srcFileName = fileNames[i];
4136 destFileName = srcFileName.replace(srcDir, destDir);
4138 if (file.copyFile(srcFileName, destFileName, onlyCopyNew)) {
4139 copiedFiles.push(destFileName);
4143 return copiedFiles.length ? copiedFiles : null; //Array or null
4146 copyFile: function (/*String*/srcFileName, /*String*/destFileName, /*boolean?*/onlyCopyNew) {
4147 //summary: copies srcFileName to destFileName. If onlyCopyNew is set, it only copies the file if
4148 //srcFileName is newer than destFileName. Returns a boolean indicating if the copy occurred.
4149 var destFile = xpfile(destFileName),
4150 srcFile = xpfile(srcFileName);
4152 //logger.trace("Src filename: " + srcFileName);
4153 //logger.trace("Dest filename: " + destFileName);
4155 //If onlyCopyNew is true, then compare dates and only copy if the src is newer
4158 if (destFile.exists() && destFile.lastModifiedTime >= srcFile.lastModifiedTime) {
4159 return false; //Boolean
4163 srcFile.copyTo(destFile.parent, destFile.leafName);
4165 return true; //Boolean
4169 * Renames a file. May fail if "to" already exists or is on another drive.
4171 renameFile: function (from, to) {
4172 var toFile = xpfile(to);
4173 return xpfile(from).moveTo(toFile.parent, toFile.leafName);
4176 readFile: xpcUtil.readFile,
4178 readFileAsync: function (path, encoding) {
4181 d.resolve(file.readFile(path, encoding));
4188 saveUtf8File: function (/*String*/fileName, /*String*/fileContents) {
4189 //summary: saves a file using UTF-8 encoding.
4190 file.saveFile(fileName, fileContents, "utf-8");
4193 saveFile: function (/*String*/fileName, /*String*/fileContents, /*String?*/encoding) {
4194 var outStream, convertStream,
4195 fileObj = xpfile(fileName);
4197 mkFullDir(fileObj.parent);
4200 outStream = Cc['@mozilla.org/network/file-output-stream;1']
4201 .createInstance(Ci.nsIFileOutputStream);
4202 //438 is decimal for 0777
4203 outStream.init(fileObj, 0x02 | 0x08 | 0x20, 511, 0);
4205 convertStream = Cc['@mozilla.org/intl/converter-output-stream;1']
4206 .createInstance(Ci.nsIConverterOutputStream);
4208 convertStream.init(outStream, encoding, 0, 0);
4209 convertStream.writeString(fileContents);
4211 throw new Error((fileObj && fileObj.path || '') + ': ' + e);
4213 if (convertStream) {
4214 convertStream.close();
4222 deleteFile: function (/*String*/fileName) {
4223 //summary: deletes a file or directory if it exists.
4224 var fileObj = xpfile(fileName);
4225 if (fileObj.exists()) {
4226 fileObj.remove(true);
4231 * Deletes any empty directories under the given directory.
4232 * The startDirIsJavaObject is private to this implementation's
4235 deleteEmptyDirs: function (startDir, startDirIsObject) {
4236 var topDir = startDir,
4237 dirFileArray, fileObj;
4239 if (!startDirIsObject) {
4240 topDir = xpfile(startDir);
4243 if (topDir.exists()) {
4244 dirFileArray = topDir.directoryEntries;
4245 while (dirFileArray.hasMoreElements()) {
4246 fileObj = dirFileArray.getNext().QueryInterface(Ci.nsILocalFile);
4248 if (fileObj.isDirectory()) {
4249 file.deleteEmptyDirs(fileObj, true);
4253 //If the directory is empty now, delete it.
4254 dirFileArray = topDir.directoryEntries;
4255 if (!dirFileArray.hasMoreElements()) {
4256 file.deleteFile(topDir.path);
4267 if(env === 'browser') {
4269 define('browser/quit', function () {
4271 return function (code) {
4276 if(env === 'node') {
4278 define('node/quit', function () {
4280 return function (code) {
4282 var exit = function () {
4283 if (draining === 0) {
4289 if (process.stdout.bufferSize) {
4291 process.stdout.once('drain', exit);
4293 if (process.stderr.bufferSize) {
4295 process.stderr.once('drain', exit);
4303 if(env === 'rhino') {
4305 define('rhino/quit', function () {
4307 return function (code) {
4314 if(env === 'xpconnect') {
4316 define('xpconnect/quit', function () {
4318 return function (code) {
4325 if(env === 'browser') {
4327 * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
4328 * Available via the MIT or new BSD license.
4329 * see: http://github.com/jrburke/requirejs for details
4332 /*jslint strict: false */
4333 /*global define: false, console: false */
4335 define('browser/print', function () {
4336 function print(msg) {
4345 if(env === 'node') {
4347 * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
4348 * Available via the MIT or new BSD license.
4349 * see: http://github.com/jrburke/requirejs for details
4352 /*jslint strict: false */
4353 /*global define: false, console: false */
4355 define('node/print', function () {
4356 function print(msg) {
4365 if(env === 'rhino') {
4367 * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
4368 * Available via the MIT or new BSD license.
4369 * see: http://github.com/jrburke/requirejs for details
4372 /*jslint strict: false */
4373 /*global define: false, print: false */
4375 define('rhino/print', function () {
4381 if(env === 'xpconnect') {
4383 * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved.
4384 * Available via the MIT or new BSD license.
4385 * see: http://github.com/jrburke/requirejs for details
4388 /*jslint strict: false */
4389 /*global define: false, print: false */
4391 define('xpconnect/print', function () {
4397 * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
4398 * Available via the MIT or new BSD license.
4399 * see: http://github.com/jrburke/requirejs for details
4402 /*jslint nomen: false, strict: false */
4403 /*global define: false */
4405 define('logger', ['env!env/print'], function (print) {
4415 logLevel: function( level ) {
4419 trace: function (message) {
4420 if (this.level <= this.TRACE) {
4421 this._print(message);
4425 info: function (message) {
4426 if (this.level <= this.INFO) {
4427 this._print(message);
4431 warn: function (message) {
4432 if (this.level <= this.WARN) {
4433 this._print(message);
4437 error: function (message) {
4438 if (this.level <= this.ERROR) {
4439 this._print(message);
4443 _print: function (message) {
4444 this._sysPrint((this.logPrefix ? (this.logPrefix + " ") : "") + message);
4447 _sysPrint: function (message) {
4454 //Just a blank file to use when building the optimizer with the optimizer,
4455 //so that the build does not attempt to inline some env modules,
4456 //like Node's fs and path.
4459 Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
4460 Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be>
4461 Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
4462 Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com>
4463 Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
4464 Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
4465 Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
4467 Redistribution and use in source and binary forms, with or without
4468 modification, are permitted provided that the following conditions are met:
4470 * Redistributions of source code must retain the above copyright
4471 notice, this list of conditions and the following disclaimer.
4472 * Redistributions in binary form must reproduce the above copyright
4473 notice, this list of conditions and the following disclaimer in the
4474 documentation and/or other materials provided with the distribution.
4476 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
4477 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4478 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4479 ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
4480 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
4481 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
4482 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
4483 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4484 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
4485 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4488 /*jslint bitwise:true plusplus:true */
4489 /*global esprima:true, define:true, exports:true, window: true,
4490 throwError: true, createLiteral: true, generateStatement: true,
4491 parseAssignmentExpression: true, parseBlock: true, parseExpression: true,
4492 parseFunctionDeclaration: true, parseFunctionExpression: true,
4493 parseFunctionSourceElements: true, parseVariableIdentifier: true,
4494 parseLeftHandSideExpression: true,
4495 parseStatement: true, parseSourceElement: true */
4497 (function (root, factory) {
4500 // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
4501 // Rhino, and plain browser loading.
4502 if (typeof define === 'function' && define.amd) {
4503 define('esprima', ['exports'], factory);
4504 } else if (typeof exports !== 'undefined') {
4507 factory((root.esprima = {}));
4509 }(this, function (exports) {
4540 TokenName[Token.BooleanLiteral] = 'Boolean';
4541 TokenName[Token.EOF] = '<end>';
4542 TokenName[Token.Identifier] = 'Identifier';
4543 TokenName[Token.Keyword] = 'Keyword';
4544 TokenName[Token.NullLiteral] = 'Null';
4545 TokenName[Token.NumericLiteral] = 'Numeric';
4546 TokenName[Token.Punctuator] = 'Punctuator';
4547 TokenName[Token.StringLiteral] = 'String';
4550 AssignmentExpression: 'AssignmentExpression',
4551 ArrayExpression: 'ArrayExpression',
4552 BlockStatement: 'BlockStatement',
4553 BinaryExpression: 'BinaryExpression',
4554 BreakStatement: 'BreakStatement',
4555 CallExpression: 'CallExpression',
4556 CatchClause: 'CatchClause',
4557 ConditionalExpression: 'ConditionalExpression',
4558 ContinueStatement: 'ContinueStatement',
4559 DoWhileStatement: 'DoWhileStatement',
4560 DebuggerStatement: 'DebuggerStatement',
4561 EmptyStatement: 'EmptyStatement',
4562 ExpressionStatement: 'ExpressionStatement',
4563 ForStatement: 'ForStatement',
4564 ForInStatement: 'ForInStatement',
4565 FunctionDeclaration: 'FunctionDeclaration',
4566 FunctionExpression: 'FunctionExpression',
4567 Identifier: 'Identifier',
4568 IfStatement: 'IfStatement',
4570 LabeledStatement: 'LabeledStatement',
4571 LogicalExpression: 'LogicalExpression',
4572 MemberExpression: 'MemberExpression',
4573 NewExpression: 'NewExpression',
4574 ObjectExpression: 'ObjectExpression',
4576 Property: 'Property',
4577 ReturnStatement: 'ReturnStatement',
4578 SequenceExpression: 'SequenceExpression',
4579 SwitchStatement: 'SwitchStatement',
4580 SwitchCase: 'SwitchCase',
4581 ThisExpression: 'ThisExpression',
4582 ThrowStatement: 'ThrowStatement',
4583 TryStatement: 'TryStatement',
4584 UnaryExpression: 'UnaryExpression',
4585 UpdateExpression: 'UpdateExpression',
4586 VariableDeclaration: 'VariableDeclaration',
4587 VariableDeclarator: 'VariableDeclarator',
4588 WhileStatement: 'WhileStatement',
4589 WithStatement: 'WithStatement'
4598 // Error messages should be identical to V8.
4600 UnexpectedToken: 'Unexpected token %0',
4601 UnexpectedNumber: 'Unexpected number',
4602 UnexpectedString: 'Unexpected string',
4603 UnexpectedIdentifier: 'Unexpected identifier',
4604 UnexpectedReserved: 'Unexpected reserved word',
4605 UnexpectedEOS: 'Unexpected end of input',
4606 NewlineAfterThrow: 'Illegal newline after throw',
4607 InvalidRegExp: 'Invalid regular expression',
4608 UnterminatedRegExp: 'Invalid regular expression: missing /',
4609 InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
4610 InvalidLHSInForIn: 'Invalid left-hand side in for-in',
4611 MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
4612 NoCatchOrFinally: 'Missing catch or finally after try',
4613 UnknownLabel: 'Undefined label \'%0\'',
4614 Redeclaration: '%0 \'%1\' has already been declared',
4615 IllegalContinue: 'Illegal continue statement',
4616 IllegalBreak: 'Illegal break statement',
4617 IllegalReturn: 'Illegal return statement',
4618 StrictModeWith: 'Strict mode code may not include a with statement',
4619 StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
4620 StrictVarName: 'Variable name may not be eval or arguments in strict mode',
4621 StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
4622 StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
4623 StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
4624 StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
4625 StrictDelete: 'Delete of an unqualified identifier in strict mode.',
4626 StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode',
4627 AccessorDataProperty: 'Object literal may not have data and accessor property with the same name',
4628 AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name',
4629 StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
4630 StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
4631 StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
4632 StrictReservedWord: 'Use of future reserved word in strict mode'
4635 // See also tools/generate-unicode-regex.py.
4637 NonAsciiIdentifierStart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]'),
4638 NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]')
4641 // Ensure the condition is true, otherwise throw an error.
4642 // This is only to have a better contract semantic, i.e. another safety net
4643 // to catch a logic error. The condition shall be fulfilled in normal case.
4644 // Do NOT use this to enforce a certain condition on any user input.
4646 function assert(condition, message) {
4648 throw new Error('ASSERT: ' + message);
4652 function sliceSource(from, to) {
4653 return source.slice(from, to);
4656 if (typeof 'esprima'[0] === 'undefined') {
4657 sliceSource = function sliceArraySource(from, to) {
4658 return source.slice(from, to).join('');
4662 function isDecimalDigit(ch) {
4663 return '0123456789'.indexOf(ch) >= 0;
4666 function isHexDigit(ch) {
4667 return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
4670 function isOctalDigit(ch) {
4671 return '01234567'.indexOf(ch) >= 0;
4677 function isWhiteSpace(ch) {
4678 return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') ||
4679 (ch === '\u000C') || (ch === '\u00A0') ||
4680 (ch.charCodeAt(0) >= 0x1680 &&
4681 '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(ch) >= 0);
4684 // 7.3 Line Terminators
4686 function isLineTerminator(ch) {
4687 return (ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029');
4690 // 7.6 Identifier Names and Identifiers
4692 function isIdentifierStart(ch) {
4693 return (ch === '$') || (ch === '_') || (ch === '\\') ||
4694 (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
4695 ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierStart.test(ch));
4698 function isIdentifierPart(ch) {
4699 return (ch === '$') || (ch === '_') || (ch === '\\') ||
4700 (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
4701 ((ch >= '0') && (ch <= '9')) ||
4702 ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch));
4705 // 7.6.1.2 Future Reserved Words
4707 function isFutureReservedWord(id) {
4710 // Future reserved words.
4723 function isStrictModeReservedWord(id) {
4726 // Strict Mode reserved words.
4742 function isRestrictedWord(id) {
4743 return id === 'eval' || id === 'arguments';
4748 function isKeyword(id) {
4749 var keyword = false;
4750 switch (id.length) {
4752 keyword = (id === 'if') || (id === 'in') || (id === 'do');
4755 keyword = (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try');
4758 keyword = (id === 'this') || (id === 'else') || (id === 'case') || (id === 'void') || (id === 'with');
4761 keyword = (id === 'while') || (id === 'break') || (id === 'catch') || (id === 'throw');
4764 keyword = (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch');
4767 keyword = (id === 'default') || (id === 'finally');
4770 keyword = (id === 'function') || (id === 'continue') || (id === 'debugger');
4773 keyword = (id === 'instanceof');
4782 // Future reserved words.
4783 // 'const' is specialized as Keyword in V8.
4787 // For compatiblity to SpiderMonkey and ES.next
4793 if (strict && isStrictModeReservedWord(id)) {
4797 return isFutureReservedWord(id);
4802 function skipComment() {
4803 var ch, blockComment, lineComment;
4805 blockComment = false;
4806 lineComment = false;
4808 while (index < length) {
4812 ch = source[index++];
4813 if (isLineTerminator(ch)) {
4814 lineComment = false;
4815 if (ch === '\r' && source[index] === '\n') {
4821 } else if (blockComment) {
4822 if (isLineTerminator(ch)) {
4823 if (ch === '\r' && source[index + 1] === '\n') {
4829 if (index >= length) {
4830 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
4833 ch = source[index++];
4834 if (index >= length) {
4835 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
4841 blockComment = false;
4845 } else if (ch === '/') {
4846 ch = source[index + 1];
4850 } else if (ch === '*') {
4852 blockComment = true;
4853 if (index >= length) {
4854 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
4859 } else if (isWhiteSpace(ch)) {
4861 } else if (isLineTerminator(ch)) {
4863 if (ch === '\r' && source[index] === '\n') {
4874 function scanHexEscape(prefix) {
4875 var i, len, ch, code = 0;
4877 len = (prefix === 'u') ? 4 : 2;
4878 for (i = 0; i < len; ++i) {
4879 if (index < length && isHexDigit(source[index])) {
4880 ch = source[index++];
4881 code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
4886 return String.fromCharCode(code);
4889 function scanIdentifier() {
4890 var ch, start, id, restore;
4893 if (!isIdentifierStart(ch)) {
4900 if (source[index] !== 'u') {
4905 ch = scanHexEscape('u');
4907 if (ch === '\\' || !isIdentifierStart(ch)) {
4916 id = source[index++];
4919 while (index < length) {
4921 if (!isIdentifierPart(ch)) {
4926 if (source[index] !== 'u') {
4931 ch = scanHexEscape('u');
4933 if (ch === '\\' || !isIdentifierPart(ch)) {
4942 id += source[index++];
4946 // There is no keyword or literal with only one character.
4947 // Thus, it must be an identifier.
4948 if (id.length === 1) {
4950 type: Token.Identifier,
4952 lineNumber: lineNumber,
4953 lineStart: lineStart,
4954 range: [start, index]
4958 if (isKeyword(id)) {
4960 type: Token.Keyword,
4962 lineNumber: lineNumber,
4963 lineStart: lineStart,
4964 range: [start, index]
4968 // 7.8.1 Null Literals
4970 if (id === 'null') {
4972 type: Token.NullLiteral,
4974 lineNumber: lineNumber,
4975 lineStart: lineStart,
4976 range: [start, index]
4980 // 7.8.2 Boolean Literals
4982 if (id === 'true' || id === 'false') {
4984 type: Token.BooleanLiteral,
4986 lineNumber: lineNumber,
4987 lineStart: lineStart,
4988 range: [start, index]
4993 type: Token.Identifier,
4995 lineNumber: lineNumber,
4996 lineStart: lineStart,
4997 range: [start, index]
5003 function scanPunctuator() {
5005 ch1 = source[index],
5010 // Check for most common single-character punctuators.
5012 if (ch1 === ';' || ch1 === '{' || ch1 === '}') {
5015 type: Token.Punctuator,
5017 lineNumber: lineNumber,
5018 lineStart: lineStart,
5019 range: [start, index]
5023 if (ch1 === ',' || ch1 === '(' || ch1 === ')') {
5026 type: Token.Punctuator,
5028 lineNumber: lineNumber,
5029 lineStart: lineStart,
5030 range: [start, index]
5034 // Dot (.) can also start a floating-point number, hence the need
5035 // to check the next character.
5037 ch2 = source[index + 1];
5038 if (ch1 === '.' && !isDecimalDigit(ch2)) {
5040 type: Token.Punctuator,
5041 value: source[index++],
5042 lineNumber: lineNumber,
5043 lineStart: lineStart,
5044 range: [start, index]
5048 // Peek more characters.
5050 ch3 = source[index + 2];
5051 ch4 = source[index + 3];
5053 // 4-character punctuator: >>>=
5055 if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
5059 type: Token.Punctuator,
5061 lineNumber: lineNumber,
5062 lineStart: lineStart,
5063 range: [start, index]
5068 // 3-character punctuators: === !== >>> <<= >>=
5070 if (ch1 === '=' && ch2 === '=' && ch3 === '=') {
5073 type: Token.Punctuator,
5075 lineNumber: lineNumber,
5076 lineStart: lineStart,
5077 range: [start, index]
5081 if (ch1 === '!' && ch2 === '=' && ch3 === '=') {
5084 type: Token.Punctuator,
5086 lineNumber: lineNumber,
5087 lineStart: lineStart,
5088 range: [start, index]
5092 if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
5095 type: Token.Punctuator,
5097 lineNumber: lineNumber,
5098 lineStart: lineStart,
5099 range: [start, index]
5103 if (ch1 === '<' && ch2 === '<' && ch3 === '=') {
5106 type: Token.Punctuator,
5108 lineNumber: lineNumber,
5109 lineStart: lineStart,
5110 range: [start, index]
5114 if (ch1 === '>' && ch2 === '>' && ch3 === '=') {
5117 type: Token.Punctuator,
5119 lineNumber: lineNumber,
5120 lineStart: lineStart,
5121 range: [start, index]
5125 // 2-character punctuators: <= >= == != ++ -- << >> && ||
5126 // += -= *= %= &= |= ^= /=
5129 if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
5132 type: Token.Punctuator,
5134 lineNumber: lineNumber,
5135 lineStart: lineStart,
5136 range: [start, index]
5141 if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) {
5142 if ('+-<>&|'.indexOf(ch2) >= 0) {
5145 type: Token.Punctuator,
5147 lineNumber: lineNumber,
5148 lineStart: lineStart,
5149 range: [start, index]
5154 // The remaining 1-character punctuators.
5156 if ('[]<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) {
5158 type: Token.Punctuator,
5159 value: source[index++],
5160 lineNumber: lineNumber,
5161 lineStart: lineStart,
5162 range: [start, index]
5167 // 7.8.3 Numeric Literals
5169 function scanNumericLiteral() {
5170 var number, start, ch;
5173 assert(isDecimalDigit(ch) || (ch === '.'),
5174 'Numeric literal must start with a decimal digit or a decimal point');
5179 number = source[index++];
5182 // Hex number starts with '0x'.
5183 // Octal number starts with '0'.
5184 if (number === '0') {
5185 if (ch === 'x' || ch === 'X') {
5186 number += source[index++];
5187 while (index < length) {
5189 if (!isHexDigit(ch)) {
5192 number += source[index++];
5195 if (number.length <= 2) {
5197 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
5200 if (index < length) {
5202 if (isIdentifierStart(ch)) {
5203 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
5207 type: Token.NumericLiteral,
5208 value: parseInt(number, 16),
5209 lineNumber: lineNumber,
5210 lineStart: lineStart,
5211 range: [start, index]
5213 } else if (isOctalDigit(ch)) {
5214 number += source[index++];
5215 while (index < length) {
5217 if (!isOctalDigit(ch)) {
5220 number += source[index++];
5223 if (index < length) {
5225 if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
5226 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
5230 type: Token.NumericLiteral,
5231 value: parseInt(number, 8),
5233 lineNumber: lineNumber,
5234 lineStart: lineStart,
5235 range: [start, index]
5239 // decimal number starts with '0' such as '09' is illegal.
5240 if (isDecimalDigit(ch)) {
5241 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
5245 while (index < length) {
5247 if (!isDecimalDigit(ch)) {
5250 number += source[index++];
5255 number += source[index++];
5256 while (index < length) {
5258 if (!isDecimalDigit(ch)) {
5261 number += source[index++];
5265 if (ch === 'e' || ch === 'E') {
5266 number += source[index++];
5269 if (ch === '+' || ch === '-') {
5270 number += source[index++];
5274 if (isDecimalDigit(ch)) {
5275 number += source[index++];
5276 while (index < length) {
5278 if (!isDecimalDigit(ch)) {
5281 number += source[index++];
5284 ch = 'character ' + ch;
5285 if (index >= length) {
5288 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
5292 if (index < length) {
5294 if (isIdentifierStart(ch)) {
5295 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
5300 type: Token.NumericLiteral,
5301 value: parseFloat(number),
5302 lineNumber: lineNumber,
5303 lineStart: lineStart,
5304 range: [start, index]
5308 // 7.8.4 String Literals
5310 function scanStringLiteral() {
5311 var str = '', quote, start, ch, code, unescaped, restore, octal = false;
5313 quote = source[index];
5314 assert((quote === '\'' || quote === '"'),
5315 'String literal must starts with a quote');
5320 while (index < length) {
5321 ch = source[index++];
5326 } else if (ch === '\\') {
5327 ch = source[index++];
5328 if (!isLineTerminator(ch)) {
5342 unescaped = scanHexEscape(ch);
5361 if (isOctalDigit(ch)) {
5362 code = '01234567'.indexOf(ch);
5364 // \0 is not octal escape sequence
5369 if (index < length && isOctalDigit(source[index])) {
5371 code = code * 8 + '01234567'.indexOf(source[index++]);
5373 // 3 digits are only allowed when string starts
5375 if ('0123'.indexOf(ch) >= 0 &&
5377 isOctalDigit(source[index])) {
5378 code = code * 8 + '01234567'.indexOf(source[index++]);
5381 str += String.fromCharCode(code);
5389 if (ch === '\r' && source[index] === '\n') {
5393 } else if (isLineTerminator(ch)) {
5401 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
5405 type: Token.StringLiteral,
5408 lineNumber: lineNumber,
5409 lineStart: lineStart,
5410 range: [start, index]
5414 function scanRegExp() {
5415 var str, ch, start, pattern, flags, value, classMarker = false, restore, terminated = false;
5422 assert(ch === '/', 'Regular expression literal must start with a slash');
5423 str = source[index++];
5425 while (index < length) {
5426 ch = source[index++];
5430 classMarker = false;
5434 ch = source[index++];
5436 if (isLineTerminator(ch)) {
5437 throwError({}, Messages.UnterminatedRegExp);
5440 } else if (ch === '/') {
5443 } else if (ch === '[') {
5445 } else if (isLineTerminator(ch)) {
5446 throwError({}, Messages.UnterminatedRegExp);
5452 throwError({}, Messages.UnterminatedRegExp);
5455 // Exclude leading and trailing slash.
5456 pattern = str.substr(1, str.length - 2);
5459 while (index < length) {
5461 if (!isIdentifierPart(ch)) {
5466 if (ch === '\\' && index < length) {
5471 ch = scanHexEscape('u');
5475 for (; restore < index; ++restore) {
5476 str += source[restore];
5493 value = new RegExp(pattern, flags);
5495 throwError({}, Messages.InvalidRegExp);
5501 range: [start, index]
5505 function isIdentifierName(token) {
5506 return token.type === Token.Identifier ||
5507 token.type === Token.Keyword ||
5508 token.type === Token.BooleanLiteral ||
5509 token.type === Token.NullLiteral;
5512 function advance() {
5517 if (index >= length) {
5520 lineNumber: lineNumber,
5521 lineStart: lineStart,
5522 range: [index, index]
5526 token = scanPunctuator();
5527 if (typeof token !== 'undefined') {
5533 if (ch === '\'' || ch === '"') {
5534 return scanStringLiteral();
5537 if (ch === '.' || isDecimalDigit(ch)) {
5538 return scanNumericLiteral();
5541 token = scanIdentifier();
5542 if (typeof token !== 'undefined') {
5546 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
5553 index = buffer.range[1];
5554 lineNumber = buffer.lineNumber;
5555 lineStart = buffer.lineStart;
5565 function lookahead() {
5566 var pos, line, start;
5568 if (buffer !== null) {
5583 // Return true if there is a line terminator before the next token.
5585 function peekLineTerminator() {
5586 var pos, line, start, found;
5592 found = lineNumber !== line;
5600 // Throw an exception
5602 function throwError(token, messageFormat) {
5604 args = Array.prototype.slice.call(arguments, 2),
5605 msg = messageFormat.replace(
5607 function (whole, index) {
5608 return args[index] || '';
5612 if (typeof token.lineNumber === 'number') {
5613 error = new Error('Line ' + token.lineNumber + ': ' + msg);
5614 error.index = token.range[0];
5615 error.lineNumber = token.lineNumber;
5616 error.column = token.range[0] - lineStart + 1;
5618 error = new Error('Line ' + lineNumber + ': ' + msg);
5619 error.index = index;
5620 error.lineNumber = lineNumber;
5621 error.column = index - lineStart + 1;
5627 function throwErrorTolerant() {
5629 throwError.apply(null, arguments);
5632 extra.errors.push(e);
5640 // Throw an exception because of the token.
5642 function throwUnexpected(token) {
5643 if (token.type === Token.EOF) {
5644 throwError(token, Messages.UnexpectedEOS);
5647 if (token.type === Token.NumericLiteral) {
5648 throwError(token, Messages.UnexpectedNumber);
5651 if (token.type === Token.StringLiteral) {
5652 throwError(token, Messages.UnexpectedString);
5655 if (token.type === Token.Identifier) {
5656 throwError(token, Messages.UnexpectedIdentifier);
5659 if (token.type === Token.Keyword) {
5660 if (isFutureReservedWord(token.value)) {
5661 throwError(token, Messages.UnexpectedReserved);
5662 } else if (strict && isStrictModeReservedWord(token.value)) {
5663 throwErrorTolerant(token, Messages.StrictReservedWord);
5666 throwError(token, Messages.UnexpectedToken, token.value);
5669 // BooleanLiteral, NullLiteral, or Punctuator.
5670 throwError(token, Messages.UnexpectedToken, token.value);
5673 // Expect the next token to match the specified punctuator.
5674 // If not, an exception will be thrown.
5676 function expect(value) {
5678 if (token.type !== Token.Punctuator || token.value !== value) {
5679 throwUnexpected(token);
5683 // Expect the next token to match the specified keyword.
5684 // If not, an exception will be thrown.
5686 function expectKeyword(keyword) {
5688 if (token.type !== Token.Keyword || token.value !== keyword) {
5689 throwUnexpected(token);
5693 // Return true if the next token matches the specified punctuator.
5695 function match(value) {
5696 var token = lookahead();
5697 return token.type === Token.Punctuator && token.value === value;
5700 // Return true if the next token matches the specified keyword
5702 function matchKeyword(keyword) {
5703 var token = lookahead();
5704 return token.type === Token.Keyword && token.value === keyword;
5707 // Return true if the next token is an assignment operator
5709 function matchAssign() {
5710 var token = lookahead(),
5713 if (token.type !== Token.Punctuator) {
5716 return op === '=' ||
5730 function consumeSemicolon() {
5733 // Catch the very common case first.
5734 if (source[index] === ';') {
5741 if (lineNumber !== line) {
5750 token = lookahead();
5751 if (token.type !== Token.EOF && !match('}')) {
5752 throwUnexpected(token);
5756 // Return true if provided expression is LeftHandSideExpression
5758 function isLeftHandSide(expr) {
5759 return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression;
5762 // 11.1.4 Array Initialiser
5764 function parseArrayInitialiser() {
5769 while (!match(']')) {
5772 elements.push(null);
5774 elements.push(parseAssignmentExpression());
5785 type: Syntax.ArrayExpression,
5790 // 11.1.5 Object Initialiser
5792 function parsePropertyFunction(param, first) {
5793 var previousStrict, body;
5795 previousStrict = strict;
5796 body = parseFunctionSourceElements();
5797 if (first && strict && isRestrictedWord(param[0].name)) {
5798 throwErrorTolerant(first, Messages.StrictParamName);
5800 strict = previousStrict;
5803 type: Syntax.FunctionExpression,
5814 function parseObjectPropertyKey() {
5817 // Note: This function is called only from parseObjectProperty(), where
5818 // EOF and Punctuator tokens are already filtered out.
5820 if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
5821 if (strict && token.octal) {
5822 throwErrorTolerant(token, Messages.StrictOctalLiteral);
5824 return createLiteral(token);
5828 type: Syntax.Identifier,
5833 function parseObjectProperty() {
5834 var token, key, id, param;
5836 token = lookahead();
5838 if (token.type === Token.Identifier) {
5840 id = parseObjectPropertyKey();
5842 // Property Assignment: Getter and Setter.
5844 if (token.value === 'get' && !match(':')) {
5845 key = parseObjectPropertyKey();
5849 type: Syntax.Property,
5851 value: parsePropertyFunction([]),
5854 } else if (token.value === 'set' && !match(':')) {
5855 key = parseObjectPropertyKey();
5857 token = lookahead();
5858 if (token.type !== Token.Identifier) {
5860 throwErrorTolerant(token, Messages.UnexpectedToken, token.value);
5862 type: Syntax.Property,
5864 value: parsePropertyFunction([]),
5868 param = [ parseVariableIdentifier() ];
5871 type: Syntax.Property,
5873 value: parsePropertyFunction(param, token),
5880 type: Syntax.Property,
5882 value: parseAssignmentExpression(),
5886 } else if (token.type === Token.EOF || token.type === Token.Punctuator) {
5887 throwUnexpected(token);
5889 key = parseObjectPropertyKey();
5892 type: Syntax.Property,
5894 value: parseAssignmentExpression(),
5900 function parseObjectInitialiser() {
5901 var properties = [], property, name, kind, map = {}, toString = String;
5905 while (!match('}')) {
5906 property = parseObjectProperty();
5908 if (property.key.type === Syntax.Identifier) {
5909 name = property.key.name;
5911 name = toString(property.key.value);
5913 kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set;
5914 if (Object.prototype.hasOwnProperty.call(map, name)) {
5915 if (map[name] === PropertyKind.Data) {
5916 if (strict && kind === PropertyKind.Data) {
5917 throwErrorTolerant({}, Messages.StrictDuplicateProperty);
5918 } else if (kind !== PropertyKind.Data) {
5919 throwErrorTolerant({}, Messages.AccessorDataProperty);
5922 if (kind === PropertyKind.Data) {
5923 throwErrorTolerant({}, Messages.AccessorDataProperty);
5924 } else if (map[name] & kind) {
5925 throwErrorTolerant({}, Messages.AccessorGetSet);
5933 properties.push(property);
5943 type: Syntax.ObjectExpression,
5944 properties: properties
5948 // 11.1.6 The Grouping Operator
5950 function parseGroupExpression() {
5955 expr = parseExpression();
5963 // 11.1 Primary Expressions
5965 function parsePrimaryExpression() {
5966 var token = lookahead(),
5969 if (type === Token.Identifier) {
5971 type: Syntax.Identifier,
5976 if (type === Token.StringLiteral || type === Token.NumericLiteral) {
5977 if (strict && token.octal) {
5978 throwErrorTolerant(token, Messages.StrictOctalLiteral);
5980 return createLiteral(lex());
5983 if (type === Token.Keyword) {
5984 if (matchKeyword('this')) {
5987 type: Syntax.ThisExpression
5991 if (matchKeyword('function')) {
5992 return parseFunctionExpression();
5996 if (type === Token.BooleanLiteral) {
5998 token.value = (token.value === 'true');
5999 return createLiteral(token);
6002 if (type === Token.NullLiteral) {
6005 return createLiteral(token);
6009 return parseArrayInitialiser();
6013 return parseObjectInitialiser();
6017 return parseGroupExpression();
6020 if (match('/') || match('/=')) {
6021 return createLiteral(scanRegExp());
6024 return throwUnexpected(lex());
6027 // 11.2 Left-Hand-Side Expressions
6029 function parseArguments() {
6035 while (index < length) {
6036 args.push(parseAssignmentExpression());
6049 function parseNonComputedProperty() {
6052 if (!isIdentifierName(token)) {
6053 throwUnexpected(token);
6057 type: Syntax.Identifier,
6062 function parseNonComputedMember() {
6065 return parseNonComputedProperty();
6068 function parseComputedMember() {
6073 expr = parseExpression();
6080 function parseNewExpression() {
6083 expectKeyword('new');
6086 type: Syntax.NewExpression,
6087 callee: parseLeftHandSideExpression(),
6092 expr['arguments'] = parseArguments();
6098 function parseLeftHandSideExpressionAllowCall() {
6101 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
6103 while (match('.') || match('[') || match('(')) {
6106 type: Syntax.CallExpression,
6108 'arguments': parseArguments()
6110 } else if (match('[')) {
6112 type: Syntax.MemberExpression,
6115 property: parseComputedMember()
6119 type: Syntax.MemberExpression,
6122 property: parseNonComputedMember()
6131 function parseLeftHandSideExpression() {
6134 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
6136 while (match('.') || match('[')) {
6139 type: Syntax.MemberExpression,
6142 property: parseComputedMember()
6146 type: Syntax.MemberExpression,
6149 property: parseNonComputedMember()
6157 // 11.3 Postfix Expressions
6159 function parsePostfixExpression() {
6160 var expr = parseLeftHandSideExpressionAllowCall(), token;
6162 token = lookahead();
6163 if (token.type !== Token.Punctuator) {
6167 if ((match('++') || match('--')) && !peekLineTerminator()) {
6169 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
6170 throwErrorTolerant({}, Messages.StrictLHSPostfix);
6173 if (!isLeftHandSide(expr)) {
6174 throwError({}, Messages.InvalidLHSInAssignment);
6178 type: Syntax.UpdateExpression,
6179 operator: lex().value,
6188 // 11.4 Unary Operators
6190 function parseUnaryExpression() {
6193 token = lookahead();
6194 if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
6195 return parsePostfixExpression();
6198 if (match('++') || match('--')) {
6200 expr = parseUnaryExpression();
6202 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
6203 throwErrorTolerant({}, Messages.StrictLHSPrefix);
6206 if (!isLeftHandSide(expr)) {
6207 throwError({}, Messages.InvalidLHSInAssignment);
6211 type: Syntax.UpdateExpression,
6212 operator: token.value,
6219 if (match('+') || match('-') || match('~') || match('!')) {
6221 type: Syntax.UnaryExpression,
6222 operator: lex().value,
6223 argument: parseUnaryExpression(),
6229 if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
6231 type: Syntax.UnaryExpression,
6232 operator: lex().value,
6233 argument: parseUnaryExpression(),
6236 if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) {
6237 throwErrorTolerant({}, Messages.StrictDelete);
6242 return parsePostfixExpression();
6245 // 11.5 Multiplicative Operators
6247 function parseMultiplicativeExpression() {
6248 var expr = parseUnaryExpression();
6250 while (match('*') || match('/') || match('%')) {
6252 type: Syntax.BinaryExpression,
6253 operator: lex().value,
6255 right: parseUnaryExpression()
6262 // 11.6 Additive Operators
6264 function parseAdditiveExpression() {
6265 var expr = parseMultiplicativeExpression();
6267 while (match('+') || match('-')) {
6269 type: Syntax.BinaryExpression,
6270 operator: lex().value,
6272 right: parseMultiplicativeExpression()
6279 // 11.7 Bitwise Shift Operators
6281 function parseShiftExpression() {
6282 var expr = parseAdditiveExpression();
6284 while (match('<<') || match('>>') || match('>>>')) {
6286 type: Syntax.BinaryExpression,
6287 operator: lex().value,
6289 right: parseAdditiveExpression()
6295 // 11.8 Relational Operators
6297 function parseRelationalExpression() {
6298 var expr, previousAllowIn;
6300 previousAllowIn = state.allowIn;
6301 state.allowIn = true;
6303 expr = parseShiftExpression();
6305 while (match('<') || match('>') || match('<=') || match('>=') || (previousAllowIn && matchKeyword('in')) || matchKeyword('instanceof')) {
6307 type: Syntax.BinaryExpression,
6308 operator: lex().value,
6310 right: parseShiftExpression()
6314 state.allowIn = previousAllowIn;
6318 // 11.9 Equality Operators
6320 function parseEqualityExpression() {
6321 var expr = parseRelationalExpression();
6323 while (match('==') || match('!=') || match('===') || match('!==')) {
6325 type: Syntax.BinaryExpression,
6326 operator: lex().value,
6328 right: parseRelationalExpression()
6335 // 11.10 Binary Bitwise Operators
6337 function parseBitwiseANDExpression() {
6338 var expr = parseEqualityExpression();
6340 while (match('&')) {
6343 type: Syntax.BinaryExpression,
6346 right: parseEqualityExpression()
6353 function parseBitwiseXORExpression() {
6354 var expr = parseBitwiseANDExpression();
6356 while (match('^')) {
6359 type: Syntax.BinaryExpression,
6362 right: parseBitwiseANDExpression()
6369 function parseBitwiseORExpression() {
6370 var expr = parseBitwiseXORExpression();
6372 while (match('|')) {
6375 type: Syntax.BinaryExpression,
6378 right: parseBitwiseXORExpression()
6385 // 11.11 Binary Logical Operators
6387 function parseLogicalANDExpression() {
6388 var expr = parseBitwiseORExpression();
6390 while (match('&&')) {
6393 type: Syntax.LogicalExpression,
6396 right: parseBitwiseORExpression()
6403 function parseLogicalORExpression() {
6404 var expr = parseLogicalANDExpression();
6406 while (match('||')) {
6409 type: Syntax.LogicalExpression,
6412 right: parseLogicalANDExpression()
6419 // 11.12 Conditional Operator
6421 function parseConditionalExpression() {
6422 var expr, previousAllowIn, consequent;
6424 expr = parseLogicalORExpression();
6428 previousAllowIn = state.allowIn;
6429 state.allowIn = true;
6430 consequent = parseAssignmentExpression();
6431 state.allowIn = previousAllowIn;
6435 type: Syntax.ConditionalExpression,
6437 consequent: consequent,
6438 alternate: parseAssignmentExpression()
6445 // 11.13 Assignment Operators
6447 function parseAssignmentExpression() {
6450 token = lookahead();
6451 expr = parseConditionalExpression();
6453 if (matchAssign()) {
6454 // LeftHandSideExpression
6455 if (!isLeftHandSide(expr)) {
6456 throwError({}, Messages.InvalidLHSInAssignment);
6460 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
6461 throwErrorTolerant(token, Messages.StrictLHSAssignment);
6465 type: Syntax.AssignmentExpression,
6466 operator: lex().value,
6468 right: parseAssignmentExpression()
6475 // 11.14 Comma Operator
6477 function parseExpression() {
6478 var expr = parseAssignmentExpression();
6482 type: Syntax.SequenceExpression,
6483 expressions: [ expr ]
6486 while (index < length) {
6491 expr.expressions.push(parseAssignmentExpression());
6500 function parseStatementList() {
6504 while (index < length) {
6508 statement = parseSourceElement();
6509 if (typeof statement === 'undefined') {
6512 list.push(statement);
6518 function parseBlock() {
6523 block = parseStatementList();
6528 type: Syntax.BlockStatement,
6533 // 12.2 Variable Statement
6535 function parseVariableIdentifier() {
6538 if (token.type !== Token.Identifier) {
6539 throwUnexpected(token);
6543 type: Syntax.Identifier,
6548 function parseVariableDeclaration(kind) {
6549 var id = parseVariableIdentifier(),
6553 if (strict && isRestrictedWord(id.name)) {
6554 throwErrorTolerant({}, Messages.StrictVarName);
6557 if (kind === 'const') {
6559 init = parseAssignmentExpression();
6560 } else if (match('=')) {
6562 init = parseAssignmentExpression();
6566 type: Syntax.VariableDeclarator,
6572 function parseVariableDeclarationList(kind) {
6576 list.push(parseVariableDeclaration(kind));
6581 } while (index < length);
6586 function parseVariableStatement() {
6589 expectKeyword('var');
6591 declarations = parseVariableDeclarationList();
6596 type: Syntax.VariableDeclaration,
6597 declarations: declarations,
6602 // kind may be `const` or `let`
6603 // Both are experimental and not in the specification yet.
6604 // see http://wiki.ecmascript.org/doku.php?id=harmony:const
6605 // and http://wiki.ecmascript.org/doku.php?id=harmony:let
6606 function parseConstLetDeclaration(kind) {
6609 expectKeyword(kind);
6611 declarations = parseVariableDeclarationList(kind);
6616 type: Syntax.VariableDeclaration,
6617 declarations: declarations,
6622 // 12.3 Empty Statement
6624 function parseEmptyStatement() {
6628 type: Syntax.EmptyStatement
6632 // 12.4 Expression Statement
6634 function parseExpressionStatement() {
6635 var expr = parseExpression();
6640 type: Syntax.ExpressionStatement,
6645 // 12.5 If statement
6647 function parseIfStatement() {
6648 var test, consequent, alternate;
6650 expectKeyword('if');
6654 test = parseExpression();
6658 consequent = parseStatement();
6660 if (matchKeyword('else')) {
6662 alternate = parseStatement();
6668 type: Syntax.IfStatement,
6670 consequent: consequent,
6671 alternate: alternate
6675 // 12.6 Iteration Statements
6677 function parseDoWhileStatement() {
6678 var body, test, oldInIteration;
6680 expectKeyword('do');
6682 oldInIteration = state.inIteration;
6683 state.inIteration = true;
6685 body = parseStatement();
6687 state.inIteration = oldInIteration;
6689 expectKeyword('while');
6693 test = parseExpression();
6702 type: Syntax.DoWhileStatement,
6708 function parseWhileStatement() {
6709 var test, body, oldInIteration;
6711 expectKeyword('while');
6715 test = parseExpression();
6719 oldInIteration = state.inIteration;
6720 state.inIteration = true;
6722 body = parseStatement();
6724 state.inIteration = oldInIteration;
6727 type: Syntax.WhileStatement,
6733 function parseForVariableDeclaration() {
6737 type: Syntax.VariableDeclaration,
6738 declarations: parseVariableDeclarationList(),
6743 function parseForStatement() {
6744 var init, test, update, left, right, body, oldInIteration;
6746 init = test = update = null;
6748 expectKeyword('for');
6755 if (matchKeyword('var') || matchKeyword('let')) {
6756 state.allowIn = false;
6757 init = parseForVariableDeclaration();
6758 state.allowIn = true;
6760 if (init.declarations.length === 1 && matchKeyword('in')) {
6763 right = parseExpression();
6767 state.allowIn = false;
6768 init = parseExpression();
6769 state.allowIn = true;
6771 if (matchKeyword('in')) {
6772 // LeftHandSideExpression
6773 if (!isLeftHandSide(init)) {
6774 throwError({}, Messages.InvalidLHSInForIn);
6779 right = parseExpression();
6784 if (typeof left === 'undefined') {
6789 if (typeof left === 'undefined') {
6792 test = parseExpression();
6797 update = parseExpression();
6803 oldInIteration = state.inIteration;
6804 state.inIteration = true;
6806 body = parseStatement();
6808 state.inIteration = oldInIteration;
6810 if (typeof left === 'undefined') {
6812 type: Syntax.ForStatement,
6821 type: Syntax.ForInStatement,
6829 // 12.7 The continue statement
6831 function parseContinueStatement() {
6832 var token, label = null;
6834 expectKeyword('continue');
6836 // Optimize the most common form: 'continue;'.
6837 if (source[index] === ';') {
6840 if (!state.inIteration) {
6841 throwError({}, Messages.IllegalContinue);
6845 type: Syntax.ContinueStatement,
6850 if (peekLineTerminator()) {
6851 if (!state.inIteration) {
6852 throwError({}, Messages.IllegalContinue);
6856 type: Syntax.ContinueStatement,
6861 token = lookahead();
6862 if (token.type === Token.Identifier) {
6863 label = parseVariableIdentifier();
6865 if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
6866 throwError({}, Messages.UnknownLabel, label.name);
6872 if (label === null && !state.inIteration) {
6873 throwError({}, Messages.IllegalContinue);
6877 type: Syntax.ContinueStatement,
6882 // 12.8 The break statement
6884 function parseBreakStatement() {
6885 var token, label = null;
6887 expectKeyword('break');
6889 // Optimize the most common form: 'break;'.
6890 if (source[index] === ';') {
6893 if (!(state.inIteration || state.inSwitch)) {
6894 throwError({}, Messages.IllegalBreak);
6898 type: Syntax.BreakStatement,
6903 if (peekLineTerminator()) {
6904 if (!(state.inIteration || state.inSwitch)) {
6905 throwError({}, Messages.IllegalBreak);
6909 type: Syntax.BreakStatement,
6914 token = lookahead();
6915 if (token.type === Token.Identifier) {
6916 label = parseVariableIdentifier();
6918 if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
6919 throwError({}, Messages.UnknownLabel, label.name);
6925 if (label === null && !(state.inIteration || state.inSwitch)) {
6926 throwError({}, Messages.IllegalBreak);
6930 type: Syntax.BreakStatement,
6935 // 12.9 The return statement
6937 function parseReturnStatement() {
6938 var token, argument = null;
6940 expectKeyword('return');
6942 if (!state.inFunctionBody) {
6943 throwErrorTolerant({}, Messages.IllegalReturn);
6946 // 'return' followed by a space and an identifier is very common.
6947 if (source[index] === ' ') {
6948 if (isIdentifierStart(source[index + 1])) {
6949 argument = parseExpression();
6952 type: Syntax.ReturnStatement,
6958 if (peekLineTerminator()) {
6960 type: Syntax.ReturnStatement,
6966 token = lookahead();
6967 if (!match('}') && token.type !== Token.EOF) {
6968 argument = parseExpression();
6975 type: Syntax.ReturnStatement,
6980 // 12.10 The with statement
6982 function parseWithStatement() {
6986 throwErrorTolerant({}, Messages.StrictModeWith);
6989 expectKeyword('with');
6993 object = parseExpression();
6997 body = parseStatement();
7000 type: Syntax.WithStatement,
7006 // 12.10 The swith statement
7008 function parseSwitchCase() {
7013 if (matchKeyword('default')) {
7017 expectKeyword('case');
7018 test = parseExpression();
7022 while (index < length) {
7023 if (match('}') || matchKeyword('default') || matchKeyword('case')) {
7026 statement = parseStatement();
7027 if (typeof statement === 'undefined') {
7030 consequent.push(statement);
7034 type: Syntax.SwitchCase,
7036 consequent: consequent
7040 function parseSwitchStatement() {
7041 var discriminant, cases, clause, oldInSwitch, defaultFound;
7043 expectKeyword('switch');
7047 discriminant = parseExpression();
7056 type: Syntax.SwitchStatement,
7057 discriminant: discriminant
7063 oldInSwitch = state.inSwitch;
7064 state.inSwitch = true;
7065 defaultFound = false;
7067 while (index < length) {
7071 clause = parseSwitchCase();
7072 if (clause.test === null) {
7074 throwError({}, Messages.MultipleDefaultsInSwitch);
7076 defaultFound = true;
7081 state.inSwitch = oldInSwitch;
7086 type: Syntax.SwitchStatement,
7087 discriminant: discriminant,
7092 // 12.13 The throw statement
7094 function parseThrowStatement() {
7097 expectKeyword('throw');
7099 if (peekLineTerminator()) {
7100 throwError({}, Messages.NewlineAfterThrow);
7103 argument = parseExpression();
7108 type: Syntax.ThrowStatement,
7113 // 12.14 The try statement
7115 function parseCatchClause() {
7118 expectKeyword('catch');
7122 throwUnexpected(lookahead());
7125 param = parseVariableIdentifier();
7127 if (strict && isRestrictedWord(param.name)) {
7128 throwErrorTolerant({}, Messages.StrictCatchVariable);
7134 type: Syntax.CatchClause,
7140 function parseTryStatement() {
7141 var block, handlers = [], finalizer = null;
7143 expectKeyword('try');
7145 block = parseBlock();
7147 if (matchKeyword('catch')) {
7148 handlers.push(parseCatchClause());
7151 if (matchKeyword('finally')) {
7153 finalizer = parseBlock();
7156 if (handlers.length === 0 && !finalizer) {
7157 throwError({}, Messages.NoCatchOrFinally);
7161 type: Syntax.TryStatement,
7163 guardedHandlers: [],
7165 finalizer: finalizer
7169 // 12.15 The debugger statement
7171 function parseDebuggerStatement() {
7172 expectKeyword('debugger');
7177 type: Syntax.DebuggerStatement
7183 function parseStatement() {
7184 var token = lookahead(),
7188 if (token.type === Token.EOF) {
7189 throwUnexpected(token);
7192 if (token.type === Token.Punctuator) {
7193 switch (token.value) {
7195 return parseEmptyStatement();
7197 return parseBlock();
7199 return parseExpressionStatement();
7205 if (token.type === Token.Keyword) {
7206 switch (token.value) {
7208 return parseBreakStatement();
7210 return parseContinueStatement();
7212 return parseDebuggerStatement();
7214 return parseDoWhileStatement();
7216 return parseForStatement();
7218 return parseFunctionDeclaration();
7220 return parseIfStatement();
7222 return parseReturnStatement();
7224 return parseSwitchStatement();
7226 return parseThrowStatement();
7228 return parseTryStatement();
7230 return parseVariableStatement();
7232 return parseWhileStatement();
7234 return parseWithStatement();
7240 expr = parseExpression();
7242 // 12.12 Labelled Statements
7243 if ((expr.type === Syntax.Identifier) && match(':')) {
7246 if (Object.prototype.hasOwnProperty.call(state.labelSet, expr.name)) {
7247 throwError({}, Messages.Redeclaration, 'Label', expr.name);
7250 state.labelSet[expr.name] = true;
7251 labeledBody = parseStatement();
7252 delete state.labelSet[expr.name];
7255 type: Syntax.LabeledStatement,
7264 type: Syntax.ExpressionStatement,
7269 // 13 Function Definition
7271 function parseFunctionSourceElements() {
7272 var sourceElement, sourceElements = [], token, directive, firstRestricted,
7273 oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody;
7277 while (index < length) {
7278 token = lookahead();
7279 if (token.type !== Token.StringLiteral) {
7283 sourceElement = parseSourceElement();
7284 sourceElements.push(sourceElement);
7285 if (sourceElement.expression.type !== Syntax.Literal) {
7286 // this is not directive
7289 directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
7290 if (directive === 'use strict') {
7292 if (firstRestricted) {
7293 throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
7296 if (!firstRestricted && token.octal) {
7297 firstRestricted = token;
7302 oldLabelSet = state.labelSet;
7303 oldInIteration = state.inIteration;
7304 oldInSwitch = state.inSwitch;
7305 oldInFunctionBody = state.inFunctionBody;
7307 state.labelSet = {};
7308 state.inIteration = false;
7309 state.inSwitch = false;
7310 state.inFunctionBody = true;
7312 while (index < length) {
7316 sourceElement = parseSourceElement();
7317 if (typeof sourceElement === 'undefined') {
7320 sourceElements.push(sourceElement);
7325 state.labelSet = oldLabelSet;
7326 state.inIteration = oldInIteration;
7327 state.inSwitch = oldInSwitch;
7328 state.inFunctionBody = oldInFunctionBody;
7331 type: Syntax.BlockStatement,
7332 body: sourceElements
7336 function parseFunctionDeclaration() {
7337 var id, param, params = [], body, token, stricted, firstRestricted, message, previousStrict, paramSet;
7339 expectKeyword('function');
7340 token = lookahead();
7341 id = parseVariableIdentifier();
7343 if (isRestrictedWord(token.value)) {
7344 throwErrorTolerant(token, Messages.StrictFunctionName);
7347 if (isRestrictedWord(token.value)) {
7348 firstRestricted = token;
7349 message = Messages.StrictFunctionName;
7350 } else if (isStrictModeReservedWord(token.value)) {
7351 firstRestricted = token;
7352 message = Messages.StrictReservedWord;
7360 while (index < length) {
7361 token = lookahead();
7362 param = parseVariableIdentifier();
7364 if (isRestrictedWord(token.value)) {
7366 message = Messages.StrictParamName;
7368 if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
7370 message = Messages.StrictParamDupe;
7372 } else if (!firstRestricted) {
7373 if (isRestrictedWord(token.value)) {
7374 firstRestricted = token;
7375 message = Messages.StrictParamName;
7376 } else if (isStrictModeReservedWord(token.value)) {
7377 firstRestricted = token;
7378 message = Messages.StrictReservedWord;
7379 } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
7380 firstRestricted = token;
7381 message = Messages.StrictParamDupe;
7385 paramSet[param.name] = true;
7395 previousStrict = strict;
7396 body = parseFunctionSourceElements();
7397 if (strict && firstRestricted) {
7398 throwError(firstRestricted, message);
7400 if (strict && stricted) {
7401 throwErrorTolerant(stricted, message);
7403 strict = previousStrict;
7406 type: Syntax.FunctionDeclaration,
7417 function parseFunctionExpression() {
7418 var token, id = null, stricted, firstRestricted, message, param, params = [], body, previousStrict, paramSet;
7420 expectKeyword('function');
7423 token = lookahead();
7424 id = parseVariableIdentifier();
7426 if (isRestrictedWord(token.value)) {
7427 throwErrorTolerant(token, Messages.StrictFunctionName);
7430 if (isRestrictedWord(token.value)) {
7431 firstRestricted = token;
7432 message = Messages.StrictFunctionName;
7433 } else if (isStrictModeReservedWord(token.value)) {
7434 firstRestricted = token;
7435 message = Messages.StrictReservedWord;
7444 while (index < length) {
7445 token = lookahead();
7446 param = parseVariableIdentifier();
7448 if (isRestrictedWord(token.value)) {
7450 message = Messages.StrictParamName;
7452 if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
7454 message = Messages.StrictParamDupe;
7456 } else if (!firstRestricted) {
7457 if (isRestrictedWord(token.value)) {
7458 firstRestricted = token;
7459 message = Messages.StrictParamName;
7460 } else if (isStrictModeReservedWord(token.value)) {
7461 firstRestricted = token;
7462 message = Messages.StrictReservedWord;
7463 } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
7464 firstRestricted = token;
7465 message = Messages.StrictParamDupe;
7469 paramSet[param.name] = true;
7479 previousStrict = strict;
7480 body = parseFunctionSourceElements();
7481 if (strict && firstRestricted) {
7482 throwError(firstRestricted, message);
7484 if (strict && stricted) {
7485 throwErrorTolerant(stricted, message);
7487 strict = previousStrict;
7490 type: Syntax.FunctionExpression,
7503 function parseSourceElement() {
7504 var token = lookahead();
7506 if (token.type === Token.Keyword) {
7507 switch (token.value) {
7510 return parseConstLetDeclaration(token.value);
7512 return parseFunctionDeclaration();
7514 return parseStatement();
7518 if (token.type !== Token.EOF) {
7519 return parseStatement();
7523 function parseSourceElements() {
7524 var sourceElement, sourceElements = [], token, directive, firstRestricted;
7526 while (index < length) {
7527 token = lookahead();
7528 if (token.type !== Token.StringLiteral) {
7532 sourceElement = parseSourceElement();
7533 sourceElements.push(sourceElement);
7534 if (sourceElement.expression.type !== Syntax.Literal) {
7535 // this is not directive
7538 directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
7539 if (directive === 'use strict') {
7541 if (firstRestricted) {
7542 throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
7545 if (!firstRestricted && token.octal) {
7546 firstRestricted = token;
7551 while (index < length) {
7552 sourceElement = parseSourceElement();
7553 if (typeof sourceElement === 'undefined') {
7556 sourceElements.push(sourceElement);
7558 return sourceElements;
7561 function parseProgram() {
7565 type: Syntax.Program,
7566 body: parseSourceElements()
7571 // The following functions are needed only when the option to preserve
7572 // the comments is active.
7574 function addComment(type, value, start, end, loc) {
7575 assert(typeof start === 'number', 'Comment must have valid position');
7577 // Because the way the actual token is scanned, often the comments
7578 // (if any) are skipped twice during the lexical analysis.
7579 // Thus, we need to skip adding a comment if the comment array already
7581 if (extra.comments.length > 0) {
7582 if (extra.comments[extra.comments.length - 1].range[1] > start) {
7587 extra.comments.push({
7590 range: [start, end],
7595 function scanComment() {
7596 var comment, ch, loc, start, blockComment, lineComment;
7599 blockComment = false;
7600 lineComment = false;
7602 while (index < length) {
7606 ch = source[index++];
7607 if (isLineTerminator(ch)) {
7610 column: index - lineStart - 1
7612 lineComment = false;
7613 addComment('Line', comment, start, index - 1, loc);
7614 if (ch === '\r' && source[index] === '\n') {
7620 } else if (index >= length) {
7621 lineComment = false;
7625 column: length - lineStart
7627 addComment('Line', comment, start, length, loc);
7631 } else if (blockComment) {
7632 if (isLineTerminator(ch)) {
7633 if (ch === '\r' && source[index + 1] === '\n') {
7642 if (index >= length) {
7643 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
7646 ch = source[index++];
7647 if (index >= length) {
7648 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
7654 comment = comment.substr(0, comment.length - 1);
7655 blockComment = false;
7659 column: index - lineStart
7661 addComment('Block', comment, start, index, loc);
7666 } else if (ch === '/') {
7667 ch = source[index + 1];
7672 column: index - lineStart
7678 if (index >= length) {
7681 column: index - lineStart
7683 lineComment = false;
7684 addComment('Line', comment, start, index, loc);
7686 } else if (ch === '*') {
7689 blockComment = true;
7693 column: index - lineStart - 2
7696 if (index >= length) {
7697 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
7702 } else if (isWhiteSpace(ch)) {
7704 } else if (isLineTerminator(ch)) {
7706 if (ch === '\r' && source[index] === '\n') {
7717 function filterCommentLocation() {
7718 var i, entry, comment, comments = [];
7720 for (i = 0; i < extra.comments.length; ++i) {
7721 entry = extra.comments[i];
7727 comment.range = entry.range;
7730 comment.loc = entry.loc;
7732 comments.push(comment);
7735 extra.comments = comments;
7738 function collectToken() {
7739 var start, loc, token, range, value;
7746 column: index - lineStart
7750 token = extra.advance();
7753 column: index - lineStart
7756 if (token.type !== Token.EOF) {
7757 range = [token.range[0], token.range[1]];
7758 value = sliceSource(token.range[0], token.range[1]);
7760 type: TokenName[token.type],
7770 function collectRegex() {
7771 var pos, loc, regex, token;
7779 column: index - lineStart
7783 regex = extra.scanRegExp();
7786 column: index - lineStart
7789 // Pop the previous token, which is likely '/' or '/='
7790 if (extra.tokens.length > 0) {
7791 token = extra.tokens[extra.tokens.length - 1];
7792 if (token.range[0] === pos && token.type === 'Punctuator') {
7793 if (token.value === '/' || token.value === '/=') {
7800 type: 'RegularExpression',
7801 value: regex.literal,
7802 range: [pos, index],
7809 function filterTokenLocation() {
7810 var i, entry, token, tokens = [];
7812 for (i = 0; i < extra.tokens.length; ++i) {
7813 entry = extra.tokens[i];
7819 token.range = entry.range;
7822 token.loc = entry.loc;
7827 extra.tokens = tokens;
7830 function createLiteral(token) {
7832 type: Syntax.Literal,
7837 function createRawLiteral(token) {
7839 type: Syntax.Literal,
7841 raw: sliceSource(token.range[0], token.range[1])
7845 function createLocationMarker() {
7848 marker.range = [index, index];
7852 column: index - lineStart
7856 column: index - lineStart
7860 marker.end = function () {
7861 this.range[1] = index;
7862 this.loc.end.line = lineNumber;
7863 this.loc.end.column = index - lineStart;
7866 marker.applyGroup = function (node) {
7868 node.groupRange = [this.range[0], this.range[1]];
7873 line: this.loc.start.line,
7874 column: this.loc.start.column
7877 line: this.loc.end.line,
7878 column: this.loc.end.column
7884 marker.apply = function (node) {
7886 node.range = [this.range[0], this.range[1]];
7891 line: this.loc.start.line,
7892 column: this.loc.start.column
7895 line: this.loc.end.line,
7896 column: this.loc.end.column
7905 function trackGroupExpression() {
7909 marker = createLocationMarker();
7912 expr = parseExpression();
7917 marker.applyGroup(expr);
7922 function trackLeftHandSideExpression() {
7926 marker = createLocationMarker();
7928 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
7930 while (match('.') || match('[')) {
7933 type: Syntax.MemberExpression,
7936 property: parseComputedMember()
7942 type: Syntax.MemberExpression,
7945 property: parseNonComputedMember()
7955 function trackLeftHandSideExpressionAllowCall() {
7959 marker = createLocationMarker();
7961 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
7963 while (match('.') || match('[') || match('(')) {
7966 type: Syntax.CallExpression,
7968 'arguments': parseArguments()
7972 } else if (match('[')) {
7974 type: Syntax.MemberExpression,
7977 property: parseComputedMember()
7983 type: Syntax.MemberExpression,
7986 property: parseNonComputedMember()
7996 function filterGroup(node) {
7999 n = (Object.prototype.toString.apply(node) === '[object Array]') ? [] : {};
8001 if (node.hasOwnProperty(i) && i !== 'groupRange' && i !== 'groupLoc') {
8003 if (entry === null || typeof entry !== 'object' || entry instanceof RegExp) {
8006 n[i] = filterGroup(entry);
8013 function wrapTrackingFunction(range, loc) {
8015 return function (parseFunction) {
8017 function isBinary(node) {
8018 return node.type === Syntax.LogicalExpression ||
8019 node.type === Syntax.BinaryExpression;
8022 function visit(node) {
8025 if (isBinary(node.left)) {
8028 if (isBinary(node.right)) {
8033 if (node.left.groupRange || node.right.groupRange) {
8034 start = node.left.groupRange ? node.left.groupRange[0] : node.left.range[0];
8035 end = node.right.groupRange ? node.right.groupRange[1] : node.right.range[1];
8036 node.range = [start, end];
8037 } else if (typeof node.range === 'undefined') {
8038 start = node.left.range[0];
8039 end = node.right.range[1];
8040 node.range = [start, end];
8044 if (node.left.groupLoc || node.right.groupLoc) {
8045 start = node.left.groupLoc ? node.left.groupLoc.start : node.left.loc.start;
8046 end = node.right.groupLoc ? node.right.groupLoc.end : node.right.loc.end;
8051 } else if (typeof node.loc === 'undefined') {
8053 start: node.left.loc.start,
8054 end: node.right.loc.end
8060 return function () {
8065 marker = createLocationMarker();
8066 node = parseFunction.apply(null, arguments);
8069 if (range && typeof node.range === 'undefined') {
8073 if (loc && typeof node.loc === 'undefined') {
8077 if (isBinary(node)) {
8090 if (extra.comments) {
8091 extra.skipComment = skipComment;
8092 skipComment = scanComment;
8096 extra.createLiteral = createLiteral;
8097 createLiteral = createRawLiteral;
8100 if (extra.range || extra.loc) {
8102 extra.parseGroupExpression = parseGroupExpression;
8103 extra.parseLeftHandSideExpression = parseLeftHandSideExpression;
8104 extra.parseLeftHandSideExpressionAllowCall = parseLeftHandSideExpressionAllowCall;
8105 parseGroupExpression = trackGroupExpression;
8106 parseLeftHandSideExpression = trackLeftHandSideExpression;
8107 parseLeftHandSideExpressionAllowCall = trackLeftHandSideExpressionAllowCall;
8109 wrapTracking = wrapTrackingFunction(extra.range, extra.loc);
8111 extra.parseAdditiveExpression = parseAdditiveExpression;
8112 extra.parseAssignmentExpression = parseAssignmentExpression;
8113 extra.parseBitwiseANDExpression = parseBitwiseANDExpression;
8114 extra.parseBitwiseORExpression = parseBitwiseORExpression;
8115 extra.parseBitwiseXORExpression = parseBitwiseXORExpression;
8116 extra.parseBlock = parseBlock;
8117 extra.parseFunctionSourceElements = parseFunctionSourceElements;
8118 extra.parseCatchClause = parseCatchClause;
8119 extra.parseComputedMember = parseComputedMember;
8120 extra.parseConditionalExpression = parseConditionalExpression;
8121 extra.parseConstLetDeclaration = parseConstLetDeclaration;
8122 extra.parseEqualityExpression = parseEqualityExpression;
8123 extra.parseExpression = parseExpression;
8124 extra.parseForVariableDeclaration = parseForVariableDeclaration;
8125 extra.parseFunctionDeclaration = parseFunctionDeclaration;
8126 extra.parseFunctionExpression = parseFunctionExpression;
8127 extra.parseLogicalANDExpression = parseLogicalANDExpression;
8128 extra.parseLogicalORExpression = parseLogicalORExpression;
8129 extra.parseMultiplicativeExpression = parseMultiplicativeExpression;
8130 extra.parseNewExpression = parseNewExpression;
8131 extra.parseNonComputedProperty = parseNonComputedProperty;
8132 extra.parseObjectProperty = parseObjectProperty;
8133 extra.parseObjectPropertyKey = parseObjectPropertyKey;
8134 extra.parsePostfixExpression = parsePostfixExpression;
8135 extra.parsePrimaryExpression = parsePrimaryExpression;
8136 extra.parseProgram = parseProgram;
8137 extra.parsePropertyFunction = parsePropertyFunction;
8138 extra.parseRelationalExpression = parseRelationalExpression;
8139 extra.parseStatement = parseStatement;
8140 extra.parseShiftExpression = parseShiftExpression;
8141 extra.parseSwitchCase = parseSwitchCase;
8142 extra.parseUnaryExpression = parseUnaryExpression;
8143 extra.parseVariableDeclaration = parseVariableDeclaration;
8144 extra.parseVariableIdentifier = parseVariableIdentifier;
8146 parseAdditiveExpression = wrapTracking(extra.parseAdditiveExpression);
8147 parseAssignmentExpression = wrapTracking(extra.parseAssignmentExpression);
8148 parseBitwiseANDExpression = wrapTracking(extra.parseBitwiseANDExpression);
8149 parseBitwiseORExpression = wrapTracking(extra.parseBitwiseORExpression);
8150 parseBitwiseXORExpression = wrapTracking(extra.parseBitwiseXORExpression);
8151 parseBlock = wrapTracking(extra.parseBlock);
8152 parseFunctionSourceElements = wrapTracking(extra.parseFunctionSourceElements);
8153 parseCatchClause = wrapTracking(extra.parseCatchClause);
8154 parseComputedMember = wrapTracking(extra.parseComputedMember);
8155 parseConditionalExpression = wrapTracking(extra.parseConditionalExpression);
8156 parseConstLetDeclaration = wrapTracking(extra.parseConstLetDeclaration);
8157 parseEqualityExpression = wrapTracking(extra.parseEqualityExpression);
8158 parseExpression = wrapTracking(extra.parseExpression);
8159 parseForVariableDeclaration = wrapTracking(extra.parseForVariableDeclaration);
8160 parseFunctionDeclaration = wrapTracking(extra.parseFunctionDeclaration);
8161 parseFunctionExpression = wrapTracking(extra.parseFunctionExpression);
8162 parseLeftHandSideExpression = wrapTracking(parseLeftHandSideExpression);
8163 parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression);
8164 parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression);
8165 parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression);
8166 parseNewExpression = wrapTracking(extra.parseNewExpression);
8167 parseNonComputedProperty = wrapTracking(extra.parseNonComputedProperty);
8168 parseObjectProperty = wrapTracking(extra.parseObjectProperty);
8169 parseObjectPropertyKey = wrapTracking(extra.parseObjectPropertyKey);
8170 parsePostfixExpression = wrapTracking(extra.parsePostfixExpression);
8171 parsePrimaryExpression = wrapTracking(extra.parsePrimaryExpression);
8172 parseProgram = wrapTracking(extra.parseProgram);
8173 parsePropertyFunction = wrapTracking(extra.parsePropertyFunction);
8174 parseRelationalExpression = wrapTracking(extra.parseRelationalExpression);
8175 parseStatement = wrapTracking(extra.parseStatement);
8176 parseShiftExpression = wrapTracking(extra.parseShiftExpression);
8177 parseSwitchCase = wrapTracking(extra.parseSwitchCase);
8178 parseUnaryExpression = wrapTracking(extra.parseUnaryExpression);
8179 parseVariableDeclaration = wrapTracking(extra.parseVariableDeclaration);
8180 parseVariableIdentifier = wrapTracking(extra.parseVariableIdentifier);
8183 if (typeof extra.tokens !== 'undefined') {
8184 extra.advance = advance;
8185 extra.scanRegExp = scanRegExp;
8187 advance = collectToken;
8188 scanRegExp = collectRegex;
8192 function unpatch() {
8193 if (typeof extra.skipComment === 'function') {
8194 skipComment = extra.skipComment;
8198 createLiteral = extra.createLiteral;
8201 if (extra.range || extra.loc) {
8202 parseAdditiveExpression = extra.parseAdditiveExpression;
8203 parseAssignmentExpression = extra.parseAssignmentExpression;
8204 parseBitwiseANDExpression = extra.parseBitwiseANDExpression;
8205 parseBitwiseORExpression = extra.parseBitwiseORExpression;
8206 parseBitwiseXORExpression = extra.parseBitwiseXORExpression;
8207 parseBlock = extra.parseBlock;
8208 parseFunctionSourceElements = extra.parseFunctionSourceElements;
8209 parseCatchClause = extra.parseCatchClause;
8210 parseComputedMember = extra.parseComputedMember;
8211 parseConditionalExpression = extra.parseConditionalExpression;
8212 parseConstLetDeclaration = extra.parseConstLetDeclaration;
8213 parseEqualityExpression = extra.parseEqualityExpression;
8214 parseExpression = extra.parseExpression;
8215 parseForVariableDeclaration = extra.parseForVariableDeclaration;
8216 parseFunctionDeclaration = extra.parseFunctionDeclaration;
8217 parseFunctionExpression = extra.parseFunctionExpression;
8218 parseGroupExpression = extra.parseGroupExpression;
8219 parseLeftHandSideExpression = extra.parseLeftHandSideExpression;
8220 parseLeftHandSideExpressionAllowCall = extra.parseLeftHandSideExpressionAllowCall;
8221 parseLogicalANDExpression = extra.parseLogicalANDExpression;
8222 parseLogicalORExpression = extra.parseLogicalORExpression;
8223 parseMultiplicativeExpression = extra.parseMultiplicativeExpression;
8224 parseNewExpression = extra.parseNewExpression;
8225 parseNonComputedProperty = extra.parseNonComputedProperty;
8226 parseObjectProperty = extra.parseObjectProperty;
8227 parseObjectPropertyKey = extra.parseObjectPropertyKey;
8228 parsePrimaryExpression = extra.parsePrimaryExpression;
8229 parsePostfixExpression = extra.parsePostfixExpression;
8230 parseProgram = extra.parseProgram;
8231 parsePropertyFunction = extra.parsePropertyFunction;
8232 parseRelationalExpression = extra.parseRelationalExpression;
8233 parseStatement = extra.parseStatement;
8234 parseShiftExpression = extra.parseShiftExpression;
8235 parseSwitchCase = extra.parseSwitchCase;
8236 parseUnaryExpression = extra.parseUnaryExpression;
8237 parseVariableDeclaration = extra.parseVariableDeclaration;
8238 parseVariableIdentifier = extra.parseVariableIdentifier;
8241 if (typeof extra.scanRegExp === 'function') {
8242 advance = extra.advance;
8243 scanRegExp = extra.scanRegExp;
8247 function stringToArray(str) {
8248 var length = str.length,
8251 for (i = 0; i < length; ++i) {
8252 result[i] = str.charAt(i);
8257 function parse(code, options) {
8258 var program, toString;
8261 if (typeof code !== 'string' && !(code instanceof String)) {
8262 code = toString(code);
8267 lineNumber = (source.length > 0) ? 1 : 0;
8269 length = source.length;
8274 inFunctionBody: false,
8280 if (typeof options !== 'undefined') {
8281 extra.range = (typeof options.range === 'boolean') && options.range;
8282 extra.loc = (typeof options.loc === 'boolean') && options.loc;
8283 extra.raw = (typeof options.raw === 'boolean') && options.raw;
8284 if (typeof options.tokens === 'boolean' && options.tokens) {
8287 if (typeof options.comment === 'boolean' && options.comment) {
8288 extra.comments = [];
8290 if (typeof options.tolerant === 'boolean' && options.tolerant) {
8296 if (typeof source[0] === 'undefined') {
8297 // Try first to convert to a string. This is good as fast path
8298 // for old IE which understands string indexing for string
8299 // literals only and not for string object.
8300 if (code instanceof String) {
8301 source = code.valueOf();
8304 // Force accessing the characters via an array.
8305 if (typeof source[0] === 'undefined') {
8306 source = stringToArray(code);
8313 program = parseProgram();
8314 if (typeof extra.comments !== 'undefined') {
8315 filterCommentLocation();
8316 program.comments = extra.comments;
8318 if (typeof extra.tokens !== 'undefined') {
8319 filterTokenLocation();
8320 program.tokens = extra.tokens;
8322 if (typeof extra.errors !== 'undefined') {
8323 program.errors = extra.errors;
8325 if (extra.range || extra.loc) {
8326 program.body = filterGroup(program.body);
8338 // Sync with package.json.
8339 exports.version = '1.0.3';
8341 exports.parse = parse;
8344 exports.Syntax = (function () {
8345 var name, types = {};
8347 if (typeof Object.create === 'function') {
8348 types = Object.create(null);
8351 for (name in Syntax) {
8352 if (Syntax.hasOwnProperty(name)) {
8353 types[name] = Syntax[name];
8357 if (typeof Object.freeze === 'function') {
8358 Object.freeze(types);
8365 /* vim: set sw=4 ts=4 et tw=80 : */
8367 * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved.
8368 * Available via the MIT or new BSD license.
8369 * see: http://github.com/jrburke/requirejs for details
8372 /*global define, Reflect */
8375 * xpcshell has a smaller stack on linux and windows (1MB vs 9MB on mac),
8376 * and the recursive nature of esprima can cause it to overflow pretty
8377 * quickly. So favor it built in Reflect parser:
8378 * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
8380 define('esprimaAdapter', ['./esprima', 'env'], function (esprima, env) {
8381 if (env.get() === 'xpconnect' && typeof Reflect !== 'undefined') {
8387 define('uglifyjs/consolidator', ["require", "exports", "module", "./parse-js", "./process"], function(require, exports, module) {
8389 * @preserve Copyright 2012 Robert Gust-Bardon <http://robert.gust-bardon.org/>.
8390 * All rights reserved.
8392 * Redistribution and use in source and binary forms, with or without
8393 * modification, are permitted provided that the following conditions
8396 * * Redistributions of source code must retain the above
8397 * copyright notice, this list of conditions and the following
8400 * * Redistributions in binary form must reproduce the above
8401 * copyright notice, this list of conditions and the following
8402 * disclaimer in the documentation and/or other materials
8403 * provided with the distribution.
8405 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
8406 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
8407 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
8408 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
8409 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
8410 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
8411 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
8412 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
8413 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
8414 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
8415 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
8420 * @fileoverview Enhances <a href="https://github.com/mishoo/UglifyJS/"
8421 * >UglifyJS</a> with consolidation of null, Boolean, and String values.
8422 * <p>Also known as aliasing, this feature has been deprecated in <a href=
8423 * "http://closure-compiler.googlecode.com/">the Closure Compiler</a> since its
8424 * initial release, where it is unavailable from the <abbr title=
8425 * "command line interface">CLI</a>. The Closure Compiler allows one to log and
8426 * influence this process. In contrast, this implementation does not introduce
8427 * any variable declarations in global code and derives String values from
8428 * identifier names used as property accessors.</p>
8429 * <p>Consolidating literals may worsen the data compression ratio when an <a
8430 * href="http://tools.ietf.org/html/rfc2616#section-3.5">encoding
8431 * transformation</a> is applied. For instance, <a href=
8432 * "http://code.jquery.com/jquery-1.7.1.js">jQuery 1.7.1</a> takes 248235 bytes.
8433 * Building it with <a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5">
8434 * UglifyJS v1.2.5</a> results in 93647 bytes (37.73% of the original) which are
8435 * then compressed to 33154 bytes (13.36% of the original) using <a href=
8436 * "http://linux.die.net/man/1/gzip">gzip(1)</a>. Building it with the same
8437 * version of UglifyJS 1.2.5 patched with the implementation of consolidation
8438 * results in 80784 bytes (a decrease of 12863 bytes, i.e. 13.74%, in comparison
8439 * to the aforementioned 93647 bytes) which are then compressed to 34013 bytes
8440 * (an increase of 859 bytes, i.e. 2.59%, in comparison to the aforementioned
8442 * <p>Written in <a href="http://es5.github.com/#x4.2.2">the strict variant</a>
8443 * of <a href="http://es5.github.com/">ECMA-262 5.1 Edition</a>. Encoded in <a
8444 * href="http://tools.ietf.org/html/rfc3629">UTF-8</a>. Follows <a href=
8445 * "http://google-styleguide.googlecode.com/svn-history/r76/trunk/javascriptguide.xml"
8446 * >Revision 2.28 of the Google JavaScript Style Guide</a> (except for the
8447 * discouraged use of the {@code function} tag and the {@code namespace} tag).
8448 * 100% typed for the <a href=
8449 * "http://closure-compiler.googlecode.com/files/compiler-20120123.tar.gz"
8450 * >Closure Compiler Version 1741</a>.</p>
8451 * <p>Should you find this software useful, please consider <a href=
8452 * "https://paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JZLW72X8FD4WG"
8453 * >a donation</a>.</p>
8454 * @author follow.me@RGustBardon (Robert Gust-Bardon)
8455 * @supported Tested with:
8457 * <li><a href="http://nodejs.org/dist/v0.6.10/">Node v0.6.10</a>,</li>
8458 * <li><a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5">UglifyJS
8463 /*global console:false, exports:true, module:false, require:false */
8464 /*jshint sub:true */
8466 * Consolidates null, Boolean, and String values found inside an <abbr title=
8467 * "abstract syntax tree">AST</abbr>.
8468 * @param {!TSyntacticCodeUnit} oAbstractSyntaxTree An array-like object
8469 * representing an <abbr title="abstract syntax tree">AST</abbr>.
8470 * @return {!TSyntacticCodeUnit} An array-like object representing an <abbr
8471 * title="abstract syntax tree">AST</abbr> with its null, Boolean, and
8472 * String values consolidated.
8474 // TODO(user) Consolidation of mathematical values found in numeric literals.
8475 // TODO(user) Unconsolidation.
8476 // TODO(user) Consolidation of ECMA-262 6th Edition programs.
8477 // TODO(user) Rewrite in ECMA-262 6th Edition.
8478 exports['ast_consolidate'] = function(oAbstractSyntaxTree) {
8480 /*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, immed:true,
8481 latedef:true, newcap:true, noarge:true, noempty:true, nonew:true,
8482 onevar:true, plusplus:true, regexp:true, undef:true, strict:true,
8483 sub:false, trailing:true */
8487 * A record consisting of data about one or more source elements.
8491 TSourceElementsData = function() {
8493 * The category of the elements.
8495 * @see ESourceElementCategories
8497 this.nCategory = ESourceElementCategories.N_OTHER;
8499 * The number of occurrences (within the elements) of each primitive
8500 * value that could be consolidated.
8501 * @type {!Array.<!Object.<string, number>>}
8504 this.aCount[EPrimaryExpressionCategories.N_IDENTIFIER_NAMES] = {};
8505 this.aCount[EPrimaryExpressionCategories.N_STRING_LITERALS] = {};
8506 this.aCount[EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS] =
8509 * Identifier names found within the elements.
8510 * @type {!Array.<string>}
8512 this.aIdentifiers = [];
8514 * Prefixed representation Strings of each primitive value that could be
8515 * consolidated within the elements.
8516 * @type {!Array.<string>}
8518 this.aPrimitiveValues = [];
8521 * A record consisting of data about a primitive value that could be
8526 TPrimitiveValue = function() {
8528 * The difference in the number of terminal symbols between the original
8529 * source text and the one with the primitive value consolidated. If the
8530 * difference is positive, the primitive value is considered worthwhile.
8535 * An identifier name of the variable that will be declared and assigned
8536 * the primitive value if the primitive value is consolidated.
8542 * A record consisting of data on what to consolidate within the range of
8543 * source elements that is currently being considered.
8547 TSolution = function() {
8549 * An object whose keys are prefixed representation Strings of each
8550 * primitive value that could be consolidated within the elements and
8551 * whose values are corresponding data about those primitive values.
8552 * @type {!Object.<string, {nSaving: number, sName: string}>}
8553 * @see TPrimitiveValue
8555 this.oPrimitiveValues = {};
8557 * The difference in the number of terminal symbols between the original
8558 * source text and the one with all the worthwhile primitive values
8561 * @see TPrimitiveValue#nSaving
8566 * The processor of <abbr title="abstract syntax tree">AST</abbr>s found
8569 * @type {!TProcessor}
8571 oProcessor = (/** @type {!TProcessor} */ require('./process')),
8573 * A record consisting of a number of constants that represent the
8574 * difference in the number of terminal symbols between a source text with
8575 * a modified syntactic code unit and the original one.
8577 * @type {!Object.<string, number>}
8581 * The difference in the number of punctuators required by the bracket
8582 * notation and the dot notation.
8583 * <p><code>'[]'.length - '.'.length</code></p>
8587 N_PROPERTY_ACCESSOR: 1,
8589 * The number of punctuators required by a variable declaration with an
8591 * <p><code>':'.length + ';'.length</code></p>
8595 N_VARIABLE_DECLARATION: 2,
8597 * The number of terminal symbols required to introduce a variable
8598 * statement (excluding its variable declaration list).
8599 * <p><code>'var '.length</code></p>
8603 N_VARIABLE_STATEMENT_AFFIXATION: 4,
8605 * The number of terminal symbols needed to enclose source elements
8606 * within a function call with no argument values to a function with an
8607 * empty parameter list.
8608 * <p><code>'(function(){}());'.length</code></p>
8615 * Categories of primary expressions from which primitive values that
8616 * could be consolidated are derivable.
8620 EPrimaryExpressionCategories = {
8622 * Identifier names used as property accessors.
8625 N_IDENTIFIER_NAMES: 0,
8630 N_STRING_LITERALS: 1,
8632 * Null and Boolean literals.
8635 N_NULL_AND_BOOLEAN_LITERALS: 2
8638 * Prefixes of primitive values that could be consolidated.
8639 * The String values of the prefixes must have same number of characters.
8640 * The prefixes must not be used in any properties defined in any version
8642 * "http://www.ecma-international.org/publications/standards/Ecma-262.htm"
8649 * Identifies String values.
8654 * Identifies null and Boolean values.
8660 * Categories of source elements in terms of their appropriateness of
8661 * having their primitive values consolidated.
8665 ESourceElementCategories = {
8667 * Identifies a source element that includes the <a href=
8668 * "http://es5.github.com/#x12.10">{@code with}</a> statement.
8673 * Identifies a source element that includes the <a href=
8674 * "http://es5.github.com/#x15.1.2.1">{@code eval}</a> identifier name.
8679 * Identifies a source element that must be excluded from the process
8680 * unless its whole scope is examined.
8685 * Identifies source elements not posing any problems.
8691 * The list of literals (other than the String ones) whose primitive
8692 * values can be consolidated.
8694 * @type {!Array.<string>}
8696 A_OTHER_SUBSTITUTABLE_LITERALS = [
8697 'null', // The null literal.
8698 'false', // The Boolean literal {@code false}.
8699 'true' // The Boolean literal {@code true}.
8703 * Consolidates all worthwhile primitive values in a syntactic code unit.
8704 * @param {!TSyntacticCodeUnit} oSyntacticCodeUnit An array-like object
8705 * representing the branch of the abstract syntax tree representing the
8706 * syntactic code unit along with its scope.
8707 * @see TPrimitiveValue#nSaving
8709 function fExamineSyntacticCodeUnit(oSyntacticCodeUnit) {
8712 * Indicates whether the syntactic code unit represents global code.
8715 bIsGlobal = 'toplevel' === oSyntacticCodeUnit[0],
8717 * Indicates whether the whole scope is being examined.
8720 bIsWhollyExaminable = !bIsGlobal,
8722 * An array-like object representing source elements that constitute a
8723 * syntactic code unit.
8724 * @type {!TSyntacticCodeUnit}
8728 * A record consisting of data about the source element that is
8729 * currently being examined.
8730 * @type {!TSourceElementsData}
8734 * The scope of the syntactic code unit.
8739 * An instance of an object that allows the traversal of an <abbr
8740 * title="abstract syntax tree">AST</abbr>.
8745 * An object encompassing collections of functions used during the
8746 * traversal of an <abbr title="abstract syntax tree">AST</abbr>.
8748 * @type {!Object.<string, !Object.<string, function(...[*])>>}
8752 * A collection of functions used during the surveyance of source
8755 * @type {!Object.<string, function(...[*])>}
8757 oSurveySourceElement: {
8758 /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
8760 * Classifies the source element as excludable if it does not
8761 * contain a {@code with} statement or the {@code eval} identifier
8762 * name. Adds the identifier of the function and its formal
8763 * parameters to the list of identifier names found.
8764 * @param {string} sIdentifier The identifier of the function.
8765 * @param {!Array.<string>} aFormalParameterList Formal parameters.
8766 * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
8770 aFormalParameterList,
8772 fClassifyAsExcludable();
8773 fAddIdentifier(sIdentifier);
8774 aFormalParameterList.forEach(fAddIdentifier);
8777 * Increments the count of the number of occurrences of the String
8778 * value that is equivalent to the sequence of terminal symbols
8779 * that constitute the encountered identifier name.
8780 * @param {!TSyntacticCodeUnit} oExpression The nonterminal
8782 * @param {string} sIdentifierName The identifier name used as the
8783 * property accessor.
8784 * @return {!Array} The encountered branch of an <abbr title=
8785 * "abstract syntax tree">AST</abbr> with its nonterminal
8786 * MemberExpression traversed.
8788 'dot': function(oExpression, sIdentifierName) {
8789 fCountPrimaryExpression(
8790 EPrimaryExpressionCategories.N_IDENTIFIER_NAMES,
8791 EValuePrefixes.S_STRING + sIdentifierName);
8792 return ['dot', oWalker.walk(oExpression), sIdentifierName];
8795 * Adds the optional identifier of the function and its formal
8796 * parameters to the list of identifier names found.
8797 * @param {?string} sIdentifier The optional identifier of the
8799 * @param {!Array.<string>} aFormalParameterList Formal parameters.
8800 * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
8802 'function': function(
8804 aFormalParameterList,
8806 if ('string' === typeof sIdentifier) {
8807 fAddIdentifier(sIdentifier);
8809 aFormalParameterList.forEach(fAddIdentifier);
8812 * Either increments the count of the number of occurrences of the
8813 * encountered null or Boolean value or classifies a source element
8814 * as containing the {@code eval} identifier name.
8815 * @param {string} sIdentifier The identifier encountered.
8817 'name': function(sIdentifier) {
8818 if (-1 !== A_OTHER_SUBSTITUTABLE_LITERALS.indexOf(sIdentifier)) {
8819 fCountPrimaryExpression(
8820 EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS,
8821 EValuePrefixes.S_SYMBOLIC + sIdentifier);
8823 if ('eval' === sIdentifier) {
8824 oSourceElementData.nCategory =
8825 ESourceElementCategories.N_EVAL;
8827 fAddIdentifier(sIdentifier);
8831 * Classifies the source element as excludable if it does not
8832 * contain a {@code with} statement or the {@code eval} identifier
8834 * @param {TSyntacticCodeUnit} oExpression The expression whose
8835 * value is to be returned.
8837 'return': function(oExpression) {
8838 fClassifyAsExcludable();
8841 * Increments the count of the number of occurrences of the
8842 * encountered String value.
8843 * @param {string} sStringValue The String value of the string
8844 * literal encountered.
8846 'string': function(sStringValue) {
8847 if (sStringValue.length > 0) {
8848 fCountPrimaryExpression(
8849 EPrimaryExpressionCategories.N_STRING_LITERALS,
8850 EValuePrefixes.S_STRING + sStringValue);
8854 * Adds the identifier reserved for an exception to the list of
8855 * identifier names found.
8856 * @param {!TSyntacticCodeUnit} oTry A block of code in which an
8857 * exception can occur.
8858 * @param {Array} aCatch The identifier reserved for an exception
8859 * and a block of code to handle the exception.
8860 * @param {TSyntacticCodeUnit} oFinally An optional block of code
8861 * to be evaluated regardless of whether an exception occurs.
8863 'try': function(oTry, aCatch, oFinally) {
8864 if (Array.isArray(aCatch)) {
8865 fAddIdentifier(aCatch[0]);
8869 * Classifies the source element as excludable if it does not
8870 * contain a {@code with} statement or the {@code eval} identifier
8871 * name. Adds the identifier of each declared variable to the list
8872 * of identifier names found.
8873 * @param {!Array.<!Array>} aVariableDeclarationList Variable
8876 'var': function(aVariableDeclarationList) {
8877 fClassifyAsExcludable();
8878 aVariableDeclarationList.forEach(fAddVariable);
8881 * Classifies a source element as containing the {@code with}
8883 * @param {!TSyntacticCodeUnit} oExpression An expression whose
8884 * value is to be converted to a value of type Object and
8885 * become the binding object of a new object environment
8886 * record of a new lexical environment in which the statement
8887 * is to be executed.
8888 * @param {!TSyntacticCodeUnit} oStatement The statement to be
8889 * executed in the augmented lexical environment.
8890 * @return {!Array} An empty array to stop the traversal.
8892 'with': function(oExpression, oStatement) {
8893 oSourceElementData.nCategory = ESourceElementCategories.N_WITH;
8896 /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
8899 * A collection of functions used while looking for nested functions.
8901 * @type {!Object.<string, function(...[*])>}
8903 oExamineFunctions: {
8904 /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
8906 * Orders an examination of a nested function declaration.
8907 * @this {!TSyntacticCodeUnit} An array-like object representing
8908 * the branch of an <abbr title="abstract syntax tree"
8909 * >AST</abbr> representing the syntactic code unit along with
8911 * @return {!Array} An empty array to stop the traversal.
8913 'defun': function() {
8914 fExamineSyntacticCodeUnit(this);
8918 * Orders an examination of a nested function expression.
8919 * @this {!TSyntacticCodeUnit} An array-like object representing
8920 * the branch of an <abbr title="abstract syntax tree"
8921 * >AST</abbr> representing the syntactic code unit along with
8923 * @return {!Array} An empty array to stop the traversal.
8925 'function': function() {
8926 fExamineSyntacticCodeUnit(this);
8929 /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
8933 * Records containing data about source elements.
8934 * @type {Array.<TSourceElementsData>}
8936 aSourceElementsData = [],
8938 * The index (in the source text order) of the source element
8939 * immediately following a <a href="http://es5.github.com/#x14.1"
8940 * >Directive Prologue</a>.
8943 nAfterDirectivePrologue = 0,
8945 * The index (in the source text order) of the source element that is
8946 * currently being considered.
8951 * The index (in the source text order) of the source element that is
8952 * the last element of the range of source elements that is currently
8954 * @type {(undefined|number)}
8958 * Initiates the traversal of a source element.
8959 * @param {!TWalker} oWalker An instance of an object that allows the
8960 * traversal of an abstract syntax tree.
8961 * @param {!TSyntacticCodeUnit} oSourceElement A source element from
8962 * which the traversal should commence.
8963 * @return {function(): !TSyntacticCodeUnit} A function that is able to
8964 * initiate the traversal from a given source element.
8966 cContext = function(oWalker, oSourceElement) {
8968 * @return {!TSyntacticCodeUnit} A function that is able to
8969 * initiate the traversal from a given source element.
8971 var fLambda = function() {
8972 return oWalker.walk(oSourceElement);
8978 * Classifies the source element as excludable if it does not
8979 * contain a {@code with} statement or the {@code eval} identifier
8982 fClassifyAsExcludable = function() {
8983 if (oSourceElementData.nCategory ===
8984 ESourceElementCategories.N_OTHER) {
8985 oSourceElementData.nCategory =
8986 ESourceElementCategories.N_EXCLUDABLE;
8990 * Adds an identifier to the list of identifier names found.
8991 * @param {string} sIdentifier The identifier to be added.
8993 fAddIdentifier = function(sIdentifier) {
8994 if (-1 === oSourceElementData.aIdentifiers.indexOf(sIdentifier)) {
8995 oSourceElementData.aIdentifiers.push(sIdentifier);
8999 * Adds the identifier of a variable to the list of identifier names
9001 * @param {!Array} aVariableDeclaration A variable declaration.
9003 fAddVariable = function(aVariableDeclaration) {
9004 fAddIdentifier(/** @type {string} */ aVariableDeclaration[0]);
9007 * Increments the count of the number of occurrences of the prefixed
9008 * String representation attributed to the primary expression.
9009 * @param {number} nCategory The category of the primary expression.
9010 * @param {string} sName The prefixed String representation attributed
9011 * to the primary expression.
9013 fCountPrimaryExpression = function(nCategory, sName) {
9014 if (!oSourceElementData.aCount[nCategory].hasOwnProperty(sName)) {
9015 oSourceElementData.aCount[nCategory][sName] = 0;
9016 if (-1 === oSourceElementData.aPrimitiveValues.indexOf(sName)) {
9017 oSourceElementData.aPrimitiveValues.push(sName);
9020 oSourceElementData.aCount[nCategory][sName] += 1;
9023 * Consolidates all worthwhile primitive values in a range of source
9025 * @param {number} nFrom The index (in the source text order) of the
9026 * source element that is the first element of the range.
9027 * @param {number} nTo The index (in the source text order) of the
9028 * source element that is the last element of the range.
9029 * @param {boolean} bEnclose Indicates whether the range should be
9030 * enclosed within a function call with no argument values to a
9031 * function with an empty parameter list if any primitive values
9033 * @see TPrimitiveValue#nSaving
9035 fExamineSourceElements = function(nFrom, nTo, bEnclose) {
9038 * The index of the last mangled name.
9041 nIndex = oScope.cname,
9043 * The index of the source element that is currently being
9049 * A collection of functions used during the consolidation of
9050 * primitive values and identifier names used as property
9053 * @type {!Object.<string, function(...[*])>}
9055 oWalkersTransformers = {
9057 * If the String value that is equivalent to the sequence of
9058 * terminal symbols that constitute the encountered identifier
9059 * name is worthwhile, a syntactic conversion from the dot
9060 * notation to the bracket notation ensues with that sequence
9061 * being substituted by an identifier name to which the value
9063 * Applies to property accessors that use the dot notation.
9064 * @param {!TSyntacticCodeUnit} oExpression The nonterminal
9066 * @param {string} sIdentifierName The identifier name used as
9067 * the property accessor.
9068 * @return {!Array} A syntactic code unit that is equivalent to
9069 * the one encountered.
9070 * @see TPrimitiveValue#nSaving
9072 'dot': function(oExpression, sIdentifierName) {
9074 * The prefixed String value that is equivalent to the
9075 * sequence of terminal symbols that constitute the
9076 * encountered identifier name.
9079 var sPrefixed = EValuePrefixes.S_STRING + sIdentifierName;
9081 return oSolutionBest.oPrimitiveValues.hasOwnProperty(
9083 oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
9085 oWalker.walk(oExpression),
9087 oSolutionBest.oPrimitiveValues[sPrefixed].sName]] :
9088 ['dot', oWalker.walk(oExpression), sIdentifierName];
9091 * If the encountered identifier is a null or Boolean literal
9092 * and its value is worthwhile, the identifier is substituted
9093 * by an identifier name to which that value is assigned.
9094 * Applies to identifier names.
9095 * @param {string} sIdentifier The identifier encountered.
9096 * @return {!Array} A syntactic code unit that is equivalent to
9097 * the one encountered.
9098 * @see TPrimitiveValue#nSaving
9100 'name': function(sIdentifier) {
9102 * The prefixed representation String of the identifier.
9105 var sPrefixed = EValuePrefixes.S_SYMBOLIC + sIdentifier;
9109 oSolutionBest.oPrimitiveValues.hasOwnProperty(sPrefixed) &&
9110 oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
9111 oSolutionBest.oPrimitiveValues[sPrefixed].sName :
9116 * If the encountered String value is worthwhile, it is
9117 * substituted by an identifier name to which that value is
9119 * Applies to String values.
9120 * @param {string} sStringValue The String value of the string
9121 * literal encountered.
9122 * @return {!Array} A syntactic code unit that is equivalent to
9123 * the one encountered.
9124 * @see TPrimitiveValue#nSaving
9126 'string': function(sStringValue) {
9128 * The prefixed representation String of the primitive value
9133 EValuePrefixes.S_STRING + sStringValue;
9135 return oSolutionBest.oPrimitiveValues.hasOwnProperty(
9137 oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
9139 oSolutionBest.oPrimitiveValues[sPrefixed].sName] :
9140 ['string', sStringValue];
9144 * Such data on what to consolidate within the range of source
9145 * elements that is currently being considered that lead to the
9146 * greatest known reduction of the number of the terminal symbols
9147 * in comparison to the original source text.
9148 * @type {!TSolution}
9150 oSolutionBest = new TSolution(),
9152 * Data representing an ongoing attempt to find a better
9153 * reduction of the number of the terminal symbols in comparison
9154 * to the original source text than the best one that is
9156 * @type {!TSolution}
9157 * @see oSolutionBest
9159 oSolutionCandidate = new TSolution(),
9161 * A record consisting of data about the range of source elements
9162 * that is currently being examined.
9163 * @type {!TSourceElementsData}
9165 oSourceElementsData = new TSourceElementsData(),
9167 * Variable declarations for each primitive value that is to be
9168 * consolidated within the elements.
9169 * @type {!Array.<!Array>}
9171 aVariableDeclarations = [],
9173 * Augments a list with a prefixed representation String.
9174 * @param {!Array.<string>} aList A list that is to be augmented.
9175 * @return {function(string)} A function that augments a list
9176 * with a prefixed representation String.
9178 cAugmentList = function(aList) {
9180 * @param {string} sPrefixed Prefixed representation String of
9181 * a primitive value that could be consolidated within the
9184 var fLambda = function(sPrefixed) {
9185 if (-1 === aList.indexOf(sPrefixed)) {
9186 aList.push(sPrefixed);
9193 * Adds the number of occurrences of a primitive value of a given
9194 * category that could be consolidated in the source element with
9195 * a given index to the count of occurrences of that primitive
9196 * value within the range of source elements that is currently
9198 * @param {number} nPosition The index (in the source text order)
9199 * of a source element.
9200 * @param {number} nCategory The category of the primary
9201 * expression from which the primitive value is derived.
9202 * @return {function(string)} A function that performs the
9204 * @see cAddOccurrencesInCategory
9206 cAddOccurrences = function(nPosition, nCategory) {
9208 * @param {string} sPrefixed The prefixed representation String
9209 * of a primitive value.
9211 var fLambda = function(sPrefixed) {
9212 if (!oSourceElementsData.aCount[nCategory].hasOwnProperty(
9214 oSourceElementsData.aCount[nCategory][sPrefixed] = 0;
9216 oSourceElementsData.aCount[nCategory][sPrefixed] +=
9217 aSourceElementsData[nPosition].aCount[nCategory][
9224 * Adds the number of occurrences of each primitive value of a
9225 * given category that could be consolidated in the source
9226 * element with a given index to the count of occurrences of that
9227 * primitive values within the range of source elements that is
9228 * currently being considered.
9229 * @param {number} nPosition The index (in the source text order)
9230 * of a source element.
9231 * @return {function(number)} A function that performs the
9233 * @see fAddOccurrences
9235 cAddOccurrencesInCategory = function(nPosition) {
9237 * @param {number} nCategory The category of the primary
9238 * expression from which the primitive value is derived.
9240 var fLambda = function(nCategory) {
9242 aSourceElementsData[nPosition].aCount[nCategory]
9243 ).forEach(cAddOccurrences(nPosition, nCategory));
9249 * Adds the number of occurrences of each primitive value that
9250 * could be consolidated in the source element with a given index
9251 * to the count of occurrences of that primitive values within
9252 * the range of source elements that is currently being
9254 * @param {number} nPosition The index (in the source text order)
9255 * of a source element.
9257 fAddOccurrences = function(nPosition) {
9258 Object.keys(aSourceElementsData[nPosition].aCount).forEach(
9259 cAddOccurrencesInCategory(nPosition));
9262 * Creates a variable declaration for a primitive value if that
9263 * primitive value is to be consolidated within the elements.
9264 * @param {string} sPrefixed Prefixed representation String of a
9265 * primitive value that could be consolidated within the
9267 * @see aVariableDeclarations
9269 cAugmentVariableDeclarations = function(sPrefixed) {
9270 if (oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0) {
9271 aVariableDeclarations.push([
9272 oSolutionBest.oPrimitiveValues[sPrefixed].sName,
9273 [0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC) ?
9275 sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length)]
9280 * Sorts primitive values with regard to the difference in the
9281 * number of terminal symbols between the original source text
9282 * and the one with those primitive values consolidated.
9283 * @param {string} sPrefixed0 The prefixed representation String
9284 * of the first of the two primitive values that are being
9286 * @param {string} sPrefixed1 The prefixed representation String
9287 * of the second of the two primitive values that are being
9292 * <dd>if the first primitive value must be placed before
9293 * the other one,</dd>
9295 * <dd>if the first primitive value may be placed before
9296 * the other one,</dd>
9298 * <dd>if the first primitive value must not be placed
9299 * before the other one.</dd>
9301 * @see TSolution.oPrimitiveValues
9303 cSortPrimitiveValues = function(sPrefixed0, sPrefixed1) {
9305 * The difference between:
9307 * <li>the difference in the number of terminal symbols
9308 * between the original source text and the one with the
9309 * first primitive value consolidated, and</li>
9310 * <li>the difference in the number of terminal symbols
9311 * between the original source text and the one with the
9312 * second primitive value consolidated.</li>
9317 oSolutionCandidate.oPrimitiveValues[sPrefixed0].nSaving -
9318 oSolutionCandidate.oPrimitiveValues[sPrefixed1].nSaving;
9320 return nDifference > 0 ? -1 : nDifference < 0 ? 1 : 0;
9323 * Assigns an identifier name to a primitive value and calculates
9324 * whether instances of that primitive value are worth
9326 * @param {string} sPrefixed The prefixed representation String
9327 * of a primitive value that is being evaluated.
9329 fEvaluatePrimitiveValue = function(sPrefixed) {
9332 * The index of the last mangled name.
9337 * The representation String of the primitive value that is
9342 sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length),
9344 * The number of source characters taken up by the
9345 * representation String of the primitive value that is
9349 nLengthOriginal = sName.length,
9351 * The number of source characters taken up by the
9352 * identifier name that could substitute the primitive
9353 * value that is being evaluated.
9357 nLengthSubstitution,
9359 * The number of source characters taken up by by the
9360 * representation String of the primitive value that is
9361 * being evaluated when it is represented by a string
9365 nLengthString = oProcessor.make_string(sName).length;
9367 oSolutionCandidate.oPrimitiveValues[sPrefixed] =
9368 new TPrimitiveValue();
9369 do { // Find an identifier unused in this or any nested scope.
9370 nIndex = oScope.cname;
9371 oSolutionCandidate.oPrimitiveValues[sPrefixed].sName =
9372 oScope.next_mangled();
9373 } while (-1 !== oSourceElementsData.aIdentifiers.indexOf(
9374 oSolutionCandidate.oPrimitiveValues[sPrefixed].sName));
9375 nLengthSubstitution = oSolutionCandidate.oPrimitiveValues[
9376 sPrefixed].sName.length;
9377 if (0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC)) {
9378 // foo:null, or foo:null;
9379 oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -=
9380 nLengthSubstitution + nLengthOriginal +
9381 oWeights.N_VARIABLE_DECLARATION;
9383 oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
9384 oSourceElementsData.aCount[
9385 EPrimaryExpressionCategories.
9386 N_NULL_AND_BOOLEAN_LITERALS][sPrefixed] *
9387 (nLengthOriginal - nLengthSubstitution);
9389 // foo:'fromCharCode';
9390 oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -=
9391 nLengthSubstitution + nLengthString +
9392 oWeights.N_VARIABLE_DECLARATION;
9393 // .fromCharCode vs [foo]
9394 if (oSourceElementsData.aCount[
9395 EPrimaryExpressionCategories.N_IDENTIFIER_NAMES
9396 ].hasOwnProperty(sPrefixed)) {
9397 oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
9398 oSourceElementsData.aCount[
9399 EPrimaryExpressionCategories.N_IDENTIFIER_NAMES
9401 (nLengthOriginal - nLengthSubstitution -
9402 oWeights.N_PROPERTY_ACCESSOR);
9404 // 'fromCharCode' vs foo
9405 if (oSourceElementsData.aCount[
9406 EPrimaryExpressionCategories.N_STRING_LITERALS
9407 ].hasOwnProperty(sPrefixed)) {
9408 oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
9409 oSourceElementsData.aCount[
9410 EPrimaryExpressionCategories.N_STRING_LITERALS
9412 (nLengthString - nLengthSubstitution);
9415 if (oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving >
9417 oSolutionCandidate.nSavings +=
9418 oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving;
9420 oScope.cname = nIndex; // Free the identifier name.
9424 * Adds a variable declaration to an existing variable statement.
9425 * @param {!Array} aVariableDeclaration A variable declaration
9426 * with an initialiser.
9428 cAddVariableDeclaration = function(aVariableDeclaration) {
9429 (/** @type {!Array} */ oSourceElements[nFrom][1]).unshift(
9430 aVariableDeclaration);
9436 // If the range is a closure, reuse the closure.
9437 if (nFrom === nTo &&
9438 'stat' === oSourceElements[nFrom][0] &&
9439 'call' === oSourceElements[nFrom][1][0] &&
9440 'function' === oSourceElements[nFrom][1][1][0]) {
9441 fExamineSyntacticCodeUnit(oSourceElements[nFrom][1][1]);
9444 // Create a list of all derived primitive values within the range.
9445 for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
9446 aSourceElementsData[nPosition].aPrimitiveValues.forEach(
9447 cAugmentList(oSourceElementsData.aPrimitiveValues));
9449 if (0 === oSourceElementsData.aPrimitiveValues.length) {
9452 for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
9453 // Add the number of occurrences to the total count.
9454 fAddOccurrences(nPosition);
9455 // Add identifiers of this or any nested scope to the list.
9456 aSourceElementsData[nPosition].aIdentifiers.forEach(
9457 cAugmentList(oSourceElementsData.aIdentifiers));
9459 // Distribute identifier names among derived primitive values.
9460 do { // If there was any progress, find a better distribution.
9461 oSolutionBest = oSolutionCandidate;
9462 if (Object.keys(oSolutionCandidate.oPrimitiveValues).length > 0) {
9463 // Sort primitive values descending by their worthwhileness.
9464 oSourceElementsData.aPrimitiveValues.sort(cSortPrimitiveValues);
9466 oSolutionCandidate = new TSolution();
9467 oSourceElementsData.aPrimitiveValues.forEach(
9468 fEvaluatePrimitiveValue);
9469 oScope.cname = nIndex;
9470 } while (oSolutionCandidate.nSavings > oSolutionBest.nSavings);
9471 // Take the necessity of adding a variable statement into account.
9472 if ('var' !== oSourceElements[nFrom][0]) {
9473 oSolutionBest.nSavings -= oWeights.N_VARIABLE_STATEMENT_AFFIXATION;
9476 // Take the necessity of forming a closure into account.
9477 oSolutionBest.nSavings -= oWeights.N_CLOSURE;
9479 if (oSolutionBest.nSavings > 0) {
9480 // Create variable declarations suitable for UglifyJS.
9481 Object.keys(oSolutionBest.oPrimitiveValues).forEach(
9482 cAugmentVariableDeclarations);
9483 // Rewrite expressions that contain worthwhile primitive values.
9484 for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
9485 oWalker = oProcessor.ast_walker();
9486 oSourceElements[nPosition] =
9487 oWalker.with_walkers(
9488 oWalkersTransformers,
9489 cContext(oWalker, oSourceElements[nPosition]));
9491 if ('var' === oSourceElements[nFrom][0]) { // Reuse the statement.
9492 (/** @type {!Array.<!Array>} */ aVariableDeclarations.reverse(
9493 )).forEach(cAddVariableDeclaration);
9494 } else { // Add a variable statement.
9495 Array.prototype.splice.call(
9499 ['var', aVariableDeclarations]);
9504 Array.prototype.splice.call(
9508 ['stat', ['call', ['function', null, [], []], []]]);
9509 // Copy source elements into the closure.
9510 for (nPosition = nTo + 1; nPosition > nFrom; nPosition -= 1) {
9511 Array.prototype.unshift.call(
9512 oSourceElements[nFrom][1][1][3],
9513 oSourceElements[nPosition]);
9515 // Remove source elements outside the closure.
9516 Array.prototype.splice.call(
9523 // Restore the availability of identifier names.
9524 oScope.cname = nIndex;
9528 oSourceElements = (/** @type {!TSyntacticCodeUnit} */
9529 oSyntacticCodeUnit[bIsGlobal ? 1 : 3]);
9530 if (0 === oSourceElements.length) {
9533 oScope = bIsGlobal ? oSyntacticCodeUnit.scope : oSourceElements.scope;
9534 // Skip a Directive Prologue.
9535 while (nAfterDirectivePrologue < oSourceElements.length &&
9536 'directive' === oSourceElements[nAfterDirectivePrologue][0]) {
9537 nAfterDirectivePrologue += 1;
9538 aSourceElementsData.push(null);
9540 if (oSourceElements.length === nAfterDirectivePrologue) {
9543 for (nPosition = nAfterDirectivePrologue;
9544 nPosition < oSourceElements.length;
9546 oSourceElementData = new TSourceElementsData();
9547 oWalker = oProcessor.ast_walker();
9548 // Classify a source element.
9549 // Find its derived primitive values and count their occurrences.
9550 // Find all identifiers used (including nested scopes).
9551 oWalker.with_walkers(
9552 oWalkers.oSurveySourceElement,
9553 cContext(oWalker, oSourceElements[nPosition]));
9554 // Establish whether the scope is still wholly examinable.
9555 bIsWhollyExaminable = bIsWhollyExaminable &&
9556 ESourceElementCategories.N_WITH !== oSourceElementData.nCategory &&
9557 ESourceElementCategories.N_EVAL !== oSourceElementData.nCategory;
9558 aSourceElementsData.push(oSourceElementData);
9560 if (bIsWhollyExaminable) { // Examine the whole scope.
9561 fExamineSourceElements(
9562 nAfterDirectivePrologue,
9563 oSourceElements.length - 1,
9565 } else { // Examine unexcluded ranges of source elements.
9566 for (nPosition = oSourceElements.length - 1;
9567 nPosition >= nAfterDirectivePrologue;
9569 oSourceElementData = (/** @type {!TSourceElementsData} */
9570 aSourceElementsData[nPosition]);
9571 if (ESourceElementCategories.N_OTHER ===
9572 oSourceElementData.nCategory) {
9573 if ('undefined' === typeof nTo) {
9574 nTo = nPosition; // Indicate the end of a range.
9576 // Examine the range if it immediately follows a Directive Prologue.
9577 if (nPosition === nAfterDirectivePrologue) {
9578 fExamineSourceElements(nPosition, nTo, true);
9581 if ('undefined' !== typeof nTo) {
9582 // Examine the range that immediately follows this source element.
9583 fExamineSourceElements(nPosition + 1, nTo, true);
9584 nTo = void 0; // Obliterate the range.
9586 // Examine nested functions.
9587 oWalker = oProcessor.ast_walker();
9588 oWalker.with_walkers(
9589 oWalkers.oExamineFunctions,
9590 cContext(oWalker, oSourceElements[nPosition]));
9594 }(oAbstractSyntaxTree = oProcessor.ast_add_scope(oAbstractSyntaxTree)));
9595 return oAbstractSyntaxTree;
9597 /*jshint sub:false */
9599 /* Local Variables: */
9602 /* indent-tabs-mode: nil */
9605 /* vim: set ft=javascript fenc=utf-8 et ts=2 sts=2 sw=2: */
9606 /* :mode=javascript:noTabs=true:tabSize=2:indentSize=2:deepIndent=true: */
9608 define('uglifyjs/parse-js', ["exports"], function(exports) {
9609 /***********************************************************************
9611 A JavaScript tokenizer / parser / beautifier / compressor.
9613 This version is suitable for Node.js. With minimal changes (the
9614 exports stuff) it should work on any JS platform.
9616 This file contains the tokenizer/parser. It is a port to JavaScript
9617 of parse-js [1], a JavaScript parser library written in Common Lisp
9618 by Marijn Haverbeke. Thank you Marijn!
9620 [1] http://marijn.haverbeke.nl/parse-js/
9624 - tokenizer(code) -- returns a function. Call the returned
9625 function to fetch the next token.
9627 - parse(code) -- returns an AST of the given JavaScript code.
9629 -------------------------------- (C) ---------------------------------
9632 <mihai.bazon@gmail.com>
9633 http://mihai.bazon.net/blog
9635 Distributed under the BSD license:
9637 Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
9638 Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
9640 Redistribution and use in source and binary forms, with or without
9641 modification, are permitted provided that the following conditions
9644 * Redistributions of source code must retain the above
9645 copyright notice, this list of conditions and the following
9648 * Redistributions in binary form must reproduce the above
9649 copyright notice, this list of conditions and the following
9650 disclaimer in the documentation and/or other materials
9651 provided with the distribution.
9653 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
9654 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
9655 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
9656 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
9657 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
9658 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
9659 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
9660 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
9661 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
9662 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
9663 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
9666 ***********************************************************************/
9668 /* -----[ Tokenizer (constants) ]----- */
9670 var KEYWORDS = array_to_hash([
9699 var RESERVED_WORDS = array_to_hash([
9731 var KEYWORDS_BEFORE_EXPRESSION = array_to_hash([
9740 var KEYWORDS_ATOM = array_to_hash([
9747 var OPERATOR_CHARS = array_to_hash(characters("+-*&%=<>!?|~^"));
9749 var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
9750 var RE_OCT_NUMBER = /^0[0-7]+$/;
9751 var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
9753 var OPERATORS = array_to_hash([
9800 var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF"));
9802 var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{(,.;:"));
9804 var PUNC_CHARS = array_to_hash(characters("[]{}(),;:"));
9806 var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy"));
9808 /* -----[ Tokenizer ]----- */
9810 var UNICODE = { // Unicode 6.1
9811 letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0527\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0\\u08A2-\\u08AC\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0977\\u0979-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F0\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA697\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA793\\uA7A0-\\uA7AA\\uA7F8-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA80-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),
9812 combining_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065F\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0859-\\u085B\\u08E4-\\u08FE\\u0900-\\u0903\\u093A-\\u093C\\u093E-\\u094F\\u0951-\\u0957\\u0962\\u0963\\u0981-\\u0983\\u09BC\\u09BE-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CD\\u09D7\\u09E2\\u09E3\\u0A01-\\u0A03\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81-\\u0A83\\u0ABC\\u0ABE-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AE2\\u0AE3\\u0B01-\\u0B03\\u0B3C\\u0B3E-\\u0B44\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B56\\u0B57\\u0B62\\u0B63\\u0B82\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD7\\u0C01-\\u0C03\\u0C3E-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0C82\\u0C83\\u0CBC\\u0CBE-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6\\u0CE2\\u0CE3\\u0D02\\u0D03\\u0D3E-\\u0D44\\u0D46-\\u0D48\\u0D4A-\\u0D4D\\u0D57\\u0D62\\u0D63\\u0D82\\u0D83\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F3E\\u0F3F\\u0F71-\\u0F84\\u0F86\\u0F87\\u0F8D-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102B-\\u103E\\u1056-\\u1059\\u105E-\\u1060\\u1062-\\u1064\\u1067-\\u106D\\u1071-\\u1074\\u1082-\\u108D\\u108F\\u109A-\\u109D\\u135D-\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B4-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u192B\\u1930-\\u193B\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A17-\\u1A1B\\u1A55-\\u1A5E\\u1A60-\\u1A7C\\u1A7F\\u1B00-\\u1B04\\u1B34-\\u1B44\\u1B6B-\\u1B73\\u1B80-\\u1B82\\u1BA1-\\u1BAD\\u1BE6-\\u1BF3\\u1C24-\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE8\\u1CED\\u1CF2-\\u1CF4\\u1DC0-\\u1DE6\\u1DFC-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2D7F\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA674-\\uA67D\\uA69F\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA823-\\uA827\\uA880\\uA881\\uA8B4-\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA953\\uA980-\\uA983\\uA9B3-\\uA9C0\\uAA29-\\uAA36\\uAA43\\uAA4C\\uAA4D\\uAA7B\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uAAEB-\\uAAEF\\uAAF5\\uAAF6\\uABE3-\\uABEA\\uABEC\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"),
9813 connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]"),
9814 digit: new RegExp("[\\u0030-\\u0039\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0BE6-\\u0BEF\\u0C66-\\u0C6F\\u0CE6-\\u0CEF\\u0D66-\\u0D6F\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F29\\u1040-\\u1049\\u1090-\\u1099\\u17E0-\\u17E9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19D9\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\uA620-\\uA629\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19]")
9817 function is_letter(ch) {
9818 return UNICODE.letter.test(ch);
9821 function is_digit(ch) {
9822 ch = ch.charCodeAt(0);
9823 return ch >= 48 && ch <= 57;
9826 function is_unicode_digit(ch) {
9827 return UNICODE.digit.test(ch);
9830 function is_alphanumeric_char(ch) {
9831 return is_digit(ch) || is_letter(ch);
9834 function is_unicode_combining_mark(ch) {
9835 return UNICODE.combining_mark.test(ch);
9838 function is_unicode_connector_punctuation(ch) {
9839 return UNICODE.connector_punctuation.test(ch);
9842 function is_identifier_start(ch) {
9843 return ch == "$" || ch == "_" || is_letter(ch);
9846 function is_identifier_char(ch) {
9847 return is_identifier_start(ch)
9848 || is_unicode_combining_mark(ch)
9849 || is_unicode_digit(ch)
9850 || is_unicode_connector_punctuation(ch)
9851 || ch == "\u200c" // zero-width non-joiner <ZWNJ>
9852 || ch == "\u200d" // zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
9856 function parse_js_number(num) {
9857 if (RE_HEX_NUMBER.test(num)) {
9858 return parseInt(num.substr(2), 16);
9859 } else if (RE_OCT_NUMBER.test(num)) {
9860 return parseInt(num.substr(1), 8);
9861 } else if (RE_DEC_NUMBER.test(num)) {
9862 return parseFloat(num);
9866 function JS_Parse_Error(message, line, col, pos) {
9867 this.message = message;
9868 this.line = line + 1;
9871 this.stack = new Error().stack;
9874 JS_Parse_Error.prototype.toString = function() {
9875 return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
9878 function js_error(message, line, col, pos) {
9879 throw new JS_Parse_Error(message, line, col, pos);
9882 function is_token(token, type, val) {
9883 return token.type == type && (val == null || token.value == val);
9888 function tokenizer($TEXT) {
9891 text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''),
9898 newline_before : false,
9899 regex_allowed : false,
9900 comments_before : []
9903 function peek() { return S.text.charAt(S.pos); };
9905 function next(signal_eof, in_string) {
9906 var ch = S.text.charAt(S.pos++);
9907 if (signal_eof && !ch)
9910 S.newline_before = S.newline_before || !in_string;
9923 function find(what, signal_eof) {
9924 var pos = S.text.indexOf(what, S.pos);
9925 if (signal_eof && pos == -1) throw EX_EOF;
9929 function start_token() {
9935 function token(type, value, is_comment) {
9936 S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) ||
9937 (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) ||
9938 (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value)));
9946 nlb : S.newline_before
9949 ret.comments_before = S.comments_before;
9950 S.comments_before = [];
9951 // make note of any newlines in the comments that came before
9952 for (var i = 0, len = ret.comments_before.length; i < len; i++) {
9953 ret.nlb = ret.nlb || ret.comments_before[i].nlb;
9956 S.newline_before = false;
9960 function skip_whitespace() {
9961 while (HOP(WHITESPACE_CHARS, peek()))
9965 function read_while(pred) {
9966 var ret = "", ch = peek(), i = 0;
9967 while (ch && pred(ch, i++)) {
9974 function parse_error(err) {
9975 js_error(err, S.tokline, S.tokcol, S.tokpos);
9978 function read_num(prefix) {
9979 var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
9980 var num = read_while(function(ch, i){
9981 if (ch == "x" || ch == "X") {
9982 if (has_x) return false;
9983 return has_x = true;
9985 if (!has_x && (ch == "E" || ch == "e")) {
9986 if (has_e) return false;
9987 return has_e = after_e = true;
9990 if (after_e || (i == 0 && !prefix)) return true;
9993 if (ch == "+") return after_e;
9996 if (!has_dot && !has_x && !has_e)
9997 return has_dot = true;
10000 return is_alphanumeric_char(ch);
10003 num = prefix + num;
10004 var valid = parse_js_number(num);
10005 if (!isNaN(valid)) {
10006 return token("num", valid);
10008 parse_error("Invalid syntax: " + num);
10012 function read_escaped_char(in_string) {
10013 var ch = next(true, in_string);
10015 case "n" : return "\n";
10016 case "r" : return "\r";
10017 case "t" : return "\t";
10018 case "b" : return "\b";
10019 case "v" : return "\u000b";
10020 case "f" : return "\f";
10021 case "0" : return "\0";
10022 case "x" : return String.fromCharCode(hex_bytes(2));
10023 case "u" : return String.fromCharCode(hex_bytes(4));
10024 case "\n": return "";
10025 default : return ch;
10029 function hex_bytes(n) {
10031 for (; n > 0; --n) {
10032 var digit = parseInt(next(true), 16);
10034 parse_error("Invalid hex-character pattern in string");
10035 num = (num << 4) | digit;
10040 function read_string() {
10041 return with_eof_error("Unterminated string constant", function(){
10042 var quote = next(), ret = "";
10044 var ch = next(true);
10046 // read OctalEscapeSequence (XXX: deprecated if "strict mode")
10047 // https://github.com/mishoo/UglifyJS/issues/178
10048 var octal_len = 0, first = null;
10049 ch = read_while(function(ch){
10050 if (ch >= "0" && ch <= "7") {
10053 return ++octal_len;
10055 else if (first <= "3" && octal_len <= 2) return ++octal_len;
10056 else if (first >= "4" && octal_len <= 1) return ++octal_len;
10060 if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
10061 else ch = read_escaped_char(true);
10063 else if (ch == quote) break;
10064 else if (ch == "\n") throw EX_EOF;
10067 return token("string", ret);
10071 function read_line_comment() {
10073 var i = find("\n"), ret;
10075 ret = S.text.substr(S.pos);
10076 S.pos = S.text.length;
10078 ret = S.text.substring(S.pos, i);
10081 return token("comment1", ret, true);
10084 function read_multiline_comment() {
10086 return with_eof_error("Unterminated multiline comment", function(){
10087 var i = find("*/", true),
10088 text = S.text.substring(S.pos, i);
10090 S.line += text.split("\n").length - 1;
10091 S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
10093 // https://github.com/mishoo/UglifyJS/issues/#issue/100
10094 if (/^@cc_on/i.test(text)) {
10095 warn("WARNING: at line " + S.line);
10096 warn("*** Found \"conditional comment\": " + text);
10097 warn("*** UglifyJS DISCARDS ALL COMMENTS. This means your code might no longer work properly in Internet Explorer.");
10100 return token("comment2", text, true);
10104 function read_name() {
10105 var backslash = false, name = "", ch, escaped = false, hex;
10106 while ((ch = peek()) != null) {
10108 if (ch == "\\") escaped = backslash = true, next();
10109 else if (is_identifier_char(ch)) name += next();
10113 if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
10114 ch = read_escaped_char();
10115 if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
10120 if (HOP(KEYWORDS, name) && escaped) {
10121 hex = name.charCodeAt(0).toString(16).toUpperCase();
10122 name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
10127 function read_regexp(regexp) {
10128 return with_eof_error("Unterminated regular expression", function(){
10129 var prev_backslash = false, ch, in_class = false;
10130 while ((ch = next(true))) if (prev_backslash) {
10131 regexp += "\\" + ch;
10132 prev_backslash = false;
10133 } else if (ch == "[") {
10136 } else if (ch == "]" && in_class) {
10139 } else if (ch == "/" && !in_class) {
10141 } else if (ch == "\\") {
10142 prev_backslash = true;
10146 var mods = read_name();
10147 return token("regexp", [ regexp, mods ]);
10151 function read_operator(prefix) {
10152 function grow(op) {
10153 if (!peek()) return op;
10154 var bigger = op + peek();
10155 if (HOP(OPERATORS, bigger)) {
10157 return grow(bigger);
10162 return token("operator", grow(prefix || next()));
10165 function handle_slash() {
10167 var regex_allowed = S.regex_allowed;
10170 S.comments_before.push(read_line_comment());
10171 S.regex_allowed = regex_allowed;
10172 return next_token();
10174 S.comments_before.push(read_multiline_comment());
10175 S.regex_allowed = regex_allowed;
10176 return next_token();
10178 return S.regex_allowed ? read_regexp("") : read_operator("/");
10181 function handle_dot() {
10183 return is_digit(peek())
10185 : token("punc", ".");
10188 function read_word() {
10189 var word = read_name();
10190 return !HOP(KEYWORDS, word)
10191 ? token("name", word)
10192 : HOP(OPERATORS, word)
10193 ? token("operator", word)
10194 : HOP(KEYWORDS_ATOM, word)
10195 ? token("atom", word)
10196 : token("keyword", word);
10199 function with_eof_error(eof_error, cont) {
10203 if (ex === EX_EOF) parse_error(eof_error);
10208 function next_token(force_regexp) {
10209 if (force_regexp != null)
10210 return read_regexp(force_regexp);
10214 if (!ch) return token("eof");
10215 if (is_digit(ch)) return read_num();
10216 if (ch == '"' || ch == "'") return read_string();
10217 if (HOP(PUNC_CHARS, ch)) return token("punc", next());
10218 if (ch == ".") return handle_dot();
10219 if (ch == "/") return handle_slash();
10220 if (HOP(OPERATOR_CHARS, ch)) return read_operator();
10221 if (ch == "\\" || is_identifier_start(ch)) return read_word();
10222 parse_error("Unexpected character '" + ch + "'");
10225 next_token.context = function(nc) {
10234 /* -----[ Parser (constants) ]----- */
10236 var UNARY_PREFIX = array_to_hash([
10248 var UNARY_POSTFIX = array_to_hash([ "--", "++" ]);
10250 var ASSIGNMENT = (function(a, ret, i){
10251 while (i < a.length) {
10252 ret[a[i]] = a[i].substr(0, a[i].length - 1);
10257 ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="],
10262 var PRECEDENCE = (function(a, ret){
10263 for (var i = 0, n = 1; i < a.length; ++i, ++n) {
10265 for (var j = 0; j < b.length; ++j) {
10277 ["==", "===", "!=", "!=="],
10278 ["<", ">", "<=", ">=", "in", "instanceof"],
10279 [">>", "<<", ">>>"],
10286 var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
10288 var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
10290 /* -----[ Parser ]----- */
10292 function NodeWithToken(str, start, end) {
10294 this.start = start;
10298 NodeWithToken.prototype.toString = function() { return this.name; };
10300 function parse($TEXT, exigent_mode, embed_tokens) {
10303 input : typeof $TEXT == "string" ? tokenizer($TEXT, true) : $TEXT,
10308 in_directives : true,
10315 function is(type, value) {
10316 return is_token(S.token, type, value);
10319 function peek() { return S.peeked || (S.peeked = S.input()); };
10324 S.token = S.peeked;
10327 S.token = S.input();
10329 S.in_directives = S.in_directives && (
10330 S.token.type == "string" || is("punc", ";")
10339 function croak(msg, line, col, pos) {
10340 var ctx = S.input.context();
10342 line != null ? line : ctx.tokline,
10343 col != null ? col : ctx.tokcol,
10344 pos != null ? pos : ctx.tokpos);
10347 function token_error(token, msg) {
10348 croak(msg, token.line, token.col);
10351 function unexpected(token) {
10354 token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
10357 function expect_token(type, val) {
10358 if (is(type, val)) {
10361 token_error(S.token, "Unexpected token " + S.token.type + ", expected " + type);
10364 function expect(punc) { return expect_token("punc", punc); };
10366 function can_insert_semicolon() {
10367 return !exigent_mode && (
10368 S.token.nlb || is("eof") || is("punc", "}")
10372 function semicolon() {
10373 if (is("punc", ";")) next();
10374 else if (!can_insert_semicolon()) unexpected();
10378 return slice(arguments);
10381 function parenthesised() {
10383 var ex = expression();
10388 function add_tokens(str, start, end) {
10389 return str instanceof NodeWithToken ? str : new NodeWithToken(str, start, end);
10392 function maybe_embed_tokens(parser) {
10393 if (embed_tokens) return function() {
10394 var start = S.token;
10395 var ast = parser.apply(this, arguments);
10396 ast[0] = add_tokens(ast[0], start, prev());
10399 else return parser;
10402 var statement = maybe_embed_tokens(function() {
10403 if (is("operator", "/") || is("operator", "/=")) {
10405 S.token = S.input(S.token.value.substr(1)); // force regexp
10407 switch (S.token.type) {
10409 var dir = S.in_directives, stat = simple_statement();
10410 if (dir && stat[1][0] == "string" && !is("punc", ","))
10411 return as("directive", stat[1][1]);
10417 return simple_statement();
10420 return is_token(peek(), "punc", ":")
10421 ? labeled_statement(prog1(S.token.value, next, next))
10422 : simple_statement();
10425 switch (S.token.value) {
10427 return as("block", block_());
10430 return simple_statement();
10433 return as("block");
10439 switch (prog1(S.token.value, next)) {
10441 return break_cont("break");
10444 return break_cont("continue");
10448 return as("debugger");
10451 return (function(body){
10452 expect_token("keyword", "while");
10453 return as("do", prog1(parenthesised, semicolon), body);
10454 })(in_loop(statement));
10460 return function_(true);
10466 if (S.in_function == 0)
10467 croak("'return' outside of function");
10468 return as("return",
10471 : can_insert_semicolon()
10473 : prog1(expression, semicolon));
10476 return as("switch", parenthesised(), switch_block_());
10480 croak("Illegal newline after 'throw'");
10481 return as("throw", prog1(expression, semicolon));
10487 return prog1(var_, semicolon);
10490 return prog1(const_, semicolon);
10493 return as("while", parenthesised(), in_loop(statement));
10496 return as("with", parenthesised(), statement());
10504 function labeled_statement(label) {
10505 S.labels.push(label);
10506 var start = S.token, stat = statement();
10507 if (exigent_mode && !HOP(STATEMENTS_WITH_LABELS, stat[0]))
10510 return as("label", label, stat);
10513 function simple_statement() {
10514 return as("stat", prog1(expression, semicolon));
10517 function break_cont(type) {
10519 if (!can_insert_semicolon()) {
10520 name = is("name") ? S.token.value : null;
10522 if (name != null) {
10524 if (!member(name, S.labels))
10525 croak("Label " + name + " without matching loop or statement");
10527 else if (S.in_loop == 0)
10528 croak(type + " not inside a loop or switch");
10530 return as(type, name);
10536 if (!is("punc", ";")) {
10537 init = is("keyword", "var")
10538 ? (next(), var_(true))
10539 : expression(true, true);
10540 if (is("operator", "in")) {
10541 if (init[0] == "var" && init[1].length > 1)
10542 croak("Only one variable declaration allowed in for..in loop");
10543 return for_in(init);
10546 return regular_for(init);
10549 function regular_for(init) {
10551 var test = is("punc", ";") ? null : expression();
10553 var step = is("punc", ")") ? null : expression();
10555 return as("for", init, test, step, in_loop(statement));
10558 function for_in(init) {
10559 var lhs = init[0] == "var" ? as("name", init[1][0]) : init;
10561 var obj = expression();
10563 return as("for-in", init, lhs, obj, in_loop(statement));
10566 var function_ = function(in_statement) {
10567 var name = is("name") ? prog1(S.token.value, next) : null;
10568 if (in_statement && !name)
10571 return as(in_statement ? "defun" : "function",
10574 (function(first, a){
10575 while (!is("punc", ")")) {
10576 if (first) first = false; else expect(",");
10577 if (!is("name")) unexpected();
10578 a.push(S.token.value);
10587 var loop = S.in_loop;
10588 S.in_directives = true;
10598 var cond = parenthesised(), body = statement(), belse;
10599 if (is("keyword", "else")) {
10601 belse = statement();
10603 return as("if", cond, body, belse);
10606 function block_() {
10609 while (!is("punc", "}")) {
10610 if (is("eof")) unexpected();
10611 a.push(statement());
10617 var switch_block_ = curry(in_loop, function(){
10619 var a = [], cur = null;
10620 while (!is("punc", "}")) {
10621 if (is("eof")) unexpected();
10622 if (is("keyword", "case")) {
10625 a.push([ expression(), cur ]);
10628 else if (is("keyword", "default")) {
10632 a.push([ null, cur ]);
10635 if (!cur) unexpected();
10636 cur.push(statement());
10644 var body = block_(), bcatch, bfinally;
10645 if (is("keyword", "catch")) {
10649 croak("Name expected");
10650 var name = S.token.value;
10653 bcatch = [ name, block_() ];
10655 if (is("keyword", "finally")) {
10657 bfinally = block_();
10659 if (!bcatch && !bfinally)
10660 croak("Missing catch/finally blocks");
10661 return as("try", body, bcatch, bfinally);
10664 function vardefs(no_in) {
10669 var name = S.token.value;
10671 if (is("operator", "=")) {
10673 a.push([ name, expression(false, no_in) ]);
10677 if (!is("punc", ","))
10684 function var_(no_in) {
10685 return as("var", vardefs(no_in));
10688 function const_() {
10689 return as("const", vardefs());
10693 var newexp = expr_atom(false), args;
10694 if (is("punc", "(")) {
10696 args = expr_list(")");
10700 return subscripts(as("new", newexp, args), true);
10703 var expr_atom = maybe_embed_tokens(function(allow_calls) {
10704 if (is("operator", "new")) {
10709 switch (S.token.value) {
10712 return subscripts(prog1(expression, curry(expect, ")")), allow_calls);
10715 return subscripts(array_(), allow_calls);
10718 return subscripts(object_(), allow_calls);
10722 if (is("keyword", "function")) {
10724 return subscripts(function_(false), allow_calls);
10726 if (HOP(ATOMIC_START_TOKEN, S.token.type)) {
10727 var atom = S.token.type == "regexp"
10728 ? as("regexp", S.token.value[0], S.token.value[1])
10729 : as(S.token.type, S.token.value);
10730 return subscripts(prog1(atom, next), allow_calls);
10735 function expr_list(closing, allow_trailing_comma, allow_empty) {
10736 var first = true, a = [];
10737 while (!is("punc", closing)) {
10738 if (first) first = false; else expect(",");
10739 if (allow_trailing_comma && is("punc", closing)) break;
10740 if (is("punc", ",") && allow_empty) {
10741 a.push([ "atom", "undefined" ]);
10743 a.push(expression(false));
10750 function array_() {
10751 return as("array", expr_list("]", !exigent_mode, true));
10754 function object_() {
10755 var first = true, a = [];
10756 while (!is("punc", "}")) {
10757 if (first) first = false; else expect(",");
10758 if (!exigent_mode && is("punc", "}"))
10759 // allow trailing comma
10761 var type = S.token.type;
10762 var name = as_property_name();
10763 if (type == "name" && (name == "get" || name == "set") && !is("punc", ":")) {
10764 a.push([ as_name(), function_(false), name ]);
10767 a.push([ name, expression(false) ]);
10771 return as("object", a);
10774 function as_property_name() {
10775 switch (S.token.type) {
10778 return prog1(S.token.value, next);
10783 function as_name() {
10784 switch (S.token.type) {
10789 return prog1(S.token.value, next);
10795 function subscripts(expr, allow_calls) {
10796 if (is("punc", ".")) {
10798 return subscripts(as("dot", expr, as_name()), allow_calls);
10800 if (is("punc", "[")) {
10802 return subscripts(as("sub", expr, prog1(expression, curry(expect, "]"))), allow_calls);
10804 if (allow_calls && is("punc", "(")) {
10806 return subscripts(as("call", expr, expr_list(")")), true);
10811 function maybe_unary(allow_calls) {
10812 if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) {
10813 return make_unary("unary-prefix",
10814 prog1(S.token.value, next),
10815 maybe_unary(allow_calls));
10817 var val = expr_atom(allow_calls);
10818 while (is("operator") && HOP(UNARY_POSTFIX, S.token.value) && !S.token.nlb) {
10819 val = make_unary("unary-postfix", S.token.value, val);
10825 function make_unary(tag, op, expr) {
10826 if ((op == "++" || op == "--") && !is_assignable(expr))
10827 croak("Invalid use of " + op + " operator");
10828 return as(tag, op, expr);
10831 function expr_op(left, min_prec, no_in) {
10832 var op = is("operator") ? S.token.value : null;
10833 if (op && op == "in" && no_in) op = null;
10834 var prec = op != null ? PRECEDENCE[op] : null;
10835 if (prec != null && prec > min_prec) {
10837 var right = expr_op(maybe_unary(true), prec, no_in);
10838 return expr_op(as("binary", op, left, right), min_prec, no_in);
10843 function expr_ops(no_in) {
10844 return expr_op(maybe_unary(true), 0, no_in);
10847 function maybe_conditional(no_in) {
10848 var expr = expr_ops(no_in);
10849 if (is("operator", "?")) {
10851 var yes = expression(false);
10853 return as("conditional", expr, yes, expression(false, no_in));
10858 function is_assignable(expr) {
10859 if (!exigent_mode) return true;
10860 switch (expr[0]+"") {
10867 return expr[1] != "this";
10871 function maybe_assign(no_in) {
10872 var left = maybe_conditional(no_in), val = S.token.value;
10873 if (is("operator") && HOP(ASSIGNMENT, val)) {
10874 if (is_assignable(left)) {
10876 return as("assign", ASSIGNMENT[val], left, maybe_assign(no_in));
10878 croak("Invalid assignment");
10883 var expression = maybe_embed_tokens(function(commas, no_in) {
10884 if (arguments.length == 0)
10886 var expr = maybe_assign(no_in);
10887 if (commas && is("punc", ",")) {
10889 return as("seq", expr, expression(true, no_in));
10894 function in_loop(cont) {
10903 return as("toplevel", (function(a){
10905 a.push(statement());
10911 /* -----[ Utilities ]----- */
10913 function curry(f) {
10914 var args = slice(arguments, 1);
10915 return function() { return f.apply(this, args.concat(slice(arguments))); };
10918 function prog1(ret) {
10919 if (ret instanceof Function)
10921 for (var i = 1, n = arguments.length; --n > 0; ++i)
10926 function array_to_hash(a) {
10928 for (var i = 0; i < a.length; ++i)
10933 function slice(a, start) {
10934 return Array.prototype.slice.call(a, start || 0);
10937 function characters(str) {
10938 return str.split("");
10941 function member(name, array) {
10942 for (var i = array.length; --i >= 0;)
10943 if (array[i] == name)
10948 function HOP(obj, prop) {
10949 return Object.prototype.hasOwnProperty.call(obj, prop);
10952 var warn = function() {};
10954 /* -----[ Exports ]----- */
10956 exports.tokenizer = tokenizer;
10957 exports.parse = parse;
10958 exports.slice = slice;
10959 exports.curry = curry;
10960 exports.member = member;
10961 exports.array_to_hash = array_to_hash;
10962 exports.PRECEDENCE = PRECEDENCE;
10963 exports.KEYWORDS_ATOM = KEYWORDS_ATOM;
10964 exports.RESERVED_WORDS = RESERVED_WORDS;
10965 exports.KEYWORDS = KEYWORDS;
10966 exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;
10967 exports.OPERATORS = OPERATORS;
10968 exports.is_alphanumeric_char = is_alphanumeric_char;
10969 exports.is_identifier_start = is_identifier_start;
10970 exports.is_identifier_char = is_identifier_char;
10971 exports.set_logger = function(logger) {
10975 // Local variables:
10976 // js-indent-level: 4
10978 });define('uglifyjs/squeeze-more', ["require", "exports", "module", "./parse-js", "./squeeze-more"], function(require, exports, module) {
10979 var jsp = require("./parse-js"),
10980 pro = require("./process"),
10982 member = jsp.member,
10985 PRECEDENCE = jsp.PRECEDENCE,
10986 OPERATORS = jsp.OPERATORS;
10988 function ast_squeeze_more(ast) {
10989 var w = pro.ast_walker(), walk = w.walk, scope;
10990 function with_scope(s, cont) {
10991 var save = scope, ret;
10997 function _lambda(name, args, body) {
10998 return [ this[0], name, args, with_scope(body.scope, curry(MAP, body, walk)) ];
11000 return w.with_walkers({
11001 "toplevel": function(body) {
11002 return [ this[0], with_scope(this.scope, curry(MAP, body, walk)) ];
11004 "function": _lambda,
11006 "new": function(ctor, args) {
11007 if (ctor[0] == "name") {
11008 if (ctor[1] == "Array" && !scope.has("Array")) {
11009 if (args.length != 1) {
11010 return [ "array", args ];
11012 return walk([ "call", [ "name", "Array" ], args ]);
11014 } else if (ctor[1] == "Object" && !scope.has("Object")) {
11015 if (!args.length) {
11016 return [ "object", [] ];
11018 return walk([ "call", [ "name", "Object" ], args ]);
11020 } else if ((ctor[1] == "RegExp" || ctor[1] == "Function" || ctor[1] == "Error") && !scope.has(ctor[1])) {
11021 return walk([ "call", [ "name", ctor[1] ], args]);
11025 "call": function(expr, args) {
11026 if (expr[0] == "dot" && expr[1][0] == "string" && args.length == 1
11027 && (args[0][1] > 0 && expr[2] == "substring" || expr[2] == "substr")) {
11028 return [ "call", [ "dot", expr[1], "slice"], args];
11030 if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
11031 // foo.toString() ==> foo+""
11032 if (expr[1][0] == "string") return expr[1];
11033 return [ "binary", "+", expr[1], [ "string", "" ]];
11035 if (expr[0] == "name") {
11036 if (expr[1] == "Array" && args.length != 1 && !scope.has("Array")) {
11037 return [ "array", args ];
11039 if (expr[1] == "Object" && !args.length && !scope.has("Object")) {
11040 return [ "object", [] ];
11042 if (expr[1] == "String" && !scope.has("String")) {
11043 return [ "binary", "+", args[0], [ "string", "" ]];
11048 return walk(pro.ast_add_scope(ast));
11052 exports.ast_squeeze_more = ast_squeeze_more;
11054 // Local variables:
11055 // js-indent-level: 4
11058 define('uglifyjs/process', ["require", "exports", "module", "./parse-js", "./squeeze-more"], function(require, exports, module) {
11059 /***********************************************************************
11061 A JavaScript tokenizer / parser / beautifier / compressor.
11063 This version is suitable for Node.js. With minimal changes (the
11064 exports stuff) it should work on any JS platform.
11066 This file implements some AST processors. They work on data built
11069 Exported functions:
11071 - ast_mangle(ast, options) -- mangles the variable/function names
11072 in the AST. Returns an AST.
11074 - ast_squeeze(ast) -- employs various optimizations to make the
11075 final generated code even smaller. Returns an AST.
11077 - gen_code(ast, options) -- generates JS code from the AST. Pass
11078 true (or an object, see the code for some options) as second
11079 argument to get "pretty" (indented) code.
11081 -------------------------------- (C) ---------------------------------
11083 Author: Mihai Bazon
11084 <mihai.bazon@gmail.com>
11085 http://mihai.bazon.net/blog
11087 Distributed under the BSD license:
11089 Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
11091 Redistribution and use in source and binary forms, with or without
11092 modification, are permitted provided that the following conditions
11095 * Redistributions of source code must retain the above
11096 copyright notice, this list of conditions and the following
11099 * Redistributions in binary form must reproduce the above
11100 copyright notice, this list of conditions and the following
11101 disclaimer in the documentation and/or other materials
11102 provided with the distribution.
11104 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
11105 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
11106 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
11107 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
11108 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
11109 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
11110 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
11111 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11112 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
11113 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
11114 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
11117 ***********************************************************************/
11119 var jsp = require("./parse-js"),
11122 member = jsp.member,
11123 is_identifier_char = jsp.is_identifier_char,
11124 PRECEDENCE = jsp.PRECEDENCE,
11125 OPERATORS = jsp.OPERATORS;
11127 /* -----[ helper for AST traversal ]----- */
11129 function ast_walker() {
11130 function _vardefs(defs) {
11131 return [ this[0], MAP(defs, function(def){
11132 var a = [ def[0] ];
11133 if (def.length > 1)
11134 a[1] = walk(def[1]);
11138 function _block(statements) {
11139 var out = [ this[0] ];
11140 if (statements != null)
11141 out.push(MAP(statements, walk));
11145 "string": function(str) {
11146 return [ this[0], str ];
11148 "num": function(num) {
11149 return [ this[0], num ];
11151 "name": function(name) {
11152 return [ this[0], name ];
11154 "toplevel": function(statements) {
11155 return [ this[0], MAP(statements, walk) ];
11161 "try": function(t, c, f) {
11165 c != null ? [ c[0], MAP(c[1], walk) ] : null,
11166 f != null ? MAP(f, walk) : null
11169 "throw": function(expr) {
11170 return [ this[0], walk(expr) ];
11172 "new": function(ctor, args) {
11173 return [ this[0], walk(ctor), MAP(args, walk) ];
11175 "switch": function(expr, body) {
11176 return [ this[0], walk(expr), MAP(body, function(branch){
11177 return [ branch[0] ? walk(branch[0]) : null,
11178 MAP(branch[1], walk) ];
11181 "break": function(label) {
11182 return [ this[0], label ];
11184 "continue": function(label) {
11185 return [ this[0], label ];
11187 "conditional": function(cond, t, e) {
11188 return [ this[0], walk(cond), walk(t), walk(e) ];
11190 "assign": function(op, lvalue, rvalue) {
11191 return [ this[0], op, walk(lvalue), walk(rvalue) ];
11193 "dot": function(expr) {
11194 return [ this[0], walk(expr) ].concat(slice(arguments, 1));
11196 "call": function(expr, args) {
11197 return [ this[0], walk(expr), MAP(args, walk) ];
11199 "function": function(name, args, body) {
11200 return [ this[0], name, args.slice(), MAP(body, walk) ];
11202 "debugger": function() {
11203 return [ this[0] ];
11205 "defun": function(name, args, body) {
11206 return [ this[0], name, args.slice(), MAP(body, walk) ];
11208 "if": function(conditional, t, e) {
11209 return [ this[0], walk(conditional), walk(t), walk(e) ];
11211 "for": function(init, cond, step, block) {
11212 return [ this[0], walk(init), walk(cond), walk(step), walk(block) ];
11214 "for-in": function(vvar, key, hash, block) {
11215 return [ this[0], walk(vvar), walk(key), walk(hash), walk(block) ];
11217 "while": function(cond, block) {
11218 return [ this[0], walk(cond), walk(block) ];
11220 "do": function(cond, block) {
11221 return [ this[0], walk(cond), walk(block) ];
11223 "return": function(expr) {
11224 return [ this[0], walk(expr) ];
11226 "binary": function(op, left, right) {
11227 return [ this[0], op, walk(left), walk(right) ];
11229 "unary-prefix": function(op, expr) {
11230 return [ this[0], op, walk(expr) ];
11232 "unary-postfix": function(op, expr) {
11233 return [ this[0], op, walk(expr) ];
11235 "sub": function(expr, subscript) {
11236 return [ this[0], walk(expr), walk(subscript) ];
11238 "object": function(props) {
11239 return [ this[0], MAP(props, function(p){
11240 return p.length == 2
11241 ? [ p[0], walk(p[1]) ]
11242 : [ p[0], walk(p[1]), p[2] ]; // get/set-ter
11245 "regexp": function(rx, mods) {
11246 return [ this[0], rx, mods ];
11248 "array": function(elements) {
11249 return [ this[0], MAP(elements, walk) ];
11251 "stat": function(stat) {
11252 return [ this[0], walk(stat) ];
11254 "seq": function() {
11255 return [ this[0] ].concat(MAP(slice(arguments), walk));
11257 "label": function(name, block) {
11258 return [ this[0], name, walk(block) ];
11260 "with": function(expr, block) {
11261 return [ this[0], walk(expr), walk(block) ];
11263 "atom": function(name) {
11264 return [ this[0], name ];
11266 "directive": function(dir) {
11267 return [ this[0], dir ];
11273 function walk(ast) {
11279 var gen = user[type];
11281 var ret = gen.apply(ast, ast.slice(1));
11285 gen = walkers[type];
11286 return gen.apply(ast, ast.slice(1));
11292 function dive(ast) {
11297 return walkers[ast[0]].apply(ast, ast.slice(1));
11303 function with_walkers(walkers, cont){
11305 for (i in walkers) if (HOP(walkers, i)) {
11307 user[i] = walkers[i];
11310 for (i in save) if (HOP(save, i)) {
11311 if (!save[i]) delete user[i];
11312 else user[i] = save[i];
11320 with_walkers: with_walkers,
11321 parent: function() {
11322 return stack[stack.length - 2]; // last one is current node
11324 stack: function() {
11330 /* -----[ Scope and mangling ]----- */
11332 function Scope(parent) {
11333 this.names = {}; // names defined in this scope
11334 this.mangled = {}; // mangled names (orig.name => mangled)
11335 this.rev_mangled = {}; // reverse lookup (mangled => orig.name)
11336 this.cname = -1; // current mangled name
11337 this.refs = {}; // names referenced from this scope
11338 this.uses_with = false; // will become TRUE if with() is detected in this or any subscopes
11339 this.uses_eval = false; // will become TRUE if eval() is detected in this or any subscopes
11340 this.directives = []; // directives activated from this scope
11341 this.parent = parent; // parent scope
11342 this.children = []; // sub-scopes
11344 this.level = parent.level + 1;
11345 parent.children.push(this);
11351 function base54_digits() {
11352 if (typeof DIGITS_OVERRIDE_FOR_TESTING != "undefined")
11353 return DIGITS_OVERRIDE_FOR_TESTING;
11355 return "etnrisouaflchpdvmgybwESxTNCkLAOM_DPHBjFIqRUzWXV$JKQGYZ0516372984";
11358 var base54 = (function(){
11359 var DIGITS = base54_digits();
11360 return function(num) {
11361 var ret = "", base = 54;
11363 ret += DIGITS.charAt(num % base);
11364 num = Math.floor(num / base);
11371 Scope.prototype = {
11372 has: function(name) {
11373 for (var s = this; s; s = s.parent)
11374 if (HOP(s.names, name))
11377 has_mangled: function(mname) {
11378 for (var s = this; s; s = s.parent)
11379 if (HOP(s.rev_mangled, mname))
11382 toJSON: function() {
11385 uses_eval: this.uses_eval,
11386 uses_with: this.uses_with
11390 next_mangled: function() {
11391 // we must be careful that the new mangled name:
11393 // 1. doesn't shadow a mangled name from a parent
11394 // scope, unless we don't reference the original
11395 // name from this scope OR from any sub-scopes!
11396 // This will get slow.
11398 // 2. doesn't shadow an original name from a parent
11399 // scope, in the event that the name is not mangled
11400 // in the parent scope and we reference that name
11401 // here OR IN ANY SUBSCOPES!
11403 // 3. doesn't shadow a name that is referenced but not
11404 // defined (possibly global defined elsewhere).
11406 var m = base54(++this.cname), prior;
11409 prior = this.has_mangled(m);
11410 if (prior && this.refs[prior.rev_mangled[m]] === prior)
11414 prior = this.has(m);
11415 if (prior && prior !== this && this.refs[m] === prior && !prior.has_mangled(m))
11419 if (HOP(this.refs, m) && this.refs[m] == null)
11422 // I got "do" once. :-/
11423 if (!is_identifier(m))
11429 set_mangle: function(name, m) {
11430 this.rev_mangled[m] = name;
11431 return this.mangled[name] = m;
11433 get_mangled: function(name, newMangle) {
11434 if (this.uses_eval || this.uses_with) return name; // no mangle if eval or with is in use
11435 var s = this.has(name);
11436 if (!s) return name; // not in visible scope, no mangle
11437 if (HOP(s.mangled, name)) return s.mangled[name]; // already mangled in this scope
11438 if (!newMangle) return name; // not found and no mangling requested
11439 return s.set_mangle(name, s.next_mangled());
11441 references: function(name) {
11442 return name && !this.parent || this.uses_with || this.uses_eval || this.refs[name];
11444 define: function(name, type) {
11445 if (name != null) {
11446 if (type == "var" || !HOP(this.names, name))
11447 this.names[name] = type || "var";
11451 active_directive: function(dir) {
11452 return member(dir, this.directives) || this.parent && this.parent.active_directive(dir);
11456 function ast_add_scope(ast) {
11458 var current_scope = null;
11459 var w = ast_walker(), walk = w.walk;
11460 var having_eval = [];
11462 function with_new_scope(cont) {
11463 current_scope = new Scope(current_scope);
11464 current_scope.labels = new Scope();
11465 var ret = current_scope.body = cont();
11466 ret.scope = current_scope;
11467 current_scope = current_scope.parent;
11471 function define(name, type) {
11472 return current_scope.define(name, type);
11475 function reference(name) {
11476 current_scope.refs[name] = true;
11479 function _lambda(name, args, body) {
11480 var is_defun = this[0] == "defun";
11481 return [ this[0], is_defun ? define(name, "defun") : name, args, with_new_scope(function(){
11482 if (!is_defun) define(name, "lambda");
11483 MAP(args, function(name){ define(name, "arg") });
11484 return MAP(body, walk);
11488 function _vardefs(type) {
11489 return function(defs) {
11490 MAP(defs, function(d){
11491 define(d[0], type);
11492 if (d[1]) reference(d[0]);
11497 function _breacont(label) {
11499 current_scope.labels.refs[label] = true;
11502 return with_new_scope(function(){
11504 var ret = w.with_walkers({
11505 "function": _lambda,
11507 "label": function(name, stat) { current_scope.labels.define(name) },
11508 "break": _breacont,
11509 "continue": _breacont,
11510 "with": function(expr, block) {
11511 for (var s = current_scope; s; s = s.parent)
11512 s.uses_with = true;
11514 "var": _vardefs("var"),
11515 "const": _vardefs("const"),
11516 "try": function(t, c, f) {
11517 if (c != null) return [
11520 [ define(c[0], "catch"), MAP(c[1], walk) ],
11521 f != null ? MAP(f, walk) : null
11524 "name": function(name) {
11525 if (name == "eval")
11526 having_eval.push(current_scope);
11533 // the reason why we need an additional pass here is
11534 // that names can be used prior to their definition.
11536 // scopes where eval was detected and their parents
11537 // are marked with uses_eval, unless they define the
11539 MAP(having_eval, function(scope){
11540 if (!scope.has("eval")) while (scope) {
11541 scope.uses_eval = true;
11542 scope = scope.parent;
11546 // for referenced names it might be useful to know
11547 // their origin scope. current_scope here is the
11549 function fixrefs(scope, i) {
11550 // do children first; order shouldn't matter
11551 for (i = scope.children.length; --i >= 0;)
11552 fixrefs(scope.children[i]);
11553 for (i in scope.refs) if (HOP(scope.refs, i)) {
11554 // find origin scope and propagate the reference to origin
11555 for (var origin = scope.has(i), s = scope; s; s = s.parent) {
11556 s.refs[i] = origin;
11557 if (s === origin) break;
11561 fixrefs(current_scope);
11568 /* -----[ mangle names ]----- */
11570 function ast_mangle(ast, options) {
11571 var w = ast_walker(), walk = w.walk, scope;
11572 options = defaults(options, {
11577 no_functions : false
11580 function get_mangled(name, newMangle) {
11581 if (!options.mangle) return name;
11582 if (!options.toplevel && !scope.parent) return name; // don't mangle toplevel
11583 if (options.except && member(name, options.except))
11585 if (options.no_functions && HOP(scope.names, name) &&
11586 (scope.names[name] == 'defun' || scope.names[name] == 'lambda'))
11588 return scope.get_mangled(name, newMangle);
11591 function get_define(name) {
11592 if (options.defines) {
11593 // we always lookup a defined symbol for the current scope FIRST, so declared
11594 // vars trump a DEFINE symbol, but if no such var is found, then match a DEFINE value
11595 if (!scope.has(name)) {
11596 if (HOP(options.defines, name)) {
11597 return options.defines[name];
11604 function _lambda(name, args, body) {
11605 if (!options.no_functions && options.mangle) {
11606 var is_defun = this[0] == "defun", extra;
11608 if (is_defun) name = get_mangled(name);
11609 else if (body.scope.references(name)) {
11611 if (!(scope.uses_eval || scope.uses_with))
11612 name = extra[name] = scope.next_mangled();
11614 extra[name] = name;
11619 body = with_scope(body.scope, function(){
11620 args = MAP(args, function(name){ return get_mangled(name) });
11621 return MAP(body, walk);
11623 return [ this[0], name, args, body ];
11626 function with_scope(s, cont, extra) {
11627 var _scope = scope;
11629 if (extra) for (var i in extra) if (HOP(extra, i)) {
11630 s.set_mangle(i, extra[i]);
11632 for (var i in s.names) if (HOP(s.names, i)) {
11633 get_mangled(i, true);
11641 function _vardefs(defs) {
11642 return [ this[0], MAP(defs, function(d){
11643 return [ get_mangled(d[0]), walk(d[1]) ];
11647 function _breacont(label) {
11648 if (label) return [ this[0], scope.labels.get_mangled(label) ];
11651 return w.with_walkers({
11652 "function": _lambda,
11653 "defun": function() {
11654 // move function declarations to the top when
11655 // they are not in some block.
11656 var ast = _lambda.apply(this, arguments);
11657 switch (w.parent()[0]) {
11661 return MAP.at_top(ast);
11665 "label": function(label, stat) {
11666 if (scope.labels.refs[label]) return [
11668 scope.labels.get_mangled(label, true),
11673 "break": _breacont,
11674 "continue": _breacont,
11677 "name": function(name) {
11678 return get_define(name) || [ this[0], get_mangled(name) ];
11680 "try": function(t, c, f) {
11683 c != null ? [ get_mangled(c[0]), MAP(c[1], walk) ] : null,
11684 f != null ? MAP(f, walk) : null ];
11686 "toplevel": function(body) {
11688 return with_scope(self.scope, function(){
11689 return [ self[0], MAP(body, walk) ];
11692 "directive": function() {
11693 return MAP.at_top(this);
11696 return walk(ast_add_scope(ast));
11701 - compress foo["bar"] into foo.bar,
11702 - remove block brackets {} where possible
11703 - join consecutive var declarations
11704 - various optimizations for IFs:
11705 - if (cond) foo(); else bar(); ==> cond?foo():bar();
11706 - if (cond) foo(); ==> cond&&foo();
11707 - if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); // also for throw
11708 - if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
11711 var warn = function(){};
11713 function best_of(ast1, ast2) {
11714 return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1;
11717 function last_stat(b) {
11718 if (b[0] == "block" && b[1] && b[1].length > 0)
11719 return b[1][b[1].length - 1];
11723 function aborts(t) {
11724 if (t) switch (last_stat(t)[0]) {
11733 function boolean_expr(expr) {
11734 return ( (expr[0] == "unary-prefix"
11735 && member(expr[1], [ "!", "delete" ])) ||
11737 (expr[0] == "binary"
11738 && member(expr[1], [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ])) ||
11740 (expr[0] == "binary"
11741 && member(expr[1], [ "&&", "||" ])
11742 && boolean_expr(expr[2])
11743 && boolean_expr(expr[3])) ||
11745 (expr[0] == "conditional"
11746 && boolean_expr(expr[2])
11747 && boolean_expr(expr[3])) ||
11749 (expr[0] == "assign"
11750 && expr[1] === true
11751 && boolean_expr(expr[3])) ||
11754 && boolean_expr(expr[expr.length - 1]))
11758 function empty(b) {
11759 return !b || (b[0] == "block" && (!b[1] || b[1].length == 0));
11762 function is_string(node) {
11763 return (node[0] == "string" ||
11764 node[0] == "unary-prefix" && node[1] == "typeof" ||
11765 node[0] == "binary" && node[1] == "+" &&
11766 (is_string(node[2]) || is_string(node[3])));
11769 var when_constant = (function(){
11771 var $NOT_CONSTANT = {};
11773 // this can only evaluate constant expressions. If it finds anything
11774 // not constant, it throws $NOT_CONSTANT.
11775 function evaluate(expr) {
11783 case "true": return true;
11784 case "false": return false;
11785 case "null": return null;
11788 case "unary-prefix":
11790 case "!": return !evaluate(expr[2]);
11791 case "typeof": return typeof evaluate(expr[2]);
11792 case "~": return ~evaluate(expr[2]);
11793 case "-": return -evaluate(expr[2]);
11794 case "+": return +evaluate(expr[2]);
11798 var left = expr[2], right = expr[3];
11800 case "&&" : return evaluate(left) && evaluate(right);
11801 case "||" : return evaluate(left) || evaluate(right);
11802 case "|" : return evaluate(left) | evaluate(right);
11803 case "&" : return evaluate(left) & evaluate(right);
11804 case "^" : return evaluate(left) ^ evaluate(right);
11805 case "+" : return evaluate(left) + evaluate(right);
11806 case "*" : return evaluate(left) * evaluate(right);
11807 case "/" : return evaluate(left) / evaluate(right);
11808 case "%" : return evaluate(left) % evaluate(right);
11809 case "-" : return evaluate(left) - evaluate(right);
11810 case "<<" : return evaluate(left) << evaluate(right);
11811 case ">>" : return evaluate(left) >> evaluate(right);
11812 case ">>>" : return evaluate(left) >>> evaluate(right);
11813 case "==" : return evaluate(left) == evaluate(right);
11814 case "===" : return evaluate(left) === evaluate(right);
11815 case "!=" : return evaluate(left) != evaluate(right);
11816 case "!==" : return evaluate(left) !== evaluate(right);
11817 case "<" : return evaluate(left) < evaluate(right);
11818 case "<=" : return evaluate(left) <= evaluate(right);
11819 case ">" : return evaluate(left) > evaluate(right);
11820 case ">=" : return evaluate(left) >= evaluate(right);
11821 case "in" : return evaluate(left) in evaluate(right);
11822 case "instanceof" : return evaluate(left) instanceof evaluate(right);
11825 throw $NOT_CONSTANT;
11828 return function(expr, yes, no) {
11830 var val = evaluate(expr), ast;
11831 switch (typeof val) {
11832 case "string": ast = [ "string", val ]; break;
11833 case "number": ast = [ "num", val ]; break;
11834 case "boolean": ast = [ "name", String(val) ]; break;
11836 if (val === null) { ast = [ "atom", "null" ]; break; }
11837 throw new Error("Can't handle constant of type: " + (typeof val));
11839 return yes.call(expr, ast, val);
11841 if (ex === $NOT_CONSTANT) {
11842 if (expr[0] == "binary"
11843 && (expr[1] == "===" || expr[1] == "!==")
11844 && ((is_string(expr[2]) && is_string(expr[3]))
11845 || (boolean_expr(expr[2]) && boolean_expr(expr[3])))) {
11846 expr[1] = expr[1].substr(0, 2);
11848 else if (no && expr[0] == "binary"
11849 && (expr[1] == "||" || expr[1] == "&&")) {
11850 // the whole expression is not constant but the lval may be...
11852 var lval = evaluate(expr[2]);
11853 expr = ((expr[1] == "&&" && (lval ? expr[3] : lval)) ||
11854 (expr[1] == "||" && (lval ? lval : expr[3])) ||
11857 // IGNORE... lval is not constant
11860 return no ? no.call(expr, expr) : null;
11868 function warn_unreachable(ast) {
11870 warn("Dropping unreachable code: " + gen_code(ast, true));
11873 function prepare_ifs(ast) {
11874 var w = ast_walker(), walk = w.walk;
11875 // In this first pass, we rewrite ifs which abort with no else with an
11876 // if-else. For example:
11884 // is rewritten into:
11892 function redo_if(statements) {
11893 statements = MAP(statements, walk);
11895 for (var i = 0; i < statements.length; ++i) {
11896 var fi = statements[i];
11897 if (fi[0] != "if") continue;
11899 if (fi[3]) continue;
11902 if (!aborts(t)) continue;
11904 var conditional = walk(fi[1]);
11906 var e_body = redo_if(statements.slice(i + 1));
11907 var e = e_body.length == 1 ? e_body[0] : [ "block", e_body ];
11909 return statements.slice(0, i).concat([ [
11911 conditional, // conditional
11920 function redo_if_lambda(name, args, body) {
11921 body = redo_if(body);
11922 return [ this[0], name, args, body ];
11925 function redo_if_block(statements) {
11926 return [ this[0], statements != null ? redo_if(statements) : null ];
11929 return w.with_walkers({
11930 "defun": redo_if_lambda,
11931 "function": redo_if_lambda,
11932 "block": redo_if_block,
11933 "splice": redo_if_block,
11934 "toplevel": function(statements) {
11935 return [ this[0], redo_if(statements) ];
11937 "try": function(t, c, f) {
11941 c != null ? [ c[0], redo_if(c[1]) ] : null,
11942 f != null ? redo_if(f) : null
11950 function for_side_effects(ast, handler) {
11951 var w = ast_walker(), walk = w.walk;
11952 var $stop = {}, $restart = {};
11953 function stop() { throw $stop };
11954 function restart() { throw $restart };
11955 function found(){ return handler.call(this, this, w, stop, restart) };
11956 function unary(op) {
11957 if (op == "++" || op == "--")
11958 return found.apply(this, arguments);
11960 function binary(op) {
11961 if (op == "&&" || op == "||")
11962 return found.apply(this, arguments);
11964 return w.with_walkers({
11980 "unary-prefix": unary,
11981 "unary-postfix": unary,
11982 "conditional": found,
11990 if (ex === $stop) break;
11991 if (ex === $restart) continue;
11997 function ast_lift_variables(ast) {
11998 var w = ast_walker(), walk = w.walk, scope;
11999 function do_body(body, env) {
12000 var _scope = scope;
12002 body = MAP(body, walk);
12003 var hash = {}, names = MAP(env.names, function(type, name){
12004 if (type != "var") return MAP.skip;
12005 if (!env.references(name)) return MAP.skip;
12009 if (names.length > 0) {
12010 // looking for assignments to any of these variables.
12011 // we can save considerable space by moving the definitions
12012 // in the var declaration.
12013 for_side_effects([ "block", body ], function(ast, walker, stop, restart) {
12014 if (ast[0] == "assign"
12016 && ast[2][0] == "name"
12017 && HOP(hash, ast[2][1])) {
12018 // insert the definition into the var declaration
12019 for (var i = names.length; --i >= 0;) {
12020 if (names[i][0] == ast[2][1]) {
12021 if (names[i][1]) // this name already defined, we must stop
12023 names[i][1] = ast[3]; // definition
12024 names.push(names.splice(i, 1)[0]);
12028 // remove this assignment from the AST.
12029 var p = walker.parent();
12030 if (p[0] == "seq") {
12032 a.unshift(0, p.length);
12033 p.splice.apply(p, a);
12035 else if (p[0] == "stat") {
12036 p.splice(0, p.length, "block"); // empty statement
12045 body.unshift([ "var", names ]);
12050 function _vardefs(defs) {
12052 for (var i = defs.length; --i >= 0;) {
12054 if (!d[1]) continue;
12055 d = [ "assign", true, [ "name", d[0] ], d[1] ];
12056 if (ret == null) ret = d;
12057 else ret = [ "seq", d, ret ];
12059 if (ret == null && w.parent()[0] != "for") {
12060 if (w.parent()[0] == "for-in")
12061 return [ "name", defs[0][0] ];
12064 return [ "stat", ret ];
12066 function _toplevel(body) {
12067 return [ this[0], do_body(body, this.scope) ];
12069 return w.with_walkers({
12070 "function": function(name, args, body){
12071 for (var i = args.length; --i >= 0 && !body.scope.references(args[i]);)
12073 if (!body.scope.references(name)) name = null;
12074 return [ this[0], name, args, do_body(body, body.scope) ];
12076 "defun": function(name, args, body){
12077 if (!scope.references(name)) return MAP.skip;
12078 for (var i = args.length; --i >= 0 && !body.scope.references(args[i]);)
12080 return [ this[0], name, args, do_body(body, body.scope) ];
12083 "toplevel": _toplevel
12085 return walk(ast_add_scope(ast));
12089 function ast_squeeze(ast, options) {
12090 ast = squeeze_1(ast, options);
12091 ast = squeeze_2(ast, options);
12095 function squeeze_1(ast, options) {
12096 options = defaults(options, {
12099 no_warnings : false,
12104 var w = ast_walker(), walk = w.walk, scope;
12106 function negate(c) {
12107 var not_c = [ "unary-prefix", "!", c ];
12109 case "unary-prefix":
12110 return c[1] == "!" && boolean_expr(c[2]) ? c[2] : not_c;
12113 c[c.length - 1] = negate(c[c.length - 1]);
12115 case "conditional":
12116 return best_of(not_c, [ "conditional", c[1], negate(c[2]), negate(c[3]) ]);
12118 var op = c[1], left = c[2], right = c[3];
12119 if (!options.keep_comps) switch (op) {
12120 case "<=" : return [ "binary", ">", left, right ];
12121 case "<" : return [ "binary", ">=", left, right ];
12122 case ">=" : return [ "binary", "<", left, right ];
12123 case ">" : return [ "binary", "<=", left, right ];
12126 case "==" : return [ "binary", "!=", left, right ];
12127 case "!=" : return [ "binary", "==", left, right ];
12128 case "===" : return [ "binary", "!==", left, right ];
12129 case "!==" : return [ "binary", "===", left, right ];
12130 case "&&" : return best_of(not_c, [ "binary", "||", negate(left), negate(right) ]);
12131 case "||" : return best_of(not_c, [ "binary", "&&", negate(left), negate(right) ]);
12138 function make_conditional(c, t, e) {
12139 var make_real_conditional = function() {
12140 if (c[0] == "unary-prefix" && c[1] == "!") {
12141 return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ];
12143 return e ? best_of(
12144 [ "conditional", c, t, e ],
12145 [ "conditional", negate(c), e, t ]
12146 ) : [ "binary", "&&", c, t ];
12149 // shortcut the conditional if the expression has a constant value
12150 return when_constant(c, function(ast, val){
12151 warn_unreachable(val ? e : t);
12152 return (val ? t : e);
12153 }, make_real_conditional);
12156 function rmblock(block) {
12157 if (block != null && block[0] == "block" && block[1]) {
12158 if (block[1].length == 1)
12159 block = block[1][0];
12160 else if (block[1].length == 0)
12161 block = [ "block" ];
12166 function _lambda(name, args, body) {
12167 return [ this[0], name, args, tighten(body, "lambda") ];
12170 // this function does a few things:
12171 // 1. discard useless blocks
12172 // 2. join consecutive var declarations
12173 // 3. remove obviously dead code
12174 // 4. transform consecutive statements using the comma operator
12175 // 5. if block_type == "lambda" and it detects constructs like if(foo) return ... - rewrite like if (!foo) { ... }
12176 function tighten(statements, block_type) {
12177 statements = MAP(statements, walk);
12179 statements = statements.reduce(function(a, stat){
12180 if (stat[0] == "block") {
12182 a.push.apply(a, stat[1]);
12190 statements = (function(a, prev){
12191 statements.forEach(function(cur){
12192 if (prev && ((cur[0] == "var" && prev[0] == "var") ||
12193 (cur[0] == "const" && prev[0] == "const"))) {
12194 prev[1] = prev[1].concat(cur[1]);
12203 if (options.dead_code) statements = (function(a, has_quit){
12204 statements.forEach(function(st){
12206 if (st[0] == "function" || st[0] == "defun") {
12209 else if (st[0] == "var" || st[0] == "const") {
12210 if (!options.no_warnings)
12211 warn("Variables declared in unreachable code");
12212 st[1] = MAP(st[1], function(def){
12213 if (def[1] && !options.no_warnings)
12214 warn_unreachable([ "assign", true, [ "name", def[0] ], def[1] ]);
12219 else if (!options.no_warnings)
12220 warn_unreachable(st);
12224 if (member(st[0], [ "return", "throw", "break", "continue" ]))
12231 if (options.make_seqs) statements = (function(a, prev) {
12232 statements.forEach(function(cur){
12233 if (prev && prev[0] == "stat" && cur[0] == "stat") {
12234 prev[1] = [ "seq", prev[1], cur[1] ];
12241 && a[a.length-2][0] == "stat"
12242 && (a[a.length-1][0] == "return" || a[a.length-1][0] == "throw")
12243 && a[a.length-1][1])
12245 a.splice(a.length - 2, 2,
12246 [ a[a.length-1][0],
12247 [ "seq", a[a.length-2][1], a[a.length-1][1] ]]);
12252 // this increases jQuery by 1K. Probably not such a good idea after all..
12253 // part of this is done in prepare_ifs anyway.
12254 // if (block_type == "lambda") statements = (function(i, a, stat){
12255 // while (i < statements.length) {
12256 // stat = statements[i++];
12257 // if (stat[0] == "if" && !stat[3]) {
12258 // if (stat[2][0] == "return" && stat[2][1] == null) {
12259 // a.push(make_if(negate(stat[1]), [ "block", statements.slice(i) ]));
12262 // var last = last_stat(stat[2]);
12263 // if (last[0] == "return" && last[1] == null) {
12264 // a.push(make_if(stat[1], [ "block", stat[2][1].slice(0, -1) ], [ "block", statements.slice(i) ]));
12276 function make_if(c, t, e) {
12277 return when_constant(c, function(ast, val){
12280 warn_unreachable(e);
12281 return t || [ "block" ];
12284 warn_unreachable(t);
12285 return e || [ "block" ];
12288 return make_real_if(c, t, e);
12292 function abort_else(c, t, e) {
12293 var ret = [ [ "if", negate(c), e ] ];
12294 if (t[0] == "block") {
12295 if (t[1]) ret = ret.concat(t[1]);
12299 return walk([ "block", ret ]);
12302 function make_real_if(c, t, e) {
12307 if (empty(e) && empty(t))
12308 return [ "stat", c ];
12314 } else if (empty(e)) {
12317 // if we have both else and then, maybe it makes sense to switch them?
12319 var a = gen_code(c);
12321 var b = gen_code(n);
12322 if (b.length < a.length) {
12330 var ret = [ "if", c, t, e ];
12331 if (t[0] == "if" && empty(t[3]) && empty(e)) {
12332 ret = best_of(ret, walk([ "if", [ "binary", "&&", c, t[1] ], t[2] ]));
12334 else if (t[0] == "stat") {
12336 if (e[0] == "stat")
12337 ret = best_of(ret, [ "stat", make_conditional(c, t[1], e[1]) ]);
12338 else if (aborts(e))
12339 ret = abort_else(c, t, e);
12342 ret = best_of(ret, [ "stat", make_conditional(c, t[1]) ]);
12345 else if (e && t[0] == e[0] && (t[0] == "return" || t[0] == "throw") && t[1] && e[1]) {
12346 ret = best_of(ret, [ t[0], make_conditional(c, t[1], e[1] ) ]);
12348 else if (e && aborts(t)) {
12349 ret = [ [ "if", c, t ] ];
12350 if (e[0] == "block") {
12351 if (e[1]) ret = ret.concat(e[1]);
12356 ret = walk([ "block", ret ]);
12358 else if (t && aborts(e)) {
12359 ret = abort_else(c, t, e);
12364 function _do_while(cond, body) {
12365 return when_constant(cond, function(cond, val){
12367 warn_unreachable(body);
12368 return [ "block" ];
12370 return [ "for", null, null, null, walk(body) ];
12375 return w.with_walkers({
12376 "sub": function(expr, subscript) {
12377 if (subscript[0] == "string") {
12378 var name = subscript[1];
12379 if (is_identifier(name))
12380 return [ "dot", walk(expr), name ];
12381 else if (/^[1-9][0-9]*$/.test(name) || name === "0")
12382 return [ "sub", walk(expr), [ "num", parseInt(name, 10) ] ];
12386 "toplevel": function(body) {
12387 return [ "toplevel", tighten(body) ];
12389 "switch": function(expr, body) {
12390 var last = body.length - 1;
12391 return [ "switch", walk(expr), MAP(body, function(branch, i){
12392 var block = tighten(branch[1]);
12393 if (i == last && block.length > 0) {
12394 var node = block[block.length - 1];
12395 if (node[0] == "break" && !node[1])
12398 return [ branch[0] ? walk(branch[0]) : null, block ];
12401 "function": _lambda,
12403 "block": function(body) {
12404 if (body) return rmblock([ "block", tighten(body) ]);
12406 "binary": function(op, left, right) {
12407 return when_constant([ "binary", op, walk(left), walk(right) ], function yes(c){
12408 return best_of(walk(c), this);
12411 if(op != "==" && op != "!=") return;
12412 var l = walk(left), r = walk(right);
12413 if(l && l[0] == "unary-prefix" && l[1] == "!" && l[2][0] == "num")
12414 left = ['num', +!l[2][1]];
12415 else if (r && r[0] == "unary-prefix" && r[1] == "!" && r[2][0] == "num")
12416 right = ['num', +!r[2][1]];
12417 return ["binary", op, left, right];
12421 "conditional": function(c, t, e) {
12422 return make_conditional(walk(c), walk(t), walk(e));
12424 "try": function(t, c, f) {
12428 c != null ? [ c[0], tighten(c[1]) ] : null,
12429 f != null ? tighten(f) : null
12432 "unary-prefix": function(op, expr) {
12434 var ret = [ "unary-prefix", op, expr ];
12436 ret = best_of(ret, negate(expr));
12437 return when_constant(ret, function(ast, val){
12438 return walk(ast); // it's either true or false, so minifies to !0 or !1
12439 }, function() { return ret });
12441 "name": function(name) {
12443 case "true": return [ "unary-prefix", "!", [ "num", 0 ]];
12444 case "false": return [ "unary-prefix", "!", [ "num", 1 ]];
12447 "while": _do_while,
12448 "assign": function(op, lvalue, rvalue) {
12449 lvalue = walk(lvalue);
12450 rvalue = walk(rvalue);
12451 var okOps = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
12452 if (op === true && lvalue[0] === "name" && rvalue[0] === "binary" &&
12453 ~okOps.indexOf(rvalue[1]) && rvalue[2][0] === "name" &&
12454 rvalue[2][1] === lvalue[1]) {
12455 return [ this[0], rvalue[1], lvalue, rvalue[3] ]
12457 return [ this[0], op, lvalue, rvalue ];
12459 "call": function(expr, args) {
12461 if (options.unsafe && expr[0] == "dot" && expr[1][0] == "string" && expr[2] == "toString") {
12464 return [ this[0], expr, MAP(args, walk) ];
12466 "num": function (num) {
12467 if (!isFinite(num))
12468 return [ "binary", "/", num === 1 / 0
12469 ? [ "num", 1 ] : num === -1 / 0
12470 ? [ "unary-prefix", "-", [ "num", 1 ] ]
12471 : [ "num", 0 ], [ "num", 0 ] ];
12473 return [ this[0], num ];
12476 return walk(prepare_ifs(walk(prepare_ifs(ast))));
12480 function squeeze_2(ast, options) {
12481 var w = ast_walker(), walk = w.walk, scope;
12482 function with_scope(s, cont) {
12483 var save = scope, ret;
12489 function lambda(name, args, body) {
12490 return [ this[0], name, args, with_scope(body.scope, curry(MAP, body, walk)) ];
12492 return w.with_walkers({
12493 "directive": function(dir) {
12494 if (scope.active_directive(dir))
12495 return [ "block" ];
12496 scope.directives.push(dir);
12498 "toplevel": function(body) {
12499 return [ this[0], with_scope(this.scope, curry(MAP, body, walk)) ];
12501 "function": lambda,
12504 return walk(ast_add_scope(ast));
12508 /* -----[ re-generate code from the AST ]----- */
12510 var DOT_CALL_NO_PARENS = jsp.array_to_hash([
12522 function make_string(str, ascii_only) {
12523 var dq = 0, sq = 0;
12524 str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){
12526 case "\\": return "\\\\";
12527 case "\b": return "\\b";
12528 case "\f": return "\\f";
12529 case "\n": return "\\n";
12530 case "\r": return "\\r";
12531 case "\u2028": return "\\u2028";
12532 case "\u2029": return "\\u2029";
12533 case '"': ++dq; return '"';
12534 case "'": ++sq; return "'";
12535 case "\0": return "\\0";
12539 if (ascii_only) str = to_ascii(str);
12540 if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'";
12541 else return '"' + str.replace(/\x22/g, '\\"') + '"';
12544 function to_ascii(str) {
12545 return str.replace(/[\u0080-\uffff]/g, function(ch) {
12546 var code = ch.charCodeAt(0).toString(16);
12547 while (code.length < 4) code = "0" + code;
12548 return "\\u" + code;
12552 var SPLICE_NEEDS_BRACKETS = jsp.array_to_hash([ "if", "while", "do", "for", "for-in", "with" ]);
12554 function gen_code(ast, options) {
12555 options = defaults(options, {
12558 quote_keys : false,
12559 space_colon : false,
12561 ascii_only : false,
12562 inline_script: false
12564 var beautify = !!options.beautify;
12565 var indentation = 0,
12566 newline = beautify ? "\n" : "",
12567 space = beautify ? " " : "";
12569 function encode_string(str) {
12570 var ret = make_string(str, options.ascii_only);
12571 if (options.inline_script)
12572 ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
12576 function make_name(name) {
12577 name = name.toString();
12578 if (options.ascii_only)
12579 name = to_ascii(name);
12583 function indent(line) {
12587 line = repeat_string(" ", options.indent_start + indentation * options.indent_level) + line;
12591 function with_indent(cont, incr) {
12592 if (incr == null) incr = 1;
12593 indentation += incr;
12594 try { return cont.apply(null, slice(arguments, 1)); }
12595 finally { indentation -= incr; }
12598 function last_char(str) {
12599 str = str.toString();
12600 return str.charAt(str.length - 1);
12603 function first_char(str) {
12604 return str.toString().charAt(0);
12607 function add_spaces(a) {
12609 return a.join(" ");
12611 for (var i = 0; i < a.length; ++i) {
12612 var next = a[i + 1];
12615 ((is_identifier_char(last_char(a[i])) && (is_identifier_char(first_char(next))
12616 || first_char(next) == "\\")) ||
12617 (/[\+\-]$/.test(a[i].toString()) && /^[\+\-]/.test(next.toString()) ||
12618 last_char(a[i]) == "/" && first_char(next) == "/"))) {
12625 function add_commas(a) {
12626 return a.join("," + space);
12629 function parenthesize(expr) {
12630 var gen = make(expr);
12631 for (var i = 1; i < arguments.length; ++i) {
12632 var el = arguments[i];
12633 if ((el instanceof Function && el(expr)) || expr[0] == el)
12634 return "(" + gen + ")";
12639 function best_of(a) {
12640 if (a.length == 1) {
12643 if (a.length == 2) {
12646 return a.length <= b.length ? a : b;
12648 return best_of([ a[0], best_of(a.slice(1)) ]);
12651 function needs_parens(expr) {
12652 if (expr[0] == "function" || expr[0] == "object") {
12653 // dot/call on a literal function requires the
12654 // function literal itself to be parenthesized
12655 // only if it's the first "thing" in a
12656 // statement. This means that the parent is
12657 // "stat", but it could also be a "seq" and
12658 // we're the first in this "seq" and the
12659 // parent is "stat", and so on. Messy stuff,
12660 // but it worths the trouble.
12661 var a = slice(w.stack()), self = a.pop(), p = a.pop();
12663 if (p[0] == "stat") return true;
12664 if (((p[0] == "seq" || p[0] == "call" || p[0] == "dot" || p[0] == "sub" || p[0] == "conditional") && p[1] === self) ||
12665 ((p[0] == "binary" || p[0] == "assign" || p[0] == "unary-postfix") && p[2] === self)) {
12673 return !HOP(DOT_CALL_NO_PARENS, expr[0]);
12676 function make_num(num) {
12677 var str = num.toString(10), a = [ str.replace(/^0\./, ".").replace('e+', 'e') ], m;
12678 if (Math.floor(num) === num) {
12680 a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
12681 "0" + num.toString(8)); // same.
12683 a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
12684 "-0" + (-num).toString(8)); // same.
12686 if ((m = /^(.*?)(0+)$/.exec(num))) {
12687 a.push(m[1] + "e" + m[2].length);
12689 } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
12690 a.push(m[2] + "e-" + (m[1].length + m[2].length),
12691 str.substr(str.indexOf(".")));
12696 var w = ast_walker();
12698 return w.with_walkers({
12699 "string": encode_string,
12702 "debugger": function(){ return "debugger;" },
12703 "toplevel": function(statements) {
12704 return make_block_statements(statements)
12705 .join(newline + newline);
12707 "splice": function(statements) {
12708 var parent = w.parent();
12709 if (HOP(SPLICE_NEEDS_BRACKETS, parent)) {
12710 // we need block brackets in this case
12711 return make_block.apply(this, arguments);
12713 return MAP(make_block_statements(statements, true),
12714 function(line, i) {
12715 // the first line is already indented
12716 return i > 0 ? indent(line) : line;
12720 "block": make_block,
12721 "var": function(defs) {
12722 return "var " + add_commas(MAP(defs, make_1vardef)) + ";";
12724 "const": function(defs) {
12725 return "const " + add_commas(MAP(defs, make_1vardef)) + ";";
12727 "try": function(tr, ca, fi) {
12728 var out = [ "try", make_block(tr) ];
12729 if (ca) out.push("catch", "(" + ca[0] + ")", make_block(ca[1]));
12730 if (fi) out.push("finally", make_block(fi));
12731 return add_spaces(out);
12733 "throw": function(expr) {
12734 return add_spaces([ "throw", make(expr) ]) + ";";
12736 "new": function(ctor, args) {
12737 args = args.length > 0 ? "(" + add_commas(MAP(args, function(expr){
12738 return parenthesize(expr, "seq");
12740 return add_spaces([ "new", parenthesize(ctor, "seq", "binary", "conditional", "assign", function(expr){
12741 var w = ast_walker(), has_call = {};
12744 "call": function() { throw has_call },
12745 "function": function() { return this }
12750 if (ex === has_call)
12756 "switch": function(expr, body) {
12757 return add_spaces([ "switch", "(" + make(expr) + ")", make_switch_block(body) ]);
12759 "break": function(label) {
12762 out += " " + make_name(label);
12765 "continue": function(label) {
12766 var out = "continue";
12768 out += " " + make_name(label);
12771 "conditional": function(co, th, el) {
12772 return add_spaces([ parenthesize(co, "assign", "seq", "conditional"), "?",
12773 parenthesize(th, "seq"), ":",
12774 parenthesize(el, "seq") ]);
12776 "assign": function(op, lvalue, rvalue) {
12777 if (op && op !== true) op += "=";
12779 return add_spaces([ make(lvalue), op, parenthesize(rvalue, "seq") ]);
12781 "dot": function(expr) {
12782 var out = make(expr), i = 1;
12783 if (expr[0] == "num") {
12784 if (!/[a-f.]/i.test(out))
12786 } else if (expr[0] != "function" && needs_parens(expr))
12787 out = "(" + out + ")";
12788 while (i < arguments.length)
12789 out += "." + make_name(arguments[i++]);
12792 "call": function(func, args) {
12793 var f = make(func);
12794 if (f.charAt(0) != "(" && needs_parens(func))
12796 return f + "(" + add_commas(MAP(args, function(expr){
12797 return parenthesize(expr, "seq");
12800 "function": make_function,
12801 "defun": make_function,
12802 "if": function(co, th, el) {
12803 var out = [ "if", "(" + make(co) + ")", el ? make_then(th) : make(th) ];
12805 out.push("else", make(el));
12807 return add_spaces(out);
12809 "for": function(init, cond, step, block) {
12810 var out = [ "for" ];
12811 init = (init != null ? make(init) : "").replace(/;*\s*$/, ";" + space);
12812 cond = (cond != null ? make(cond) : "").replace(/;*\s*$/, ";" + space);
12813 step = (step != null ? make(step) : "").replace(/;*\s*$/, "");
12814 var args = init + cond + step;
12815 if (args == "; ; ") args = ";;";
12816 out.push("(" + args + ")", make(block));
12817 return add_spaces(out);
12819 "for-in": function(vvar, key, hash, block) {
12820 return add_spaces([ "for", "(" +
12821 (vvar ? make(vvar).replace(/;+$/, "") : make(key)),
12823 make(hash) + ")", make(block) ]);
12825 "while": function(condition, block) {
12826 return add_spaces([ "while", "(" + make(condition) + ")", make(block) ]);
12828 "do": function(condition, block) {
12829 return add_spaces([ "do", make(block), "while", "(" + make(condition) + ")" ]) + ";";
12831 "return": function(expr) {
12832 var out = [ "return" ];
12833 if (expr != null) out.push(make(expr));
12834 return add_spaces(out) + ";";
12836 "binary": function(operator, lvalue, rvalue) {
12837 var left = make(lvalue), right = make(rvalue);
12838 // XXX: I'm pretty sure other cases will bite here.
12839 // we need to be smarter.
12840 // adding parens all the time is the safest bet.
12841 if (member(lvalue[0], [ "assign", "conditional", "seq" ]) ||
12842 lvalue[0] == "binary" && PRECEDENCE[operator] > PRECEDENCE[lvalue[1]] ||
12843 lvalue[0] == "function" && needs_parens(this)) {
12844 left = "(" + left + ")";
12846 if (member(rvalue[0], [ "assign", "conditional", "seq" ]) ||
12847 rvalue[0] == "binary" && PRECEDENCE[operator] >= PRECEDENCE[rvalue[1]] &&
12848 !(rvalue[1] == operator && member(operator, [ "&&", "||", "*" ]))) {
12849 right = "(" + right + ")";
12851 else if (!beautify && options.inline_script && (operator == "<" || operator == "<<")
12852 && rvalue[0] == "regexp" && /^script/i.test(rvalue[1])) {
12853 right = " " + right;
12855 return add_spaces([ left, operator, right ]);
12857 "unary-prefix": function(operator, expr) {
12858 var val = make(expr);
12859 if (!(expr[0] == "num" || (expr[0] == "unary-prefix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr)))
12860 val = "(" + val + ")";
12861 return operator + (jsp.is_alphanumeric_char(operator.charAt(0)) ? " " : "") + val;
12863 "unary-postfix": function(operator, expr) {
12864 var val = make(expr);
12865 if (!(expr[0] == "num" || (expr[0] == "unary-postfix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr)))
12866 val = "(" + val + ")";
12867 return val + operator;
12869 "sub": function(expr, subscript) {
12870 var hash = make(expr);
12871 if (needs_parens(expr))
12872 hash = "(" + hash + ")";
12873 return hash + "[" + make(subscript) + "]";
12875 "object": function(props) {
12876 var obj_needs_parens = needs_parens(this);
12877 if (props.length == 0)
12878 return obj_needs_parens ? "({})" : "{}";
12879 var out = "{" + newline + with_indent(function(){
12880 return MAP(props, function(p){
12881 if (p.length == 3) {
12882 // getter/setter. The name is in p[0], the arg.list in p[1][2], the
12883 // body in p[1][3] and type ("get" / "set") in p[2].
12884 return indent(make_function(p[0], p[1][2], p[1][3], p[2], true));
12886 var key = p[0], val = parenthesize(p[1], "seq");
12887 if (options.quote_keys) {
12888 key = encode_string(key);
12889 } else if ((typeof key == "number" || !beautify && +key + "" == key)
12890 && parseFloat(key) >= 0) {
12891 key = make_num(+key);
12892 } else if (!is_identifier(key)) {
12893 key = encode_string(key);
12895 return indent(add_spaces(beautify && options.space_colon
12896 ? [ key, ":", val ]
12897 : [ key + ":", val ]));
12898 }).join("," + newline);
12899 }) + newline + indent("}");
12900 return obj_needs_parens ? "(" + out + ")" : out;
12902 "regexp": function(rx, mods) {
12903 if (options.ascii_only) rx = to_ascii(rx);
12904 return "/" + rx + "/" + mods;
12906 "array": function(elements) {
12907 if (elements.length == 0) return "[]";
12908 return add_spaces([ "[", add_commas(MAP(elements, function(el, i){
12909 if (!beautify && el[0] == "atom" && el[1] == "undefined") return i === elements.length - 1 ? "," : "";
12910 return parenthesize(el, "seq");
12913 "stat": function(stmt) {
12914 return stmt != null
12915 ? make(stmt).replace(/;*\s*$/, ";")
12918 "seq": function() {
12919 return add_commas(MAP(slice(arguments), make));
12921 "label": function(name, block) {
12922 return add_spaces([ make_name(name), ":", make(block) ]);
12924 "with": function(expr, block) {
12925 return add_spaces([ "with", "(" + make(expr) + ")", make(block) ]);
12927 "atom": function(name) {
12928 return make_name(name);
12930 "directive": function(dir) {
12931 return make_string(dir) + ";";
12933 }, function(){ return make(ast) });
12935 // The squeezer replaces "block"-s that contain only a single
12936 // statement with the statement itself; technically, the AST
12937 // is correct, but this can create problems when we output an
12938 // IF having an ELSE clause where the THEN clause ends in an
12939 // IF *without* an ELSE block (then the outer ELSE would refer
12940 // to the inner IF). This function checks for this case and
12941 // adds the block brackets if needed.
12942 function make_then(th) {
12943 if (th == null) return ";";
12944 if (th[0] == "do") {
12945 // https://github.com/mishoo/UglifyJS/issues/#issue/57
12946 // IE croaks with "syntax error" on code like this:
12947 // if (foo) do ... while(cond); else ...
12948 // we need block brackets around do/while
12949 return make_block([ th ]);
12954 if (type == "if") {
12956 // no else, we must add the block
12957 return make([ "block", [ th ]]);
12960 else if (type == "while" || type == "do") b = b[2];
12961 else if (type == "for" || type == "for-in") b = b[4];
12967 function make_function(name, args, body, keyword, no_parens) {
12968 var out = keyword || "function";
12970 out += " " + make_name(name);
12972 out += "(" + add_commas(MAP(args, make_name)) + ")";
12973 out = add_spaces([ out, make_block(body) ]);
12974 return (!no_parens && needs_parens(this)) ? "(" + out + ")" : out;
12977 function must_has_semicolon(node) {
12981 return empty(node[2]) || must_has_semicolon(node[2]);
12984 return empty(node[4]) || must_has_semicolon(node[4]);
12986 if (empty(node[2]) && !node[3]) return true; // `if' with empty `then' and no `else'
12988 if (empty(node[3])) return true; // `else' present but empty
12989 return must_has_semicolon(node[3]); // dive into the `else' branch
12991 return must_has_semicolon(node[2]); // dive into the `then' branch
12997 function make_block_statements(statements, noindent) {
12998 for (var a = [], last = statements.length - 1, i = 0; i <= last; ++i) {
12999 var stat = statements[i];
13000 var code = make(stat);
13002 if (!beautify && i == last && !must_has_semicolon(stat)) {
13003 code = code.replace(/;+\s*$/, "");
13008 return noindent ? a : MAP(a, indent);
13011 function make_switch_block(body) {
13012 var n = body.length;
13013 if (n == 0) return "{}";
13014 return "{" + newline + MAP(body, function(branch, i){
13015 var has_body = branch[1].length > 0, code = with_indent(function(){
13016 return indent(branch[0]
13017 ? add_spaces([ "case", make(branch[0]) + ":" ])
13019 }, 0.5) + (has_body ? newline + with_indent(function(){
13020 return make_block_statements(branch[1]).join(newline);
13022 if (!beautify && has_body && i < n - 1)
13025 }).join(newline) + newline + indent("}");
13028 function make_block(statements) {
13029 if (!statements) return ";";
13030 if (statements.length == 0) return "{}";
13031 return "{" + newline + with_indent(function(){
13032 return make_block_statements(statements).join(newline);
13033 }) + newline + indent("}");
13036 function make_1vardef(def) {
13037 var name = def[0], val = def[1];
13039 name = add_spaces([ make_name(name), "=", parenthesize(val, "seq") ]);
13045 function split_lines(code, max_line_length) {
13046 var splits = [ 0 ];
13047 jsp.parse(function(){
13048 var next_token = jsp.tokenizer(code);
13049 var last_split = 0;
13051 function current_length(tok) {
13052 return tok.pos - last_split;
13054 function split_here(tok) {
13055 last_split = tok.pos;
13056 splits.push(last_split);
13059 var tok = next_token.apply(this, arguments);
13062 if (prev_token.type == "keyword") break out;
13064 if (current_length(tok) > max_line_length) {
13065 switch (tok.type) {
13078 custom.context = function() {
13079 return next_token.context.apply(this, arguments);
13083 return splits.map(function(pos, i){
13084 return code.substring(pos, splits[i + 1] || code.length);
13088 /* -----[ Utilities ]----- */
13090 function repeat_string(str, i) {
13091 if (i <= 0) return "";
13092 if (i == 1) return str;
13093 var d = repeat_string(str, i >> 1);
13095 if (i & 1) d += str;
13099 function defaults(args, defs) {
13103 for (var i in defs) if (HOP(defs, i)) {
13104 ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
13109 function is_identifier(name) {
13110 return /^[a-z_$][a-z0-9_$]*$/i.test(name)
13112 && !HOP(jsp.KEYWORDS_ATOM, name)
13113 && !HOP(jsp.RESERVED_WORDS, name)
13114 && !HOP(jsp.KEYWORDS, name);
13117 function HOP(obj, prop) {
13118 return Object.prototype.hasOwnProperty.call(obj, prop);
13126 MAP = function(a, f, o) {
13127 var ret = [], top = [], i;
13129 var val = f.call(o, a[i], i);
13130 if (val instanceof AtTop) {
13132 if (val instanceof Splice) {
13133 top.push.apply(top, val.v);
13138 else if (val != skip) {
13139 if (val instanceof Splice) {
13140 ret.push.apply(ret, val.v);
13146 if (a instanceof Array) for (i = 0; i < a.length; ++i) doit();
13147 else for (i in a) if (HOP(a, i)) doit();
13148 return top.concat(ret);
13150 MAP.at_top = function(val) { return new AtTop(val) };
13151 MAP.splice = function(val) { return new Splice(val) };
13152 var skip = MAP.skip = {};
13153 function AtTop(val) { this.v = val };
13154 function Splice(val) { this.v = val };
13157 /* -----[ Exports ]----- */
13159 exports.ast_walker = ast_walker;
13160 exports.ast_mangle = ast_mangle;
13161 exports.ast_squeeze = ast_squeeze;
13162 exports.ast_lift_variables = ast_lift_variables;
13163 exports.gen_code = gen_code;
13164 exports.ast_add_scope = ast_add_scope;
13165 exports.set_logger = function(logger) { warn = logger };
13166 exports.make_string = make_string;
13167 exports.split_lines = split_lines;
13171 exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more;
13173 // Local variables:
13174 // js-indent-level: 4
13177 define('uglifyjs/index', ["require", "exports", "module", "./parse-js", "./process", "./consolidator"], function(require, exports, module) {
13178 //convienence function(src, [options]);
13179 function uglify(orig_code, options){
13180 options || (options = {});
13181 var jsp = uglify.parser;
13182 var pro = uglify.uglify;
13184 var ast = jsp.parse(orig_code, options.strict_semicolons); // parse code and get the initial AST
13185 ast = pro.ast_mangle(ast, options.mangle_options); // get a new AST with mangled names
13186 ast = pro.ast_squeeze(ast, options.squeeze_options); // get an AST with compression optimizations
13187 var final_code = pro.gen_code(ast, options.gen_options); // compressed code here
13191 uglify.parser = require("./parse-js");
13192 uglify.uglify = require("./process");
13193 uglify.consolidator = require("./consolidator");
13195 module.exports = uglify
13196 });/* -*- Mode: js; js-indent-level: 2; -*- */
13198 * Copyright 2011 Mozilla Foundation and contributors
13199 * Licensed under the New BSD license. See LICENSE or:
13200 * http://opensource.org/licenses/BSD-3-Clause
13203 define('source-map/array-set', function (require, exports, module) {
13205 var util = require('./util');
13208 * A data structure which is a combination of an array and a set. Adding a new
13209 * member is O(1), testing for membership is O(1), and finding the index of an
13210 * element is O(1). Removing elements from the set is not supported. Only
13211 * strings are supported for membership.
13213 function ArraySet() {
13219 * Static method for creating ArraySet instances from an existing array.
13221 ArraySet.fromArray = function ArraySet_fromArray(aArray) {
13222 var set = new ArraySet();
13223 for (var i = 0, len = aArray.length; i < len; i++) {
13224 set.add(aArray[i]);
13230 * Add the given string to this set.
13232 * @param String aStr
13234 ArraySet.prototype.add = function ArraySet_add(aStr) {
13235 if (this.has(aStr)) {
13236 // Already a member; nothing to do.
13239 var idx = this._array.length;
13240 this._array.push(aStr);
13241 this._set[util.toSetString(aStr)] = idx;
13245 * Is the given string a member of this set?
13247 * @param String aStr
13249 ArraySet.prototype.has = function ArraySet_has(aStr) {
13250 return Object.prototype.hasOwnProperty.call(this._set,
13251 util.toSetString(aStr));
13255 * What is the index of the given string in the array?
13257 * @param String aStr
13259 ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
13260 if (this.has(aStr)) {
13261 return this._set[util.toSetString(aStr)];
13263 throw new Error('"' + aStr + '" is not in the set.');
13267 * What is the element at the given index?
13269 * @param Number aIdx
13271 ArraySet.prototype.at = function ArraySet_at(aIdx) {
13272 if (aIdx >= 0 && aIdx < this._array.length) {
13273 return this._array[aIdx];
13275 throw new Error('No element indexed by ' + aIdx);
13279 * Returns the array representation of this set (which has the proper indices
13280 * indicated by indexOf). Note that this is a copy of the internal array used
13281 * for storing the members so that no one can mess with internal state.
13283 ArraySet.prototype.toArray = function ArraySet_toArray() {
13284 return this._array.slice();
13287 exports.ArraySet = ArraySet;
13290 /* -*- Mode: js; js-indent-level: 2; -*- */
13292 * Copyright 2011 Mozilla Foundation and contributors
13293 * Licensed under the New BSD license. See LICENSE or:
13294 * http://opensource.org/licenses/BSD-3-Clause
13296 * Based on the Base 64 VLQ implementation in Closure Compiler:
13297 * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
13299 * Copyright 2011 The Closure Compiler Authors. All rights reserved.
13300 * Redistribution and use in source and binary forms, with or without
13301 * modification, are permitted provided that the following conditions are
13304 * * Redistributions of source code must retain the above copyright
13305 * notice, this list of conditions and the following disclaimer.
13306 * * Redistributions in binary form must reproduce the above
13307 * copyright notice, this list of conditions and the following
13308 * disclaimer in the documentation and/or other materials provided
13309 * with the distribution.
13310 * * Neither the name of Google Inc. nor the names of its
13311 * contributors may be used to endorse or promote products derived
13312 * from this software without specific prior written permission.
13314 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
13315 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
13316 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
13317 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
13318 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
13319 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
13320 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
13321 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13322 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13323 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13324 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13327 define('source-map/base64-vlq', function (require, exports, module) {
13329 var base64 = require('./base64');
13331 // A single base 64 digit can contain 6 bits of data. For the base 64 variable
13332 // length quantities we use in the source map spec, the first bit is the sign,
13333 // the next four bits are the actual value, and the 6th bit is the
13334 // continuation bit. The continuation bit tells us whether there are more
13335 // digits in this value following this digit.
13343 var VLQ_BASE_SHIFT = 5;
13346 var VLQ_BASE = 1 << VLQ_BASE_SHIFT;
13349 var VLQ_BASE_MASK = VLQ_BASE - 1;
13352 var VLQ_CONTINUATION_BIT = VLQ_BASE;
13355 * Converts from a two-complement value to a value where the sign bit is
13356 * is placed in the least significant bit. For example, as decimals:
13357 * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
13358 * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
13360 function toVLQSigned(aValue) {
13362 ? ((-aValue) << 1) + 1
13363 : (aValue << 1) + 0;
13367 * Converts to a two-complement value from a value where the sign bit is
13368 * is placed in the least significant bit. For example, as decimals:
13369 * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
13370 * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
13372 function fromVLQSigned(aValue) {
13373 var isNegative = (aValue & 1) === 1;
13374 var shifted = aValue >> 1;
13381 * Returns the base 64 VLQ encoded value.
13383 exports.encode = function base64VLQ_encode(aValue) {
13387 var vlq = toVLQSigned(aValue);
13390 digit = vlq & VLQ_BASE_MASK;
13391 vlq >>>= VLQ_BASE_SHIFT;
13393 // There are still more digits in this value, so we must make sure the
13394 // continuation bit is marked.
13395 digit |= VLQ_CONTINUATION_BIT;
13397 encoded += base64.encode(digit);
13404 * Decodes the next base 64 VLQ value from the given string and returns the
13405 * value and the rest of the string.
13407 exports.decode = function base64VLQ_decode(aStr) {
13409 var strLen = aStr.length;
13412 var continuation, digit;
13416 throw new Error("Expected more digits in base 64 VLQ value.");
13418 digit = base64.decode(aStr.charAt(i++));
13419 continuation = !!(digit & VLQ_CONTINUATION_BIT);
13420 digit &= VLQ_BASE_MASK;
13421 result = result + (digit << shift);
13422 shift += VLQ_BASE_SHIFT;
13423 } while (continuation);
13426 value: fromVLQSigned(result),
13427 rest: aStr.slice(i)
13432 /* -*- Mode: js; js-indent-level: 2; -*- */
13434 * Copyright 2011 Mozilla Foundation and contributors
13435 * Licensed under the New BSD license. See LICENSE or:
13436 * http://opensource.org/licenses/BSD-3-Clause
13439 define('source-map/base64', function (require, exports, module) {
13441 var charToIntMap = {};
13442 var intToCharMap = {};
13444 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
13446 .forEach(function (ch, index) {
13447 charToIntMap[ch] = index;
13448 intToCharMap[index] = ch;
13452 * Encode an integer in the range of 0 to 63 to a single base 64 digit.
13454 exports.encode = function base64_encode(aNumber) {
13455 if (aNumber in intToCharMap) {
13456 return intToCharMap[aNumber];
13458 throw new TypeError("Must be between 0 and 63: " + aNumber);
13462 * Decode a single base 64 digit to an integer.
13464 exports.decode = function base64_decode(aChar) {
13465 if (aChar in charToIntMap) {
13466 return charToIntMap[aChar];
13468 throw new TypeError("Not a valid base 64 digit: " + aChar);
13472 /* -*- Mode: js; js-indent-level: 2; -*- */
13474 * Copyright 2011 Mozilla Foundation and contributors
13475 * Licensed under the New BSD license. See LICENSE or:
13476 * http://opensource.org/licenses/BSD-3-Clause
13479 define('source-map/binary-search', function (require, exports, module) {
13482 * Recursive implementation of binary search.
13484 * @param aLow Indices here and lower do not contain the needle.
13485 * @param aHigh Indices here and higher do not contain the needle.
13486 * @param aNeedle The element being searched for.
13487 * @param aHaystack The non-empty array being searched.
13488 * @param aCompare Function which takes two elements and returns -1, 0, or 1.
13490 function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) {
13491 // This function terminates when one of the following is true:
13493 // 1. We find the exact element we are looking for.
13495 // 2. We did not find the exact element, but we can return the next
13496 // closest element that is less than that element.
13498 // 3. We did not find the exact element, and there is no next-closest
13499 // element which is less than the one we are searching for, so we
13501 var mid = Math.floor((aHigh - aLow) / 2) + aLow;
13502 var cmp = aCompare(aNeedle, aHaystack[mid]);
13504 // Found the element we are looking for.
13505 return aHaystack[mid];
13507 else if (cmp > 0) {
13508 // aHaystack[mid] is greater than our needle.
13509 if (aHigh - mid > 1) {
13510 // The element is in the upper half.
13511 return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare);
13513 // We did not find an exact match, return the next closest one
13514 // (termination case 2).
13515 return aHaystack[mid];
13518 // aHaystack[mid] is less than our needle.
13519 if (mid - aLow > 1) {
13520 // The element is in the lower half.
13521 return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare);
13523 // The exact needle element was not found in this haystack. Determine if
13524 // we are in termination case (2) or (3) and return the appropriate thing.
13532 * This is an implementation of binary search which will always try and return
13533 * the next lowest value checked if there is no exact hit. This is because
13534 * mappings between original and generated line/col pairs are single points,
13535 * and there is an implicit region between each of them, so a miss just means
13536 * that you aren't on the very start of a region.
13538 * @param aNeedle The element you are looking for.
13539 * @param aHaystack The array that is being searched.
13540 * @param aCompare A function which takes the needle and an element in the
13541 * array and returns -1, 0, or 1 depending on whether the needle is less
13542 * than, equal to, or greater than the element, respectively.
13544 exports.search = function search(aNeedle, aHaystack, aCompare) {
13545 return aHaystack.length > 0
13546 ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare)
13551 /* -*- Mode: js; js-indent-level: 2; -*- */
13553 * Copyright 2011 Mozilla Foundation and contributors
13554 * Licensed under the New BSD license. See LICENSE or:
13555 * http://opensource.org/licenses/BSD-3-Clause
13558 define('source-map/source-map-consumer', function (require, exports, module) {
13560 var util = require('./util');
13561 var binarySearch = require('./binary-search');
13562 var ArraySet = require('./array-set').ArraySet;
13563 var base64VLQ = require('./base64-vlq');
13566 * A SourceMapConsumer instance represents a parsed source map which we can
13567 * query for information about the original file positions by giving it a file
13568 * position in the generated source.
13570 * The only parameter is the raw source map (either as a JSON string, or
13571 * already parsed to an object). According to the spec, source maps have the
13572 * following attributes:
13574 * - version: Which version of the source map spec this map is following.
13575 * - sources: An array of URLs to the original source files.
13576 * - names: An array of identifiers which can be referrenced by individual mappings.
13577 * - sourceRoot: Optional. The URL root from which all sources are relative.
13578 * - sourcesContent: Optional. An array of contents of the original source files.
13579 * - mappings: A string of base64 VLQs which contain the actual mappings.
13580 * - file: The generated file this source map is associated with.
13582 * Here is an example source map, taken from the source map spec[0]:
13588 * sources: ["foo.js", "bar.js"],
13589 * names: ["src", "maps", "are", "fun"],
13590 * mappings: "AA,AB;;ABCDE;"
13593 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
13595 function SourceMapConsumer(aSourceMap) {
13596 var sourceMap = aSourceMap;
13597 if (typeof aSourceMap === 'string') {
13598 sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
13601 var version = util.getArg(sourceMap, 'version');
13602 var sources = util.getArg(sourceMap, 'sources');
13603 var names = util.getArg(sourceMap, 'names');
13604 var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
13605 var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
13606 var mappings = util.getArg(sourceMap, 'mappings');
13607 var file = util.getArg(sourceMap, 'file');
13609 if (version !== this._version) {
13610 throw new Error('Unsupported version: ' + version);
13613 this._names = ArraySet.fromArray(names);
13614 this._sources = ArraySet.fromArray(sources);
13615 this.sourceRoot = sourceRoot;
13616 this.sourcesContent = sourcesContent;
13619 // `this._generatedMappings` and `this._originalMappings` hold the parsed
13620 // mapping coordinates from the source map's "mappings" attribute. Each
13621 // object in the array is of the form
13624 // generatedLine: The line number in the generated code,
13625 // generatedColumn: The column number in the generated code,
13626 // source: The path to the original source file that generated this
13628 // originalLine: The line number in the original source that
13629 // corresponds to this chunk of generated code,
13630 // originalColumn: The column number in the original source that
13631 // corresponds to this chunk of generated code,
13632 // name: The name of the original symbol which generated this chunk of
13636 // All properties except for `generatedLine` and `generatedColumn` can be
13639 // `this._generatedMappings` is ordered by the generated positions.
13641 // `this._originalMappings` is ordered by the original positions.
13642 this._generatedMappings = [];
13643 this._originalMappings = [];
13644 this._parseMappings(mappings, sourceRoot);
13648 * The version of the source mapping spec that we are consuming.
13650 SourceMapConsumer.prototype._version = 3;
13653 * The list of original sources.
13655 Object.defineProperty(SourceMapConsumer.prototype, 'sources', {
13657 return this._sources.toArray().map(function (s) {
13658 return this.sourceRoot ? util.join(this.sourceRoot, s) : s;
13664 * Parse the mappings in a string in to a data structure which we can easily
13665 * query (an ordered list in this._generatedMappings).
13667 SourceMapConsumer.prototype._parseMappings =
13668 function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
13669 var generatedLine = 1;
13670 var previousGeneratedColumn = 0;
13671 var previousOriginalLine = 0;
13672 var previousOriginalColumn = 0;
13673 var previousSource = 0;
13674 var previousName = 0;
13675 var mappingSeparator = /^[,;]/;
13680 while (str.length > 0) {
13681 if (str.charAt(0) === ';') {
13683 str = str.slice(1);
13684 previousGeneratedColumn = 0;
13686 else if (str.charAt(0) === ',') {
13687 str = str.slice(1);
13691 mapping.generatedLine = generatedLine;
13693 // Generated column.
13694 temp = base64VLQ.decode(str);
13695 mapping.generatedColumn = previousGeneratedColumn + temp.value;
13696 previousGeneratedColumn = mapping.generatedColumn;
13699 if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
13700 // Original source.
13701 temp = base64VLQ.decode(str);
13702 mapping.source = this._sources.at(previousSource + temp.value);
13703 previousSource += temp.value;
13705 if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
13706 throw new Error('Found a source, but no line and column');
13710 temp = base64VLQ.decode(str);
13711 mapping.originalLine = previousOriginalLine + temp.value;
13712 previousOriginalLine = mapping.originalLine;
13713 // Lines are stored 0-based
13714 mapping.originalLine += 1;
13716 if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
13717 throw new Error('Found a source and line, but no column');
13720 // Original column.
13721 temp = base64VLQ.decode(str);
13722 mapping.originalColumn = previousOriginalColumn + temp.value;
13723 previousOriginalColumn = mapping.originalColumn;
13726 if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
13728 temp = base64VLQ.decode(str);
13729 mapping.name = this._names.at(previousName + temp.value);
13730 previousName += temp.value;
13735 this._generatedMappings.push(mapping);
13736 if (typeof mapping.originalLine === 'number') {
13737 this._originalMappings.push(mapping);
13742 this._originalMappings.sort(this._compareOriginalPositions);
13746 * Comparator between two mappings where the original positions are compared.
13748 SourceMapConsumer.prototype._compareOriginalPositions =
13749 function SourceMapConsumer_compareOriginalPositions(mappingA, mappingB) {
13750 if (mappingA.source > mappingB.source) {
13753 else if (mappingA.source < mappingB.source) {
13757 var cmp = mappingA.originalLine - mappingB.originalLine;
13759 ? mappingA.originalColumn - mappingB.originalColumn
13765 * Comparator between two mappings where the generated positions are compared.
13767 SourceMapConsumer.prototype._compareGeneratedPositions =
13768 function SourceMapConsumer_compareGeneratedPositions(mappingA, mappingB) {
13769 var cmp = mappingA.generatedLine - mappingB.generatedLine;
13771 ? mappingA.generatedColumn - mappingB.generatedColumn
13776 * Find the mapping that best matches the hypothetical "needle" mapping that
13777 * we are searching for in the given "haystack" of mappings.
13779 SourceMapConsumer.prototype._findMapping =
13780 function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
13781 aColumnName, aComparator) {
13782 // To return the position we are searching for, we must first find the
13783 // mapping for the given position and then return the opposite position it
13784 // points to. Because the mappings are sorted, we can use binary search to
13785 // find the best mapping.
13787 if (aNeedle[aLineName] <= 0) {
13788 throw new TypeError('Line must be greater than or equal to 1, got '
13789 + aNeedle[aLineName]);
13791 if (aNeedle[aColumnName] < 0) {
13792 throw new TypeError('Column must be greater than or equal to 0, got '
13793 + aNeedle[aColumnName]);
13796 return binarySearch.search(aNeedle, aMappings, aComparator);
13800 * Returns the original source, line, and column information for the generated
13801 * source's line and column positions provided. The only argument is an object
13802 * with the following properties:
13804 * - line: The line number in the generated source.
13805 * - column: The column number in the generated source.
13807 * and an object is returned with the following properties:
13809 * - source: The original source file, or null.
13810 * - line: The line number in the original source, or null.
13811 * - column: The column number in the original source, or null.
13812 * - name: The original identifier, or null.
13814 SourceMapConsumer.prototype.originalPositionFor =
13815 function SourceMapConsumer_originalPositionFor(aArgs) {
13817 generatedLine: util.getArg(aArgs, 'line'),
13818 generatedColumn: util.getArg(aArgs, 'column')
13821 var mapping = this._findMapping(needle,
13822 this._generatedMappings,
13825 this._compareGeneratedPositions);
13828 var source = util.getArg(mapping, 'source', null);
13829 if (source && this.sourceRoot) {
13830 source = util.join(this.sourceRoot, source);
13834 line: util.getArg(mapping, 'originalLine', null),
13835 column: util.getArg(mapping, 'originalColumn', null),
13836 name: util.getArg(mapping, 'name', null)
13849 * Returns the original source content. The only argument is the url of the
13850 * original source file. Returns null if no original source content is
13853 SourceMapConsumer.prototype.sourceContentFor =
13854 function SourceMapConsumer_sourceContentFor(aSource) {
13855 if (!this.sourcesContent) {
13859 if (this.sourceRoot) {
13860 aSource = util.relative(this.sourceRoot, aSource);
13863 if (this._sources.has(aSource)) {
13864 return this.sourcesContent[this._sources.indexOf(aSource)];
13868 if (this.sourceRoot
13869 && (url = util.urlParse(this.sourceRoot))) {
13870 // XXX: file:// URIs and absolute paths lead to unexpected behavior for
13871 // many users. We can help them out when they expect file:// URIs to
13872 // behave like it would if they were running a local HTTP server. See
13873 // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
13874 var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
13875 if (url.scheme == "file"
13876 && this._sources.has(fileUriAbsPath)) {
13877 return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
13880 if ((!url.path || url.path == "/")
13881 && this._sources.has("/" + aSource)) {
13882 return this.sourcesContent[this._sources.indexOf("/" + aSource)];
13886 throw new Error('"' + aSource + '" is not in the SourceMap.');
13890 * Returns the generated line and column information for the original source,
13891 * line, and column positions provided. The only argument is an object with
13892 * the following properties:
13894 * - source: The filename of the original source.
13895 * - line: The line number in the original source.
13896 * - column: The column number in the original source.
13898 * and an object is returned with the following properties:
13900 * - line: The line number in the generated source, or null.
13901 * - column: The column number in the generated source, or null.
13903 SourceMapConsumer.prototype.generatedPositionFor =
13904 function SourceMapConsumer_generatedPositionFor(aArgs) {
13906 source: util.getArg(aArgs, 'source'),
13907 originalLine: util.getArg(aArgs, 'line'),
13908 originalColumn: util.getArg(aArgs, 'column')
13911 if (this.sourceRoot) {
13912 needle.source = util.relative(this.sourceRoot, needle.source);
13915 var mapping = this._findMapping(needle,
13916 this._originalMappings,
13919 this._compareOriginalPositions);
13923 line: util.getArg(mapping, 'generatedLine', null),
13924 column: util.getArg(mapping, 'generatedColumn', null)
13934 SourceMapConsumer.GENERATED_ORDER = 1;
13935 SourceMapConsumer.ORIGINAL_ORDER = 2;
13938 * Iterate over each mapping between an original source/line/column and a
13939 * generated line/column in this source map.
13941 * @param Function aCallback
13942 * The function that is called with each mapping.
13943 * @param Object aContext
13944 * Optional. If specified, this object will be the value of `this` every
13945 * time that `aCallback` is called.
13947 * Either `SourceMapConsumer.GENERATED_ORDER` or
13948 * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
13949 * iterate over the mappings sorted by the generated file's line/column
13950 * order or the original's source/line/column order, respectively. Defaults to
13951 * `SourceMapConsumer.GENERATED_ORDER`.
13953 SourceMapConsumer.prototype.eachMapping =
13954 function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
13955 var context = aContext || null;
13956 var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
13960 case SourceMapConsumer.GENERATED_ORDER:
13961 mappings = this._generatedMappings;
13963 case SourceMapConsumer.ORIGINAL_ORDER:
13964 mappings = this._originalMappings;
13967 throw new Error("Unknown order of iteration.");
13970 var sourceRoot = this.sourceRoot;
13971 mappings.map(function (mapping) {
13972 var source = mapping.source;
13973 if (source && sourceRoot) {
13974 source = util.join(sourceRoot, source);
13978 generatedLine: mapping.generatedLine,
13979 generatedColumn: mapping.generatedColumn,
13980 originalLine: mapping.originalLine,
13981 originalColumn: mapping.originalColumn,
13984 }).forEach(aCallback, context);
13987 exports.SourceMapConsumer = SourceMapConsumer;
13990 /* -*- Mode: js; js-indent-level: 2; -*- */
13992 * Copyright 2011 Mozilla Foundation and contributors
13993 * Licensed under the New BSD license. See LICENSE or:
13994 * http://opensource.org/licenses/BSD-3-Clause
13997 define('source-map/source-map-generator', function (require, exports, module) {
13999 var base64VLQ = require('./base64-vlq');
14000 var util = require('./util');
14001 var ArraySet = require('./array-set').ArraySet;
14004 * An instance of the SourceMapGenerator represents a source map which is
14005 * being built incrementally. To create a new one, you must pass an object
14006 * with the following properties:
14008 * - file: The filename of the generated source.
14009 * - sourceRoot: An optional root for all URLs in this source map.
14011 function SourceMapGenerator(aArgs) {
14012 this._file = util.getArg(aArgs, 'file');
14013 this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
14014 this._sources = new ArraySet();
14015 this._names = new ArraySet();
14016 this._mappings = [];
14017 this._sourcesContents = null;
14020 SourceMapGenerator.prototype._version = 3;
14023 * Creates a new SourceMapGenerator based on a SourceMapConsumer
14025 * @param aSourceMapConsumer The SourceMap.
14027 SourceMapGenerator.fromSourceMap =
14028 function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
14029 var sourceRoot = aSourceMapConsumer.sourceRoot;
14030 var generator = new SourceMapGenerator({
14031 file: aSourceMapConsumer.file,
14032 sourceRoot: sourceRoot
14034 aSourceMapConsumer.eachMapping(function (mapping) {
14037 line: mapping.generatedLine,
14038 column: mapping.generatedColumn
14042 if (mapping.source) {
14043 newMapping.source = mapping.source;
14045 newMapping.source = util.relative(sourceRoot, newMapping.source);
14048 newMapping.original = {
14049 line: mapping.originalLine,
14050 column: mapping.originalColumn
14053 if (mapping.name) {
14054 newMapping.name = mapping.name;
14058 generator.addMapping(newMapping);
14060 aSourceMapConsumer.sources.forEach(function (sourceFile) {
14061 var content = aSourceMapConsumer.sourceContentFor(sourceFile);
14063 generator.setSourceContent(sourceFile, content);
14070 * Add a single mapping from original source line and column to the generated
14071 * source's line and column for this source map being created. The mapping
14072 * object should have the following properties:
14074 * - generated: An object with the generated line and column positions.
14075 * - original: An object with the original line and column positions.
14076 * - source: The original source file (relative to the sourceRoot).
14077 * - name: An optional original token name for this mapping.
14079 SourceMapGenerator.prototype.addMapping =
14080 function SourceMapGenerator_addMapping(aArgs) {
14081 var generated = util.getArg(aArgs, 'generated');
14082 var original = util.getArg(aArgs, 'original', null);
14083 var source = util.getArg(aArgs, 'source', null);
14084 var name = util.getArg(aArgs, 'name', null);
14086 this._validateMapping(generated, original, source, name);
14088 if (source && !this._sources.has(source)) {
14089 this._sources.add(source);
14092 if (name && !this._names.has(name)) {
14093 this._names.add(name);
14096 this._mappings.push({
14097 generated: generated,
14098 original: original,
14105 * Set the source content for a source file.
14107 SourceMapGenerator.prototype.setSourceContent =
14108 function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
14109 var source = aSourceFile;
14110 if (this._sourceRoot) {
14111 source = util.relative(this._sourceRoot, source);
14114 if (aSourceContent !== null) {
14115 // Add the source content to the _sourcesContents map.
14116 // Create a new _sourcesContents map if the property is null.
14117 if (!this._sourcesContents) {
14118 this._sourcesContents = {};
14120 this._sourcesContents[util.toSetString(source)] = aSourceContent;
14122 // Remove the source file from the _sourcesContents map.
14123 // If the _sourcesContents map is empty, set the property to null.
14124 delete this._sourcesContents[util.toSetString(source)];
14125 if (Object.keys(this._sourcesContents).length === 0) {
14126 this._sourcesContents = null;
14132 * Applies the mappings of a sub-source-map for a specific source file to the
14133 * source map being generated. Each mapping to the supplied source file is
14134 * rewritten using the supplied source map. Note: The resolution for the
14135 * resulting mappings is the minimium of this map and the supplied map.
14137 * @param aSourceMapConsumer The source map to be applied.
14138 * @param aSourceFile Optional. The filename of the source file.
14139 * If omitted, SourceMapConsumer's file property will be used.
14141 SourceMapGenerator.prototype.applySourceMap =
14142 function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) {
14143 // If aSourceFile is omitted, we will use the file property of the SourceMap
14144 if (!aSourceFile) {
14145 aSourceFile = aSourceMapConsumer.file;
14147 var sourceRoot = this._sourceRoot;
14148 // Make "aSourceFile" relative if an absolute Url is passed.
14150 aSourceFile = util.relative(sourceRoot, aSourceFile);
14152 // Applying the SourceMap can add and remove items from the sources and
14153 // the names array.
14154 var newSources = new ArraySet();
14155 var newNames = new ArraySet();
14157 // Find mappings for the "aSourceFile"
14158 this._mappings.forEach(function (mapping) {
14159 if (mapping.source === aSourceFile && mapping.original) {
14160 // Check if it can be mapped by the source map, then update the mapping.
14161 var original = aSourceMapConsumer.originalPositionFor({
14162 line: mapping.original.line,
14163 column: mapping.original.column
14165 if (original.source !== null) {
14168 mapping.source = util.relative(sourceRoot, original.source);
14170 mapping.source = original.source;
14172 mapping.original.line = original.line;
14173 mapping.original.column = original.column;
14174 if (original.name !== null && mapping.name !== null) {
14175 // Only use the identifier name if it's an identifier
14176 // in both SourceMaps
14177 mapping.name = original.name;
14182 var source = mapping.source;
14183 if (source && !newSources.has(source)) {
14184 newSources.add(source);
14187 var name = mapping.name;
14188 if (name && !newNames.has(name)) {
14189 newNames.add(name);
14193 this._sources = newSources;
14194 this._names = newNames;
14196 // Copy sourcesContents of applied map.
14197 aSourceMapConsumer.sources.forEach(function (sourceFile) {
14198 var content = aSourceMapConsumer.sourceContentFor(sourceFile);
14201 sourceFile = util.relative(sourceRoot, sourceFile);
14203 this.setSourceContent(sourceFile, content);
14209 * A mapping can have one of the three levels of data:
14211 * 1. Just the generated position.
14212 * 2. The Generated position, original position, and original source.
14213 * 3. Generated and original position, original source, as well as a name
14216 * To maintain consistency, we validate that any new mapping being added falls
14217 * in to one of these categories.
14219 SourceMapGenerator.prototype._validateMapping =
14220 function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
14222 if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
14223 && aGenerated.line > 0 && aGenerated.column >= 0
14224 && !aOriginal && !aSource && !aName) {
14228 else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
14229 && aOriginal && 'line' in aOriginal && 'column' in aOriginal
14230 && aGenerated.line > 0 && aGenerated.column >= 0
14231 && aOriginal.line > 0 && aOriginal.column >= 0
14237 throw new Error('Invalid mapping.');
14241 function cmpLocation(loc1, loc2) {
14242 var cmp = (loc1 && loc1.line) - (loc2 && loc2.line);
14243 return cmp ? cmp : (loc1 && loc1.column) - (loc2 && loc2.column);
14246 function strcmp(str1, str2) {
14249 return (str1 > str2) - (str1 < str2);
14252 function cmpMapping(mappingA, mappingB) {
14253 return cmpLocation(mappingA.generated, mappingB.generated) ||
14254 cmpLocation(mappingA.original, mappingB.original) ||
14255 strcmp(mappingA.source, mappingB.source) ||
14256 strcmp(mappingA.name, mappingB.name);
14260 * Serialize the accumulated mappings in to the stream of base 64 VLQs
14261 * specified by the source map format.
14263 SourceMapGenerator.prototype._serializeMappings =
14264 function SourceMapGenerator_serializeMappings() {
14265 var previousGeneratedColumn = 0;
14266 var previousGeneratedLine = 1;
14267 var previousOriginalColumn = 0;
14268 var previousOriginalLine = 0;
14269 var previousName = 0;
14270 var previousSource = 0;
14274 // The mappings must be guaranteed to be in sorted order before we start
14275 // serializing them or else the generated line numbers (which are defined
14276 // via the ';' separators) will be all messed up. Note: it might be more
14277 // performant to maintain the sorting as we insert them, rather than as we
14278 // serialize them, but the big O is the same either way.
14279 this._mappings.sort(cmpMapping);
14281 for (var i = 0, len = this._mappings.length; i < len; i++) {
14282 mapping = this._mappings[i];
14284 if (mapping.generated.line !== previousGeneratedLine) {
14285 previousGeneratedColumn = 0;
14286 while (mapping.generated.line !== previousGeneratedLine) {
14288 previousGeneratedLine++;
14293 if (!cmpMapping(mapping, this._mappings[i - 1])) {
14300 result += base64VLQ.encode(mapping.generated.column
14301 - previousGeneratedColumn);
14302 previousGeneratedColumn = mapping.generated.column;
14304 if (mapping.source && mapping.original) {
14305 result += base64VLQ.encode(this._sources.indexOf(mapping.source)
14307 previousSource = this._sources.indexOf(mapping.source);
14309 // lines are stored 0-based in SourceMap spec version 3
14310 result += base64VLQ.encode(mapping.original.line - 1
14311 - previousOriginalLine);
14312 previousOriginalLine = mapping.original.line - 1;
14314 result += base64VLQ.encode(mapping.original.column
14315 - previousOriginalColumn);
14316 previousOriginalColumn = mapping.original.column;
14318 if (mapping.name) {
14319 result += base64VLQ.encode(this._names.indexOf(mapping.name)
14321 previousName = this._names.indexOf(mapping.name);
14330 * Externalize the source map.
14332 SourceMapGenerator.prototype.toJSON =
14333 function SourceMapGenerator_toJSON() {
14335 version: this._version,
14337 sources: this._sources.toArray(),
14338 names: this._names.toArray(),
14339 mappings: this._serializeMappings()
14341 if (this._sourceRoot) {
14342 map.sourceRoot = this._sourceRoot;
14344 if (this._sourcesContents) {
14345 map.sourcesContent = map.sources.map(function (source) {
14346 if (map.sourceRoot) {
14347 source = util.relative(map.sourceRoot, source);
14349 return Object.prototype.hasOwnProperty.call(
14350 this._sourcesContents, util.toSetString(source))
14351 ? this._sourcesContents[util.toSetString(source)]
14359 * Render the source map being generated to a string.
14361 SourceMapGenerator.prototype.toString =
14362 function SourceMapGenerator_toString() {
14363 return JSON.stringify(this);
14366 exports.SourceMapGenerator = SourceMapGenerator;
14369 /* -*- Mode: js; js-indent-level: 2; -*- */
14371 * Copyright 2011 Mozilla Foundation and contributors
14372 * Licensed under the New BSD license. See LICENSE or:
14373 * http://opensource.org/licenses/BSD-3-Clause
14376 define('source-map/source-node', function (require, exports, module) {
14378 var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;
14379 var util = require('./util');
14382 * SourceNodes provide a way to abstract over interpolating/concatenating
14383 * snippets of generated JavaScript source code while maintaining the line and
14384 * column information associated with the original source code.
14386 * @param aLine The original line number.
14387 * @param aColumn The original column number.
14388 * @param aSource The original source's filename.
14389 * @param aChunks Optional. An array of strings which are snippets of
14390 * generated JS, or other SourceNodes.
14391 * @param aName The original identifier.
14393 function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
14394 this.children = [];
14395 this.sourceContents = {};
14396 this.line = aLine === undefined ? null : aLine;
14397 this.column = aColumn === undefined ? null : aColumn;
14398 this.source = aSource === undefined ? null : aSource;
14399 this.name = aName === undefined ? null : aName;
14400 if (aChunks != null) this.add(aChunks);
14404 * Creates a SourceNode from generated code and a SourceMapConsumer.
14406 * @param aGeneratedCode The generated code
14407 * @param aSourceMapConsumer The SourceMap for the generated code
14409 SourceNode.fromStringWithSourceMap =
14410 function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) {
14411 // The SourceNode we want to fill with the generated code
14412 // and the SourceMap
14413 var node = new SourceNode();
14415 // The generated code
14416 // Processed fragments are removed from this array.
14417 var remainingLines = aGeneratedCode.split('\n');
14419 // We need to remember the position of "remainingLines"
14420 var lastGeneratedLine = 1, lastGeneratedColumn = 0;
14422 // The generate SourceNodes we need a code range.
14423 // To extract it current and last mapping is used.
14424 // Here we store the last mapping.
14425 var lastMapping = null;
14427 aSourceMapConsumer.eachMapping(function (mapping) {
14428 if (lastMapping === null) {
14429 // We add the generated code until the first mapping
14430 // to the SourceNode without any mapping.
14431 // Each line is added as separate string.
14432 while (lastGeneratedLine < mapping.generatedLine) {
14433 node.add(remainingLines.shift() + "\n");
14434 lastGeneratedLine++;
14436 if (lastGeneratedColumn < mapping.generatedColumn) {
14437 var nextLine = remainingLines[0];
14438 node.add(nextLine.substr(0, mapping.generatedColumn));
14439 remainingLines[0] = nextLine.substr(mapping.generatedColumn);
14440 lastGeneratedColumn = mapping.generatedColumn;
14443 // We add the code from "lastMapping" to "mapping":
14444 // First check if there is a new line in between.
14445 if (lastGeneratedLine < mapping.generatedLine) {
14447 // Associate full lines with "lastMapping"
14449 code += remainingLines.shift() + "\n";
14450 lastGeneratedLine++;
14451 lastGeneratedColumn = 0;
14452 } while (lastGeneratedLine < mapping.generatedLine);
14453 // When we reached the correct line, we add code until we
14454 // reach the correct column too.
14455 if (lastGeneratedColumn < mapping.generatedColumn) {
14456 var nextLine = remainingLines[0];
14457 code += nextLine.substr(0, mapping.generatedColumn);
14458 remainingLines[0] = nextLine.substr(mapping.generatedColumn);
14459 lastGeneratedColumn = mapping.generatedColumn;
14461 // Create the SourceNode.
14462 addMappingWithCode(lastMapping, code);
14464 // There is no new line in between.
14465 // Associate the code between "lastGeneratedColumn" and
14466 // "mapping.generatedColumn" with "lastMapping"
14467 var nextLine = remainingLines[0];
14468 var code = nextLine.substr(0, mapping.generatedColumn -
14469 lastGeneratedColumn);
14470 remainingLines[0] = nextLine.substr(mapping.generatedColumn -
14471 lastGeneratedColumn);
14472 lastGeneratedColumn = mapping.generatedColumn;
14473 addMappingWithCode(lastMapping, code);
14476 lastMapping = mapping;
14478 // We have processed all mappings.
14479 // Associate the remaining code in the current line with "lastMapping"
14480 // and add the remaining lines without any mapping
14481 addMappingWithCode(lastMapping, remainingLines.join("\n"));
14483 // Copy sourcesContent into SourceNode
14484 aSourceMapConsumer.sources.forEach(function (sourceFile) {
14485 var content = aSourceMapConsumer.sourceContentFor(sourceFile);
14487 node.setSourceContent(sourceFile, content);
14493 function addMappingWithCode(mapping, code) {
14494 if (mapping.source === undefined) {
14497 node.add(new SourceNode(mapping.originalLine,
14498 mapping.originalColumn,
14507 * Add a chunk of generated JS to this source node.
14509 * @param aChunk A string snippet of generated JS code, another instance of
14510 * SourceNode, or an array where each member is one of those things.
14512 SourceNode.prototype.add = function SourceNode_add(aChunk) {
14513 if (Array.isArray(aChunk)) {
14514 aChunk.forEach(function (chunk) {
14518 else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
14520 this.children.push(aChunk);
14524 throw new TypeError(
14525 "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
14532 * Add a chunk of generated JS to the beginning of this source node.
14534 * @param aChunk A string snippet of generated JS code, another instance of
14535 * SourceNode, or an array where each member is one of those things.
14537 SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
14538 if (Array.isArray(aChunk)) {
14539 for (var i = aChunk.length-1; i >= 0; i--) {
14540 this.prepend(aChunk[i]);
14543 else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
14544 this.children.unshift(aChunk);
14547 throw new TypeError(
14548 "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
14555 * Walk over the tree of JS snippets in this node and its children. The
14556 * walking function is called once for each snippet of JS and is passed that
14557 * snippet and the its original associated source's line/column location.
14559 * @param aFn The traversal function.
14561 SourceNode.prototype.walk = function SourceNode_walk(aFn) {
14562 this.children.forEach(function (chunk) {
14563 if (chunk instanceof SourceNode) {
14567 if (chunk !== '') {
14568 aFn(chunk, { source: this.source,
14570 column: this.column,
14571 name: this.name });
14578 * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
14579 * each of `this.children`.
14581 * @param aSep The separator.
14583 SourceNode.prototype.join = function SourceNode_join(aSep) {
14586 var len = this.children.length;
14589 for (i = 0; i < len-1; i++) {
14590 newChildren.push(this.children[i]);
14591 newChildren.push(aSep);
14593 newChildren.push(this.children[i]);
14594 this.children = newChildren;
14600 * Call String.prototype.replace on the very right-most source snippet. Useful
14601 * for trimming whitespace from the end of a source node, etc.
14603 * @param aPattern The pattern to replace.
14604 * @param aReplacement The thing to replace the pattern with.
14606 SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
14607 var lastChild = this.children[this.children.length - 1];
14608 if (lastChild instanceof SourceNode) {
14609 lastChild.replaceRight(aPattern, aReplacement);
14611 else if (typeof lastChild === 'string') {
14612 this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
14615 this.children.push(''.replace(aPattern, aReplacement));
14621 * Set the source content for a source file. This will be added to the SourceMapGenerator
14622 * in the sourcesContent field.
14624 * @param aSourceFile The filename of the source file
14625 * @param aSourceContent The content of the source file
14627 SourceNode.prototype.setSourceContent =
14628 function SourceNode_setSourceContent(aSourceFile, aSourceContent) {
14629 this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
14633 * Walk over the tree of SourceNodes. The walking function is called for each
14634 * source file content and is passed the filename and source content.
14636 * @param aFn The traversal function.
14638 SourceNode.prototype.walkSourceContents =
14639 function SourceNode_walkSourceContents(aFn) {
14640 this.children.forEach(function (chunk) {
14641 if (chunk instanceof SourceNode) {
14642 chunk.walkSourceContents(aFn);
14645 Object.keys(this.sourceContents).forEach(function (sourceFileKey) {
14646 aFn(util.fromSetString(sourceFileKey), this.sourceContents[sourceFileKey]);
14651 * Return the string representation of this source node. Walks over the tree
14652 * and concatenates all the various snippets together to one string.
14654 SourceNode.prototype.toString = function SourceNode_toString() {
14656 this.walk(function (chunk) {
14663 * Returns the string representation of this source node along with a source
14666 SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
14672 var map = new SourceMapGenerator(aArgs);
14673 var sourceMappingActive = false;
14674 this.walk(function (chunk, original) {
14675 generated.code += chunk;
14676 if (original.source !== null
14677 && original.line !== null
14678 && original.column !== null) {
14680 source: original.source,
14682 line: original.line,
14683 column: original.column
14686 line: generated.line,
14687 column: generated.column
14689 name: original.name
14691 sourceMappingActive = true;
14692 } else if (sourceMappingActive) {
14695 line: generated.line,
14696 column: generated.column
14699 sourceMappingActive = false;
14701 chunk.split('').forEach(function (ch) {
14704 generated.column = 0;
14706 generated.column++;
14710 this.walkSourceContents(function (sourceFile, sourceContent) {
14711 map.setSourceContent(sourceFile, sourceContent);
14714 return { code: generated.code, map: map };
14717 exports.SourceNode = SourceNode;
14720 /* -*- Mode: js; js-indent-level: 2; -*- */
14722 * Copyright 2011 Mozilla Foundation and contributors
14723 * Licensed under the New BSD license. See LICENSE or:
14724 * http://opensource.org/licenses/BSD-3-Clause
14727 define('source-map/util', function (require, exports, module) {
14730 * This is a helper function for getting values from parameter/options
14733 * @param args The object we are extracting values from
14734 * @param name The name of the property we are getting.
14735 * @param defaultValue An optional value to return if the property is missing
14736 * from the object. If this is not specified and the property is missing, an
14737 * error will be thrown.
14739 function getArg(aArgs, aName, aDefaultValue) {
14740 if (aName in aArgs) {
14741 return aArgs[aName];
14742 } else if (arguments.length === 3) {
14743 return aDefaultValue;
14745 throw new Error('"' + aName + '" is a required argument.');
14748 exports.getArg = getArg;
14750 var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/;
14752 function urlParse(aUrl) {
14753 var match = aUrl.match(urlRegexp);
14765 exports.urlParse = urlParse;
14767 function urlGenerate(aParsedUrl) {
14768 var url = aParsedUrl.scheme + "://";
14769 if (aParsedUrl.auth) {
14770 url += aParsedUrl.auth + "@"
14772 if (aParsedUrl.host) {
14773 url += aParsedUrl.host;
14775 if (aParsedUrl.port) {
14776 url += ":" + aParsedUrl.port
14778 if (aParsedUrl.path) {
14779 url += aParsedUrl.path;
14783 exports.urlGenerate = urlGenerate;
14785 function join(aRoot, aPath) {
14788 if (aPath.match(urlRegexp)) {
14792 if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) {
14794 return urlGenerate(url);
14797 return aRoot.replace(/\/$/, '') + '/' + aPath;
14799 exports.join = join;
14802 * Because behavior goes wacky when you set `__proto__` on objects, we
14803 * have to prefix all the strings in our set with an arbitrary character.
14805 * See https://github.com/mozilla/source-map/pull/31 and
14806 * https://github.com/mozilla/source-map/issues/30
14808 * @param String aStr
14810 function toSetString(aStr) {
14813 exports.toSetString = toSetString;
14815 function fromSetString(aStr) {
14816 return aStr.substr(1);
14818 exports.fromSetString = fromSetString;
14820 function relative(aRoot, aPath) {
14821 aRoot = aRoot.replace(/\/$/, '');
14823 var url = urlParse(aRoot);
14824 if (aPath.charAt(0) == "/" && url && url.path == "/") {
14825 return aPath.slice(1);
14828 return aPath.indexOf(aRoot + '/') === 0
14829 ? aPath.substr(aRoot.length + 1)
14832 exports.relative = relative;
14835 define('source-map', function (require, exports, module) {
14838 * Copyright 2009-2011 Mozilla Foundation and contributors
14839 * Licensed under the New BSD license. See LICENSE.txt or:
14840 * http://opensource.org/licenses/BSD-3-Clause
14842 exports.SourceMapGenerator = require('./source-map/source-map-generator').SourceMapGenerator;
14843 exports.SourceMapConsumer = require('./source-map/source-map-consumer').SourceMapConsumer;
14844 exports.SourceNode = require('./source-map/source-node').SourceNode;
14848 //Distributed under the BSD license:
14849 //Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
14850 define('uglifyjs2', ['exports', 'source-map', 'logger', 'env!env/file'], function (exports, MOZ_SourceMap, logger, rjsFile) {
14851 (function(exports, global) {
14852 global["UglifyJS"] = exports;
14854 function array_to_hash(a) {
14855 var ret = Object.create(null);
14856 for (var i = 0; i < a.length; ++i) ret[a[i]] = true;
14859 function slice(a, start) {
14860 return Array.prototype.slice.call(a, start || 0);
14862 function characters(str) {
14863 return str.split("");
14865 function member(name, array) {
14866 for (var i = array.length; --i >= 0; ) if (array[i] == name) return true;
14869 function find_if(func, array) {
14870 for (var i = 0, n = array.length; i < n; ++i) {
14871 if (func(array[i])) return array[i];
14874 function repeat_string(str, i) {
14875 if (i <= 0) return "";
14876 if (i == 1) return str;
14877 var d = repeat_string(str, i >> 1);
14879 if (i & 1) d += str;
14882 function DefaultsError(msg, defs) {
14886 function defaults(args, defs, croak) {
14887 if (args === true) args = {};
14888 var ret = args || {};
14889 if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i)) throw new DefaultsError("`" + i + "` is not a supported option", defs);
14890 for (var i in defs) if (defs.hasOwnProperty(i)) {
14891 ret[i] = args && args.hasOwnProperty(i) ? args[i] : defs[i];
14895 function merge(obj, ext) {
14896 for (var i in ext) if (ext.hasOwnProperty(i)) {
14902 var MAP = function() {
14903 function MAP(a, f, backwards) {
14904 var ret = [], top = [], i;
14906 var val = f(a[i], i);
14907 var is_last = val instanceof Last;
14908 if (is_last) val = val.v;
14909 if (val instanceof AtTop) {
14911 if (val instanceof Splice) {
14912 top.push.apply(top, backwards ? val.v.slice().reverse() : val.v);
14916 } else if (val !== skip) {
14917 if (val instanceof Splice) {
14918 ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
14925 if (a instanceof Array) {
14927 for (i = a.length; --i >= 0; ) if (doit()) break;
14931 for (i = 0; i < a.length; ++i) if (doit()) break;
14934 for (i in a) if (a.hasOwnProperty(i)) if (doit()) break;
14936 return top.concat(ret);
14938 MAP.at_top = function(val) {
14939 return new AtTop(val);
14941 MAP.splice = function(val) {
14942 return new Splice(val);
14944 MAP.last = function(val) {
14945 return new Last(val);
14947 var skip = MAP.skip = {};
14948 function AtTop(val) {
14951 function Splice(val) {
14954 function Last(val) {
14959 function push_uniq(array, el) {
14960 if (array.indexOf(el) < 0) array.push(el);
14962 function string_template(text, props) {
14963 return text.replace(/\{(.+?)\}/g, function(str, p) {
14967 function remove(array, el) {
14968 for (var i = array.length; --i >= 0; ) {
14969 if (array[i] === el) array.splice(i, 1);
14972 function mergeSort(array, cmp) {
14973 if (array.length < 2) return array.slice();
14974 function merge(a, b) {
14975 var r = [], ai = 0, bi = 0, i = 0;
14976 while (ai < a.length && bi < b.length) {
14977 cmp(a[ai], b[bi]) <= 0 ? r[i++] = a[ai++] : r[i++] = b[bi++];
14979 if (ai < a.length) r.push.apply(r, a.slice(ai));
14980 if (bi < b.length) r.push.apply(r, b.slice(bi));
14984 if (a.length <= 1) return a;
14985 var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m);
14987 right = _ms(right);
14988 return merge(left, right);
14992 function set_difference(a, b) {
14993 return a.filter(function(el) {
14994 return b.indexOf(el) < 0;
14997 function set_intersection(a, b) {
14998 return a.filter(function(el) {
14999 return b.indexOf(el) >= 0;
15002 function makePredicate(words) {
15003 if (!(words instanceof Array)) words = words.split(" ");
15004 var f = "", cats = [];
15005 out: for (var i = 0; i < words.length; ++i) {
15006 for (var j = 0; j < cats.length; ++j) if (cats[j][0].length == words[i].length) {
15007 cats[j].push(words[i]);
15010 cats.push([ words[i] ]);
15012 function compareTo(arr) {
15013 if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
15014 f += "switch(str){";
15015 for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":";
15016 f += "return true}return false;";
15018 if (cats.length > 3) {
15019 cats.sort(function(a, b) {
15020 return b.length - a.length;
15022 f += "switch(str.length){";
15023 for (var i = 0; i < cats.length; ++i) {
15025 f += "case " + cat[0].length + ":";
15032 return new Function("str", f);
15034 function all(array, predicate) {
15035 for (var i = array.length; --i >= 0; ) if (!predicate(array[i])) return false;
15038 function Dictionary() {
15039 this._values = Object.create(null);
15042 Dictionary.prototype = {
15043 set: function(key, val) {
15044 if (!this.has(key)) ++this._size;
15045 this._values["$" + key] = val;
15048 add: function(key, val) {
15049 if (this.has(key)) {
15050 this.get(key).push(val);
15052 this.set(key, [ val ]);
15056 get: function(key) {
15057 return this._values["$" + key];
15059 del: function(key) {
15060 if (this.has(key)) {
15062 delete this._values["$" + key];
15066 has: function(key) {
15067 return "$" + key in this._values;
15069 each: function(f) {
15070 for (var i in this._values) f(this._values[i], i.substr(1));
15077 for (var i in this._values) ret.push(f(this._values[i], i.substr(1)));
15082 function DEFNODE(type, props, methods, base) {
15083 if (arguments.length < 4) base = AST_Node;
15084 if (!props) props = []; else props = props.split(/\s+/);
15085 var self_props = props;
15086 if (base && base.PROPS) props = props.concat(base.PROPS);
15087 var code = "return function AST_" + type + "(props){ if (props) { ";
15088 for (var i = props.length; --i >= 0; ) {
15089 code += "this." + props[i] + " = props." + props[i] + ";";
15091 var proto = base && new base();
15092 if (proto && proto.initialize || methods && methods.initialize) code += "this.initialize();";
15094 var ctor = new Function(code)();
15096 ctor.prototype = proto;
15099 if (base) base.SUBCLASSES.push(ctor);
15100 ctor.prototype.CTOR = ctor;
15101 ctor.PROPS = props || null;
15102 ctor.SELF_PROPS = self_props;
15103 ctor.SUBCLASSES = [];
15105 ctor.prototype.TYPE = ctor.TYPE = type;
15107 if (methods) for (i in methods) if (methods.hasOwnProperty(i)) {
15108 if (/^\$/.test(i)) {
15109 ctor[i.substr(1)] = methods[i];
15111 ctor.prototype[i] = methods[i];
15114 ctor.DEFMETHOD = function(name, method) {
15115 this.prototype[name] = method;
15119 var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", {}, null);
15120 var AST_Node = DEFNODE("Node", "start end", {
15121 clone: function() {
15122 return new this.CTOR(this);
15124 $documentation: "Base class of all AST nodes",
15126 start: "[AST_Token] The first token of this node",
15127 end: "[AST_Token] The last token of this node"
15129 _walk: function(visitor) {
15130 return visitor._visit(this);
15132 walk: function(visitor) {
15133 return this._walk(visitor);
15136 AST_Node.warn_function = null;
15137 AST_Node.warn = function(txt, props) {
15138 if (AST_Node.warn_function) AST_Node.warn_function(string_template(txt, props));
15140 var AST_Statement = DEFNODE("Statement", null, {
15141 $documentation: "Base class of all statements"
15143 var AST_Debugger = DEFNODE("Debugger", null, {
15144 $documentation: "Represents a debugger statement"
15146 var AST_Directive = DEFNODE("Directive", "value scope", {
15147 $documentation: 'Represents a directive, like "use strict";',
15149 value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
15150 scope: "[AST_Scope/S] The scope that this directive affects"
15153 var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
15154 $documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
15156 body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
15158 _walk: function(visitor) {
15159 return visitor._visit(this, function() {
15160 this.body._walk(visitor);
15164 function walk_body(node, visitor) {
15165 if (node.body instanceof AST_Statement) {
15166 node.body._walk(visitor);
15167 } else node.body.forEach(function(stat) {
15168 stat._walk(visitor);
15171 var AST_Block = DEFNODE("Block", "body", {
15172 $documentation: "A body of statements (usually bracketed)",
15174 body: "[AST_Statement*] an array of statements"
15176 _walk: function(visitor) {
15177 return visitor._visit(this, function() {
15178 walk_body(this, visitor);
15182 var AST_BlockStatement = DEFNODE("BlockStatement", null, {
15183 $documentation: "A block statement"
15185 var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
15186 $documentation: "The empty statement (empty block or simply a semicolon)",
15187 _walk: function(visitor) {
15188 return visitor._visit(this);
15191 var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
15192 $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
15194 body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
15196 _walk: function(visitor) {
15197 return visitor._visit(this, function() {
15198 this.body._walk(visitor);
15202 var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
15203 $documentation: "Statement with a label",
15205 label: "[AST_Label] a label definition"
15207 _walk: function(visitor) {
15208 return visitor._visit(this, function() {
15209 this.label._walk(visitor);
15210 this.body._walk(visitor);
15213 }, AST_StatementWithBody);
15214 var AST_DWLoop = DEFNODE("DWLoop", "condition", {
15215 $documentation: "Base class for do/while statements",
15217 condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
15219 _walk: function(visitor) {
15220 return visitor._visit(this, function() {
15221 this.condition._walk(visitor);
15222 this.body._walk(visitor);
15225 }, AST_StatementWithBody);
15226 var AST_Do = DEFNODE("Do", null, {
15227 $documentation: "A `do` statement"
15229 var AST_While = DEFNODE("While", null, {
15230 $documentation: "A `while` statement"
15232 var AST_For = DEFNODE("For", "init condition step", {
15233 $documentation: "A `for` statement",
15235 init: "[AST_Node?] the `for` initialization code, or null if empty",
15236 condition: "[AST_Node?] the `for` termination clause, or null if empty",
15237 step: "[AST_Node?] the `for` update clause, or null if empty"
15239 _walk: function(visitor) {
15240 return visitor._visit(this, function() {
15241 if (this.init) this.init._walk(visitor);
15242 if (this.condition) this.condition._walk(visitor);
15243 if (this.step) this.step._walk(visitor);
15244 this.body._walk(visitor);
15247 }, AST_StatementWithBody);
15248 var AST_ForIn = DEFNODE("ForIn", "init name object", {
15249 $documentation: "A `for ... in` statement",
15251 init: "[AST_Node] the `for/in` initialization code",
15252 name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
15253 object: "[AST_Node] the object that we're looping through"
15255 _walk: function(visitor) {
15256 return visitor._visit(this, function() {
15257 this.init._walk(visitor);
15258 this.object._walk(visitor);
15259 this.body._walk(visitor);
15262 }, AST_StatementWithBody);
15263 var AST_With = DEFNODE("With", "expression", {
15264 $documentation: "A `with` statement",
15266 expression: "[AST_Node] the `with` expression"
15268 _walk: function(visitor) {
15269 return visitor._visit(this, function() {
15270 this.expression._walk(visitor);
15271 this.body._walk(visitor);
15274 }, AST_StatementWithBody);
15275 var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
15276 $documentation: "Base class for all statements introducing a lexical scope",
15278 directives: "[string*/S] an array of directives declared in this scope",
15279 variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
15280 functions: "[Object/S] like `variables`, but only lists function declarations",
15281 uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
15282 uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
15283 parent_scope: "[AST_Scope?/S] link to the parent scope",
15284 enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
15285 cname: "[integer/S] current index for mangling variables (used internally by the mangler)"
15288 var AST_Toplevel = DEFNODE("Toplevel", "globals", {
15289 $documentation: "The toplevel scope",
15291 globals: "[Object/S] a map of name -> SymbolDef for all undeclared names"
15293 wrap_enclose: function(arg_parameter_pairs) {
15296 var parameters = [];
15297 arg_parameter_pairs.forEach(function(pair) {
15298 var split = pair.split(":");
15299 args.push(split[0]);
15300 parameters.push(split[1]);
15302 var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
15303 wrapped_tl = parse(wrapped_tl);
15304 wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node) {
15305 if (node instanceof AST_Directive && node.value == "$ORIG") {
15306 return MAP.splice(self.body);
15311 wrap_commonjs: function(name, export_all) {
15313 var to_export = [];
15315 self.figure_out_scope();
15316 self.walk(new TreeWalker(function(node) {
15317 if (node instanceof AST_SymbolDeclaration && node.definition().global) {
15318 if (!find_if(function(n) {
15319 return n.name == node.name;
15320 }, to_export)) to_export.push(node);
15324 var wrapped_tl = "(function(exports, global){ global['" + name + "'] = exports; '$ORIG'; '$EXPORTS'; }({}, (function(){return this}())))";
15325 wrapped_tl = parse(wrapped_tl);
15326 wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node) {
15327 if (node instanceof AST_SimpleStatement) {
15329 if (node instanceof AST_String) switch (node.getValue()) {
15331 return MAP.splice(self.body);
15335 to_export.forEach(function(sym) {
15336 body.push(new AST_SimpleStatement({
15337 body: new AST_Assign({
15338 left: new AST_Sub({
15339 expression: new AST_SymbolRef({
15342 property: new AST_String({
15347 right: new AST_SymbolRef(sym)
15351 return MAP.splice(body);
15358 var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
15359 $documentation: "Base class for functions",
15361 name: "[AST_SymbolDeclaration?] the name of this function",
15362 argnames: "[AST_SymbolFunarg*] array of function arguments",
15363 uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
15365 _walk: function(visitor) {
15366 return visitor._visit(this, function() {
15367 if (this.name) this.name._walk(visitor);
15368 this.argnames.forEach(function(arg) {
15369 arg._walk(visitor);
15371 walk_body(this, visitor);
15375 var AST_Accessor = DEFNODE("Accessor", null, {
15376 $documentation: "A setter/getter function"
15378 var AST_Function = DEFNODE("Function", null, {
15379 $documentation: "A function expression"
15381 var AST_Defun = DEFNODE("Defun", null, {
15382 $documentation: "A function definition"
15384 var AST_Jump = DEFNODE("Jump", null, {
15385 $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
15387 var AST_Exit = DEFNODE("Exit", "value", {
15388 $documentation: "Base class for “exits” (`return` and `throw`)",
15390 value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
15392 _walk: function(visitor) {
15393 return visitor._visit(this, this.value && function() {
15394 this.value._walk(visitor);
15398 var AST_Return = DEFNODE("Return", null, {
15399 $documentation: "A `return` statement"
15401 var AST_Throw = DEFNODE("Throw", null, {
15402 $documentation: "A `throw` statement"
15404 var AST_LoopControl = DEFNODE("LoopControl", "label", {
15405 $documentation: "Base class for loop control statements (`break` and `continue`)",
15407 label: "[AST_LabelRef?] the label, or null if none"
15409 _walk: function(visitor) {
15410 return visitor._visit(this, this.label && function() {
15411 this.label._walk(visitor);
15415 var AST_Break = DEFNODE("Break", null, {
15416 $documentation: "A `break` statement"
15417 }, AST_LoopControl);
15418 var AST_Continue = DEFNODE("Continue", null, {
15419 $documentation: "A `continue` statement"
15420 }, AST_LoopControl);
15421 var AST_If = DEFNODE("If", "condition alternative", {
15422 $documentation: "A `if` statement",
15424 condition: "[AST_Node] the `if` condition",
15425 alternative: "[AST_Statement?] the `else` part, or null if not present"
15427 _walk: function(visitor) {
15428 return visitor._visit(this, function() {
15429 this.condition._walk(visitor);
15430 this.body._walk(visitor);
15431 if (this.alternative) this.alternative._walk(visitor);
15434 }, AST_StatementWithBody);
15435 var AST_Switch = DEFNODE("Switch", "expression", {
15436 $documentation: "A `switch` statement",
15438 expression: "[AST_Node] the `switch` “discriminant”"
15440 _walk: function(visitor) {
15441 return visitor._visit(this, function() {
15442 this.expression._walk(visitor);
15443 walk_body(this, visitor);
15447 var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
15448 $documentation: "Base class for `switch` branches"
15450 var AST_Default = DEFNODE("Default", null, {
15451 $documentation: "A `default` switch branch"
15452 }, AST_SwitchBranch);
15453 var AST_Case = DEFNODE("Case", "expression", {
15454 $documentation: "A `case` switch branch",
15456 expression: "[AST_Node] the `case` expression"
15458 _walk: function(visitor) {
15459 return visitor._visit(this, function() {
15460 this.expression._walk(visitor);
15461 walk_body(this, visitor);
15464 }, AST_SwitchBranch);
15465 var AST_Try = DEFNODE("Try", "bcatch bfinally", {
15466 $documentation: "A `try` statement",
15468 bcatch: "[AST_Catch?] the catch block, or null if not present",
15469 bfinally: "[AST_Finally?] the finally block, or null if not present"
15471 _walk: function(visitor) {
15472 return visitor._visit(this, function() {
15473 walk_body(this, visitor);
15474 if (this.bcatch) this.bcatch._walk(visitor);
15475 if (this.bfinally) this.bfinally._walk(visitor);
15479 var AST_Catch = DEFNODE("Catch", "argname", {
15480 $documentation: "A `catch` node; only makes sense as part of a `try` statement",
15482 argname: "[AST_SymbolCatch] symbol for the exception"
15484 _walk: function(visitor) {
15485 return visitor._visit(this, function() {
15486 this.argname._walk(visitor);
15487 walk_body(this, visitor);
15491 var AST_Finally = DEFNODE("Finally", null, {
15492 $documentation: "A `finally` node; only makes sense as part of a `try` statement"
15494 var AST_Definitions = DEFNODE("Definitions", "definitions", {
15495 $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
15497 definitions: "[AST_VarDef*] array of variable definitions"
15499 _walk: function(visitor) {
15500 return visitor._visit(this, function() {
15501 this.definitions.forEach(function(def) {
15502 def._walk(visitor);
15507 var AST_Var = DEFNODE("Var", null, {
15508 $documentation: "A `var` statement"
15509 }, AST_Definitions);
15510 var AST_Const = DEFNODE("Const", null, {
15511 $documentation: "A `const` statement"
15512 }, AST_Definitions);
15513 var AST_VarDef = DEFNODE("VarDef", "name value", {
15514 $documentation: "A variable declaration; only appears in a AST_Definitions node",
15516 name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
15517 value: "[AST_Node?] initializer, or null of there's no initializer"
15519 _walk: function(visitor) {
15520 return visitor._visit(this, function() {
15521 this.name._walk(visitor);
15522 if (this.value) this.value._walk(visitor);
15526 var AST_Call = DEFNODE("Call", "expression args", {
15527 $documentation: "A function call expression",
15529 expression: "[AST_Node] expression to invoke as function",
15530 args: "[AST_Node*] array of arguments"
15532 _walk: function(visitor) {
15533 return visitor._visit(this, function() {
15534 this.expression._walk(visitor);
15535 this.args.forEach(function(arg) {
15536 arg._walk(visitor);
15541 var AST_New = DEFNODE("New", null, {
15542 $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties"
15544 var AST_Seq = DEFNODE("Seq", "car cdr", {
15545 $documentation: "A sequence expression (two comma-separated expressions)",
15547 car: "[AST_Node] first element in sequence",
15548 cdr: "[AST_Node] second element in sequence"
15550 $cons: function(x, y) {
15551 var seq = new AST_Seq(x);
15556 $from_array: function(array) {
15557 if (array.length == 0) return null;
15558 if (array.length == 1) return array[0].clone();
15560 for (var i = array.length; --i >= 0; ) {
15561 list = AST_Seq.cons(array[i], list);
15565 if (p.cdr && !p.cdr.cdr) {
15573 to_array: function() {
15574 var p = this, a = [];
15577 if (p.cdr && !(p.cdr instanceof AST_Seq)) {
15585 add: function(node) {
15588 if (!(p.cdr instanceof AST_Seq)) {
15589 var cell = AST_Seq.cons(p.cdr, node);
15590 return p.cdr = cell;
15595 _walk: function(visitor) {
15596 return visitor._visit(this, function() {
15597 this.car._walk(visitor);
15598 if (this.cdr) this.cdr._walk(visitor);
15602 var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
15603 $documentation: 'Base class for property access expressions, i.e. `a.foo` or `a["foo"]`',
15605 expression: "[AST_Node] the “container” expression",
15606 property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node"
15609 var AST_Dot = DEFNODE("Dot", null, {
15610 $documentation: "A dotted property access expression",
15611 _walk: function(visitor) {
15612 return visitor._visit(this, function() {
15613 this.expression._walk(visitor);
15616 }, AST_PropAccess);
15617 var AST_Sub = DEFNODE("Sub", null, {
15618 $documentation: 'Index-style property access, i.e. `a["foo"]`',
15619 _walk: function(visitor) {
15620 return visitor._visit(this, function() {
15621 this.expression._walk(visitor);
15622 this.property._walk(visitor);
15625 }, AST_PropAccess);
15626 var AST_Unary = DEFNODE("Unary", "operator expression", {
15627 $documentation: "Base class for unary expressions",
15629 operator: "[string] the operator",
15630 expression: "[AST_Node] expression that this unary operator applies to"
15632 _walk: function(visitor) {
15633 return visitor._visit(this, function() {
15634 this.expression._walk(visitor);
15638 var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, {
15639 $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`"
15641 var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
15642 $documentation: "Unary postfix expression, i.e. `i++`"
15644 var AST_Binary = DEFNODE("Binary", "left operator right", {
15645 $documentation: "Binary expression, i.e. `a + b`",
15647 left: "[AST_Node] left-hand side expression",
15648 operator: "[string] the operator",
15649 right: "[AST_Node] right-hand side expression"
15651 _walk: function(visitor) {
15652 return visitor._visit(this, function() {
15653 this.left._walk(visitor);
15654 this.right._walk(visitor);
15658 var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
15659 $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
15661 condition: "[AST_Node]",
15662 consequent: "[AST_Node]",
15663 alternative: "[AST_Node]"
15665 _walk: function(visitor) {
15666 return visitor._visit(this, function() {
15667 this.condition._walk(visitor);
15668 this.consequent._walk(visitor);
15669 this.alternative._walk(visitor);
15673 var AST_Assign = DEFNODE("Assign", null, {
15674 $documentation: "An assignment expression — `a = b + 5`"
15676 var AST_Array = DEFNODE("Array", "elements", {
15677 $documentation: "An array literal",
15679 elements: "[AST_Node*] array of elements"
15681 _walk: function(visitor) {
15682 return visitor._visit(this, function() {
15683 this.elements.forEach(function(el) {
15689 var AST_Object = DEFNODE("Object", "properties", {
15690 $documentation: "An object literal",
15692 properties: "[AST_ObjectProperty*] array of properties"
15694 _walk: function(visitor) {
15695 return visitor._visit(this, function() {
15696 this.properties.forEach(function(prop) {
15697 prop._walk(visitor);
15702 var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
15703 $documentation: "Base class for literal object properties",
15705 key: "[string] the property name; it's always a plain string in our AST, no matter if it was a string, number or identifier in original code",
15706 value: "[AST_Node] property value. For setters and getters this is an AST_Function."
15708 _walk: function(visitor) {
15709 return visitor._visit(this, function() {
15710 this.value._walk(visitor);
15714 var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
15715 $documentation: "A key: value object property"
15716 }, AST_ObjectProperty);
15717 var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
15718 $documentation: "An object setter property"
15719 }, AST_ObjectProperty);
15720 var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
15721 $documentation: "An object getter property"
15722 }, AST_ObjectProperty);
15723 var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
15725 name: "[string] name of this symbol",
15726 scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
15727 thedef: "[SymbolDef/S] the definition of this symbol"
15729 $documentation: "Base class for all symbols"
15731 var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
15732 $documentation: "The name of a property accessor (setter/getter function)"
15734 var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
15735 $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
15737 init: "[AST_Node*/S] array of initializers for this declaration."
15740 var AST_SymbolVar = DEFNODE("SymbolVar", null, {
15741 $documentation: "Symbol defining a variable"
15742 }, AST_SymbolDeclaration);
15743 var AST_SymbolConst = DEFNODE("SymbolConst", null, {
15744 $documentation: "A constant declaration"
15745 }, AST_SymbolDeclaration);
15746 var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
15747 $documentation: "Symbol naming a function argument"
15749 var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
15750 $documentation: "Symbol defining a function"
15751 }, AST_SymbolDeclaration);
15752 var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
15753 $documentation: "Symbol naming a function expression"
15754 }, AST_SymbolDeclaration);
15755 var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
15756 $documentation: "Symbol naming the exception in catch"
15757 }, AST_SymbolDeclaration);
15758 var AST_Label = DEFNODE("Label", "references", {
15759 $documentation: "Symbol naming a label (declaration)",
15761 references: "[AST_LabelRef*] a list of nodes referring to this label"
15764 var AST_SymbolRef = DEFNODE("SymbolRef", null, {
15765 $documentation: "Reference to some symbol (not definition/declaration)"
15767 var AST_LabelRef = DEFNODE("LabelRef", null, {
15768 $documentation: "Reference to a label symbol"
15770 var AST_This = DEFNODE("This", null, {
15771 $documentation: "The `this` symbol"
15773 var AST_Constant = DEFNODE("Constant", null, {
15774 $documentation: "Base class for all constants",
15775 getValue: function() {
15779 var AST_String = DEFNODE("String", "value", {
15780 $documentation: "A string literal",
15782 value: "[string] the contents of this string"
15785 var AST_Number = DEFNODE("Number", "value", {
15786 $documentation: "A number literal",
15788 value: "[number] the numeric value"
15791 var AST_RegExp = DEFNODE("RegExp", "value", {
15792 $documentation: "A regexp literal",
15794 value: "[RegExp] the actual regexp"
15797 var AST_Atom = DEFNODE("Atom", null, {
15798 $documentation: "Base class for atoms"
15800 var AST_Null = DEFNODE("Null", null, {
15801 $documentation: "The `null` atom",
15804 var AST_NaN = DEFNODE("NaN", null, {
15805 $documentation: "The impossible value",
15808 var AST_Undefined = DEFNODE("Undefined", null, {
15809 $documentation: "The `undefined` value",
15810 value: function() {}()
15812 var AST_Hole = DEFNODE("Hole", null, {
15813 $documentation: "A hole in an array",
15814 value: function() {}()
15816 var AST_Infinity = DEFNODE("Infinity", null, {
15817 $documentation: "The `Infinity` value",
15820 var AST_Boolean = DEFNODE("Boolean", null, {
15821 $documentation: "Base class for booleans"
15823 var AST_False = DEFNODE("False", null, {
15824 $documentation: "The `false` atom",
15827 var AST_True = DEFNODE("True", null, {
15828 $documentation: "The `true` atom",
15831 function TreeWalker(callback) {
15832 this.visit = callback;
15835 TreeWalker.prototype = {
15836 _visit: function(node, descend) {
15837 this.stack.push(node);
15838 var ret = this.visit(node, descend ? function() {
15839 descend.call(node);
15841 if (!ret && descend) {
15842 descend.call(node);
15847 parent: function(n) {
15848 return this.stack[this.stack.length - 2 - (n || 0)];
15850 push: function(node) {
15851 this.stack.push(node);
15854 return this.stack.pop();
15857 return this.stack[this.stack.length - 1];
15859 find_parent: function(type) {
15860 var stack = this.stack;
15861 for (var i = stack.length; --i >= 0; ) {
15863 if (x instanceof type) return x;
15866 in_boolean_context: function() {
15867 var stack = this.stack;
15868 var i = stack.length, self = stack[--i];
15870 var p = stack[--i];
15871 if (p instanceof AST_If && p.condition === self || p instanceof AST_Conditional && p.condition === self || p instanceof AST_DWLoop && p.condition === self || p instanceof AST_For && p.condition === self || p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
15874 if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||"))) return false;
15878 loopcontrol_target: function(label) {
15879 var stack = this.stack;
15881 for (var i = stack.length; --i >= 0; ) {
15883 if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
15888 for (var i = stack.length; --i >= 0; ) {
15890 if (x instanceof AST_Switch || x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) return x;
15896 var KEYWORDS = "break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with";
15897 var KEYWORDS_ATOM = "false null true";
15898 var RESERVED_WORDS = "abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile" + " " + KEYWORDS_ATOM + " " + KEYWORDS;
15899 var KEYWORDS_BEFORE_EXPRESSION = "return new delete throw else case";
15900 KEYWORDS = makePredicate(KEYWORDS);
15901 RESERVED_WORDS = makePredicate(RESERVED_WORDS);
15902 KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION);
15903 KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
15904 var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
15905 var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
15906 var RE_OCT_NUMBER = /^0[0-7]+$/;
15907 var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
15908 var OPERATORS = makePredicate([ "in", "instanceof", "typeof", "new", "void", "delete", "++", "--", "+", "-", "!", "~", "&", "|", "^", "*", "/", "%", ">>", "<<", ">>>", "<", ">", "<=", ">=", "==", "===", "!=", "!==", "?", "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=", "&&", "||" ]);
15909 var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
15910 var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
15911 var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
15912 var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
15914 letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971\\u0972\\u097B-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3D\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC\\u0EDD\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8B\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10D0-\\u10FA\\u10FC\\u1100-\\u1159\\u115F-\\u11A2\\u11A8-\\u11F9\\u1200-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u1676\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19A9\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u2094\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2C6F\\u2C71-\\u2C7D\\u2C80-\\u2CE4\\u2D00-\\u2D25\\u2D30-\\u2D65\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31B7\\u31F0-\\u31FF\\u3400\\u4DB5\\u4E00\\u9FC3\\uA000-\\uA48C\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA65F\\uA662-\\uA66E\\uA67F-\\uA697\\uA717-\\uA71F\\uA722-\\uA788\\uA78B\\uA78C\\uA7FB-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA90A-\\uA925\\uA930-\\uA946\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAC00\\uD7A3\\uF900-\\uFA2D\\uFA30-\\uFA6A\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),
15915 non_spacing_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"),
15916 space_combining_mark: new RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]"),
15917 connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
15919 function is_letter(code) {
15920 return code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 170 && UNICODE.letter.test(String.fromCharCode(code));
15922 function is_digit(code) {
15923 return code >= 48 && code <= 57;
15925 function is_alphanumeric_char(code) {
15926 return is_digit(code) || is_letter(code);
15928 function is_unicode_combining_mark(ch) {
15929 return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
15931 function is_unicode_connector_punctuation(ch) {
15932 return UNICODE.connector_punctuation.test(ch);
15934 function is_identifier(name) {
15935 return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name);
15937 function is_identifier_start(code) {
15938 return code == 36 || code == 95 || is_letter(code);
15940 function is_identifier_char(ch) {
15941 var code = ch.charCodeAt(0);
15942 return is_identifier_start(code) || is_digit(code) || code == 8204 || code == 8205 || is_unicode_combining_mark(ch) || is_unicode_connector_punctuation(ch);
15944 function is_identifier_string(str) {
15945 var i = str.length;
15946 if (i == 0) return false;
15947 if (is_digit(str.charCodeAt(0))) return false;
15949 if (!is_identifier_char(str.charAt(i))) return false;
15953 function parse_js_number(num) {
15954 if (RE_HEX_NUMBER.test(num)) {
15955 return parseInt(num.substr(2), 16);
15956 } else if (RE_OCT_NUMBER.test(num)) {
15957 return parseInt(num.substr(1), 8);
15958 } else if (RE_DEC_NUMBER.test(num)) {
15959 return parseFloat(num);
15962 function JS_Parse_Error(message, line, col, pos) {
15963 this.message = message;
15967 this.stack = new Error().stack;
15969 JS_Parse_Error.prototype.toString = function() {
15970 return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
15972 function js_error(message, filename, line, col, pos) {
15973 throw new JS_Parse_Error(message, line, col, pos);
15975 function is_token(token, type, val) {
15976 return token.type == type && (val == null || token.value == val);
15979 function tokenizer($TEXT, filename) {
15981 text: $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ""),
15982 filename: filename,
15989 newline_before: false,
15990 regex_allowed: false,
15991 comments_before: []
15994 return S.text.charAt(S.pos);
15996 function next(signal_eof, in_string) {
15997 var ch = S.text.charAt(S.pos++);
15998 if (signal_eof && !ch) throw EX_EOF;
16000 S.newline_before = S.newline_before || !in_string;
16008 function find(what, signal_eof) {
16009 var pos = S.text.indexOf(what, S.pos);
16010 if (signal_eof && pos == -1) throw EX_EOF;
16013 function start_token() {
16014 S.tokline = S.line;
16018 function token(type, value, is_comment) {
16019 S.regex_allowed = type == "operator" && !UNARY_POSTFIX[value] || type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value) || type == "punc" && PUNC_BEFORE_EXPRESSION(value);
16027 nlb: S.newline_before,
16031 ret.comments_before = S.comments_before;
16032 S.comments_before = [];
16033 for (var i = 0, len = ret.comments_before.length; i < len; i++) {
16034 ret.nlb = ret.nlb || ret.comments_before[i].nlb;
16037 S.newline_before = false;
16038 return new AST_Token(ret);
16040 function skip_whitespace() {
16041 while (WHITESPACE_CHARS(peek())) next();
16043 function read_while(pred) {
16044 var ret = "", ch, i = 0;
16045 while ((ch = peek()) && pred(ch, i++)) ret += next();
16048 function parse_error(err) {
16049 js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
16051 function read_num(prefix) {
16052 var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
16053 var num = read_while(function(ch, i) {
16054 var code = ch.charCodeAt(0);
16058 return has_x ? false : has_x = true;
16062 return has_x ? true : has_e ? false : has_e = after_e = true;
16065 return after_e || i == 0 && !prefix;
16070 case after_e = false, 46:
16071 return !has_dot && !has_x && !has_e ? has_dot = true : false;
16073 return is_alphanumeric_char(code);
16075 if (prefix) num = prefix + num;
16076 var valid = parse_js_number(num);
16077 if (!isNaN(valid)) {
16078 return token("num", valid);
16080 parse_error("Invalid syntax: " + num);
16083 function read_escaped_char(in_string) {
16084 var ch = next(true, in_string);
16085 switch (ch.charCodeAt(0)) {
16108 return String.fromCharCode(hex_bytes(2));
16111 return String.fromCharCode(hex_bytes(4));
16120 function hex_bytes(n) {
16122 for (;n > 0; --n) {
16123 var digit = parseInt(next(true), 16);
16124 if (isNaN(digit)) parse_error("Invalid hex-character pattern in string");
16125 num = num << 4 | digit;
16129 var read_string = with_eof_error("Unterminated string constant", function() {
16130 var quote = next(), ret = "";
16132 var ch = next(true);
16134 var octal_len = 0, first = null;
16135 ch = read_while(function(ch) {
16136 if (ch >= "0" && ch <= "7") {
16139 return ++octal_len;
16140 } else if (first <= "3" && octal_len <= 2) return ++octal_len; else if (first >= "4" && octal_len <= 1) return ++octal_len;
16144 if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8)); else ch = read_escaped_char(true);
16145 } else if (ch == quote) break;
16148 return token("string", ret);
16150 function read_line_comment() {
16152 var i = find("\n"), ret;
16154 ret = S.text.substr(S.pos);
16155 S.pos = S.text.length;
16157 ret = S.text.substring(S.pos, i);
16160 return token("comment1", ret, true);
16162 var read_multiline_comment = with_eof_error("Unterminated multiline comment", function() {
16164 var i = find("*/", true);
16165 var text = S.text.substring(S.pos, i);
16166 var a = text.split("\n"), n = a.length;
16169 if (n > 1) S.col = a[n - 1].length; else S.col += a[n - 1].length;
16171 S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
16172 return token("comment2", text, true);
16174 function read_name() {
16175 var backslash = false, name = "", ch, escaped = false, hex;
16176 while ((ch = peek()) != null) {
16178 if (ch == "\\") escaped = backslash = true, next(); else if (is_identifier_char(ch)) name += next(); else break;
16180 if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
16181 ch = read_escaped_char();
16182 if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
16187 if (KEYWORDS(name) && escaped) {
16188 hex = name.charCodeAt(0).toString(16).toUpperCase();
16189 name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
16193 var read_regexp = with_eof_error("Unterminated regular expression", function(regexp) {
16194 var prev_backslash = false, ch, in_class = false;
16195 while (ch = next(true)) if (prev_backslash) {
16196 regexp += "\\" + ch;
16197 prev_backslash = false;
16198 } else if (ch == "[") {
16201 } else if (ch == "]" && in_class) {
16204 } else if (ch == "/" && !in_class) {
16206 } else if (ch == "\\") {
16207 prev_backslash = true;
16211 var mods = read_name();
16212 return token("regexp", new RegExp(regexp, mods));
16214 function read_operator(prefix) {
16215 function grow(op) {
16216 if (!peek()) return op;
16217 var bigger = op + peek();
16218 if (OPERATORS(bigger)) {
16220 return grow(bigger);
16225 return token("operator", grow(prefix || next()));
16227 function handle_slash() {
16229 var regex_allowed = S.regex_allowed;
16232 S.comments_before.push(read_line_comment());
16233 S.regex_allowed = regex_allowed;
16234 return next_token();
16237 S.comments_before.push(read_multiline_comment());
16238 S.regex_allowed = regex_allowed;
16239 return next_token();
16241 return S.regex_allowed ? read_regexp("") : read_operator("/");
16243 function handle_dot() {
16245 return is_digit(peek().charCodeAt(0)) ? read_num(".") : token("punc", ".");
16247 function read_word() {
16248 var word = read_name();
16249 return KEYWORDS_ATOM(word) ? token("atom", word) : !KEYWORDS(word) ? token("name", word) : OPERATORS(word) ? token("operator", word) : token("keyword", word);
16251 function with_eof_error(eof_error, cont) {
16252 return function(x) {
16256 if (ex === EX_EOF) parse_error(eof_error); else throw ex;
16260 function next_token(force_regexp) {
16261 if (force_regexp != null) return read_regexp(force_regexp);
16265 if (!ch) return token("eof");
16266 var code = ch.charCodeAt(0);
16270 return read_string();
16273 return handle_dot();
16276 return handle_slash();
16278 if (is_digit(code)) return read_num();
16279 if (PUNC_CHARS(ch)) return token("punc", next());
16280 if (OPERATOR_CHARS(ch)) return read_operator();
16281 if (code == 92 || is_identifier_start(code)) return read_word();
16282 parse_error("Unexpected character '" + ch + "'");
16284 next_token.context = function(nc) {
16290 var UNARY_PREFIX = makePredicate([ "typeof", "void", "delete", "--", "++", "!", "~", "-", "+" ]);
16291 var UNARY_POSTFIX = makePredicate([ "--", "++" ]);
16292 var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
16293 var PRECEDENCE = function(a, ret) {
16294 for (var i = 0, n = 1; i < a.length; ++i, ++n) {
16296 for (var j = 0; j < b.length; ++j) {
16301 }([ [ "||" ], [ "&&" ], [ "|" ], [ "^" ], [ "&" ], [ "==", "===", "!=", "!==" ], [ "<", ">", "<=", ">=", "in", "instanceof" ], [ ">>", "<<", ">>>" ], [ "+", "-" ], [ "*", "/", "%" ] ], {});
16302 var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
16303 var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
16304 function parse($TEXT, options) {
16305 options = defaults(options, {
16312 input: typeof $TEXT == "string" ? tokenizer($TEXT, options.filename) : $TEXT,
16317 in_directives: true,
16322 function is(type, value) {
16323 return is_token(S.token, type, value);
16326 return S.peeked || (S.peeked = S.input());
16331 S.token = S.peeked;
16334 S.token = S.input();
16336 S.in_directives = S.in_directives && (S.token.type == "string" || is("punc", ";"));
16342 function croak(msg, line, col, pos) {
16343 var ctx = S.input.context();
16344 js_error(msg, ctx.filename, line != null ? line : ctx.tokline, col != null ? col : ctx.tokcol, pos != null ? pos : ctx.tokpos);
16346 function token_error(token, msg) {
16347 croak(msg, token.line, token.col);
16349 function unexpected(token) {
16350 if (token == null) token = S.token;
16351 token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
16353 function expect_token(type, val) {
16354 if (is(type, val)) {
16357 token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
16359 function expect(punc) {
16360 return expect_token("punc", punc);
16362 function can_insert_semicolon() {
16363 return !options.strict && (S.token.nlb || is("eof") || is("punc", "}"));
16365 function semicolon() {
16366 if (is("punc", ";")) next(); else if (!can_insert_semicolon()) unexpected();
16368 function parenthesised() {
16370 var exp = expression(true);
16374 function embed_tokens(parser) {
16375 return function() {
16376 var start = S.token;
16377 var expr = parser();
16379 expr.start = start;
16384 var statement = embed_tokens(function() {
16386 if (is("operator", "/") || is("operator", "/=")) {
16388 S.token = S.input(S.token.value.substr(1));
16390 switch (S.token.type) {
16392 var dir = S.in_directives, stat = simple_statement();
16393 if (dir && stat.body instanceof AST_String && !is("punc", ",")) return new AST_Directive({
16394 value: stat.body.value
16402 return simple_statement();
16405 return is_token(peek(), "punc", ":") ? labeled_statement() : simple_statement();
16408 switch (S.token.value) {
16410 return new AST_BlockStatement({
16418 return simple_statement();
16422 return new AST_EmptyStatement();
16429 switch (tmp = S.token.value, next(), tmp) {
16431 return break_cont(AST_Break);
16434 return break_cont(AST_Continue);
16438 return new AST_Debugger();
16441 return new AST_Do({
16442 body: in_loop(statement),
16443 condition: (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(),
16448 return new AST_While({
16449 condition: parenthesised(),
16450 body: in_loop(statement)
16457 return function_(true);
16463 if (S.in_function == 0) croak("'return' outside of function");
16464 return new AST_Return({
16465 value: is("punc", ";") ? (next(), null) : can_insert_semicolon() ? null : (tmp = expression(true),
16470 return new AST_Switch({
16471 expression: parenthesised(),
16472 body: in_loop(switch_body_)
16476 if (S.token.nlb) croak("Illegal newline after 'throw'");
16477 return new AST_Throw({
16478 value: (tmp = expression(true), semicolon(), tmp)
16485 return tmp = var_(), semicolon(), tmp;
16488 return tmp = const_(), semicolon(), tmp;
16491 return new AST_With({
16492 expression: parenthesised(),
16501 function labeled_statement() {
16502 var label = as_symbol(AST_Label);
16503 if (find_if(function(l) {
16504 return l.name == label.name;
16506 croak("Label " + label.name + " defined twice");
16509 S.labels.push(label);
16510 var stat = statement();
16512 return new AST_LabeledStatement({
16517 function simple_statement(tmp) {
16518 return new AST_SimpleStatement({
16519 body: (tmp = expression(true), semicolon(), tmp)
16522 function break_cont(type) {
16524 if (!can_insert_semicolon()) {
16525 label = as_symbol(AST_LabelRef, true);
16527 if (label != null) {
16528 if (!find_if(function(l) {
16529 return l.name == label.name;
16530 }, S.labels)) croak("Undefined label " + label.name);
16531 } else if (S.in_loop == 0) croak(type.TYPE + " not inside a loop or switch");
16540 if (!is("punc", ";")) {
16541 init = is("keyword", "var") ? (next(), var_(true)) : expression(true, true);
16542 if (is("operator", "in")) {
16543 if (init instanceof AST_Var && init.definitions.length > 1) croak("Only one variable declaration allowed in for..in loop");
16545 return for_in(init);
16548 return regular_for(init);
16550 function regular_for(init) {
16552 var test = is("punc", ";") ? null : expression(true);
16554 var step = is("punc", ")") ? null : expression(true);
16556 return new AST_For({
16560 body: in_loop(statement)
16563 function for_in(init) {
16564 var lhs = init instanceof AST_Var ? init.definitions[0].name : null;
16565 var obj = expression(true);
16567 return new AST_ForIn({
16571 body: in_loop(statement)
16574 var function_ = function(in_statement, ctor) {
16575 var is_accessor = ctor === AST_Accessor;
16576 var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : is_accessor ? AST_SymbolAccessor : AST_SymbolLambda) : is_accessor && (is("string") || is("num")) ? as_atom_node() : null;
16577 if (in_statement && !name) unexpected();
16579 if (!ctor) ctor = in_statement ? AST_Defun : AST_Function;
16582 argnames: function(first, a) {
16583 while (!is("punc", ")")) {
16584 if (first) first = false; else expect(",");
16585 a.push(as_symbol(AST_SymbolFunarg));
16590 body: function(loop, labels) {
16592 S.in_directives = true;
16600 }(S.in_loop, S.labels)
16604 var cond = parenthesised(), body = statement(), belse = null;
16605 if (is("keyword", "else")) {
16607 belse = statement();
16609 return new AST_If({
16615 function block_() {
16618 while (!is("punc", "}")) {
16619 if (is("eof")) unexpected();
16620 a.push(statement());
16625 function switch_body_() {
16627 var a = [], cur = null, branch = null, tmp;
16628 while (!is("punc", "}")) {
16629 if (is("eof")) unexpected();
16630 if (is("keyword", "case")) {
16631 if (branch) branch.end = prev();
16633 branch = new AST_Case({
16634 start: (tmp = S.token, next(), tmp),
16635 expression: expression(true),
16640 } else if (is("keyword", "default")) {
16641 if (branch) branch.end = prev();
16643 branch = new AST_Default({
16644 start: (tmp = S.token, next(), expect(":"), tmp),
16649 if (!cur) unexpected();
16650 cur.push(statement());
16653 if (branch) branch.end = prev();
16658 var body = block_(), bcatch = null, bfinally = null;
16659 if (is("keyword", "catch")) {
16660 var start = S.token;
16663 var name = as_symbol(AST_SymbolCatch);
16665 bcatch = new AST_Catch({
16672 if (is("keyword", "finally")) {
16673 var start = S.token;
16675 bfinally = new AST_Finally({
16681 if (!bcatch && !bfinally) croak("Missing catch/finally blocks");
16682 return new AST_Try({
16688 function vardefs(no_in, in_const) {
16691 a.push(new AST_VarDef({
16693 name: as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
16694 value: is("operator", "=") ? (next(), expression(false, no_in)) : null,
16697 if (!is("punc", ",")) break;
16702 var var_ = function(no_in) {
16703 return new AST_Var({
16705 definitions: vardefs(no_in, false),
16709 var const_ = function() {
16710 return new AST_Const({
16712 definitions: vardefs(false, true),
16716 var new_ = function() {
16717 var start = S.token;
16718 expect_token("operator", "new");
16719 var newexp = expr_atom(false), args;
16720 if (is("punc", "(")) {
16722 args = expr_list(")");
16726 return subscripts(new AST_New({
16728 expression: newexp,
16733 function as_atom_node() {
16734 var tok = S.token, ret;
16735 switch (tok.type) {
16737 return as_symbol(AST_SymbolRef);
16740 ret = new AST_Number({
16748 ret = new AST_String({
16756 ret = new AST_RegExp({
16764 switch (tok.value) {
16766 ret = new AST_False({
16773 ret = new AST_True({
16780 ret = new AST_Null({
16791 var expr_atom = function(allow_calls) {
16792 if (is("operator", "new")) {
16795 var start = S.token;
16797 switch (start.value) {
16800 var ex = expression(true);
16804 return subscripts(ex, allow_calls);
16807 return subscripts(array_(), allow_calls);
16810 return subscripts(object_(), allow_calls);
16814 if (is("keyword", "function")) {
16816 var func = function_(false);
16817 func.start = start;
16819 return subscripts(func, allow_calls);
16821 if (ATOMIC_START_TOKEN[S.token.type]) {
16822 return subscripts(as_atom_node(), allow_calls);
16826 function expr_list(closing, allow_trailing_comma, allow_empty) {
16827 var first = true, a = [];
16828 while (!is("punc", closing)) {
16829 if (first) first = false; else expect(",");
16830 if (allow_trailing_comma && is("punc", closing)) break;
16831 if (is("punc", ",") && allow_empty) {
16832 a.push(new AST_Hole({
16837 a.push(expression(false));
16843 var array_ = embed_tokens(function() {
16845 return new AST_Array({
16846 elements: expr_list("]", !options.strict, true)
16849 var object_ = embed_tokens(function() {
16851 var first = true, a = [];
16852 while (!is("punc", "}")) {
16853 if (first) first = false; else expect(",");
16854 if (!options.strict && is("punc", "}")) break;
16855 var start = S.token;
16856 var type = start.type;
16857 var name = as_property_name();
16858 if (type == "name" && !is("punc", ":")) {
16859 if (name == "get") {
16860 a.push(new AST_ObjectGetter({
16863 value: function_(false, AST_Accessor),
16868 if (name == "set") {
16869 a.push(new AST_ObjectSetter({
16872 value: function_(false, AST_Accessor),
16879 a.push(new AST_ObjectKeyVal({
16882 value: expression(false),
16887 return new AST_Object({
16891 function as_property_name() {
16894 switch (tmp.type) {
16907 function as_name() {
16910 switch (tmp.type) {
16921 function as_symbol(type, noerror) {
16923 if (!noerror) croak("Name expected");
16926 var name = S.token.value;
16927 var sym = new (name == "this" ? AST_This : type)({
16928 name: String(S.token.value),
16935 var subscripts = function(expr, allow_calls) {
16936 var start = expr.start;
16937 if (is("punc", ".")) {
16939 return subscripts(new AST_Dot({
16942 property: as_name(),
16946 if (is("punc", "[")) {
16948 var prop = expression(true);
16950 return subscripts(new AST_Sub({
16957 if (allow_calls && is("punc", "(")) {
16959 return subscripts(new AST_Call({
16962 args: expr_list(")"),
16968 var maybe_unary = function(allow_calls) {
16969 var start = S.token;
16970 if (is("operator") && UNARY_PREFIX(start.value)) {
16972 var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls));
16977 var val = expr_atom(allow_calls);
16978 while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
16979 val = make_unary(AST_UnaryPostfix, S.token.value, val);
16986 function make_unary(ctor, op, expr) {
16987 if ((op == "++" || op == "--") && !is_assignable(expr)) croak("Invalid use of " + op + " operator");
16993 var expr_op = function(left, min_prec, no_in) {
16994 var op = is("operator") ? S.token.value : null;
16995 if (op == "in" && no_in) op = null;
16996 var prec = op != null ? PRECEDENCE[op] : null;
16997 if (prec != null && prec > min_prec) {
16999 var right = expr_op(maybe_unary(true), prec, no_in);
17000 return expr_op(new AST_Binary({
17006 }), min_prec, no_in);
17010 function expr_ops(no_in) {
17011 return expr_op(maybe_unary(true), 0, no_in);
17013 var maybe_conditional = function(no_in) {
17014 var start = S.token;
17015 var expr = expr_ops(no_in);
17016 if (is("operator", "?")) {
17018 var yes = expression(false);
17020 return new AST_Conditional({
17024 alternative: expression(false, no_in),
17030 function is_assignable(expr) {
17031 if (!options.strict) return true;
17032 if (expr instanceof AST_This) return false;
17033 return expr instanceof AST_PropAccess || expr instanceof AST_Symbol;
17035 var maybe_assign = function(no_in) {
17036 var start = S.token;
17037 var left = maybe_conditional(no_in), val = S.token.value;
17038 if (is("operator") && ASSIGNMENT(val)) {
17039 if (is_assignable(left)) {
17041 return new AST_Assign({
17045 right: maybe_assign(no_in),
17049 croak("Invalid assignment");
17053 var expression = function(commas, no_in) {
17054 var start = S.token;
17055 var expr = maybe_assign(no_in);
17056 if (commas && is("punc", ",")) {
17058 return new AST_Seq({
17061 cdr: expression(true, no_in),
17067 function in_loop(cont) {
17073 if (options.expression) {
17074 return expression(true);
17076 return function() {
17077 var start = S.token;
17079 while (!is("eof")) body.push(statement());
17081 var toplevel = options.toplevel;
17083 toplevel.body = toplevel.body.concat(body);
17084 toplevel.end = end;
17086 toplevel = new AST_Toplevel({
17096 function TreeTransformer(before, after) {
17097 TreeWalker.call(this);
17098 this.before = before;
17099 this.after = after;
17101 TreeTransformer.prototype = new TreeWalker();
17102 (function(undefined) {
17103 function _(node, descend) {
17104 node.DEFMETHOD("transform", function(tw, in_list) {
17107 if (tw.before) x = tw.before(this, descend, in_list);
17108 if (x === undefined) {
17113 tw.stack[tw.stack.length - 1] = x = this.clone();
17115 y = tw.after(x, in_list);
17116 if (y !== undefined) x = y;
17123 function do_list(list, tw) {
17124 return MAP(list, function(node) {
17125 return node.transform(tw, true);
17129 _(AST_LabeledStatement, function(self, tw) {
17130 self.label = self.label.transform(tw);
17131 self.body = self.body.transform(tw);
17133 _(AST_SimpleStatement, function(self, tw) {
17134 self.body = self.body.transform(tw);
17136 _(AST_Block, function(self, tw) {
17137 self.body = do_list(self.body, tw);
17139 _(AST_DWLoop, function(self, tw) {
17140 self.condition = self.condition.transform(tw);
17141 self.body = self.body.transform(tw);
17143 _(AST_For, function(self, tw) {
17144 if (self.init) self.init = self.init.transform(tw);
17145 if (self.condition) self.condition = self.condition.transform(tw);
17146 if (self.step) self.step = self.step.transform(tw);
17147 self.body = self.body.transform(tw);
17149 _(AST_ForIn, function(self, tw) {
17150 self.init = self.init.transform(tw);
17151 self.object = self.object.transform(tw);
17152 self.body = self.body.transform(tw);
17154 _(AST_With, function(self, tw) {
17155 self.expression = self.expression.transform(tw);
17156 self.body = self.body.transform(tw);
17158 _(AST_Exit, function(self, tw) {
17159 if (self.value) self.value = self.value.transform(tw);
17161 _(AST_LoopControl, function(self, tw) {
17162 if (self.label) self.label = self.label.transform(tw);
17164 _(AST_If, function(self, tw) {
17165 self.condition = self.condition.transform(tw);
17166 self.body = self.body.transform(tw);
17167 if (self.alternative) self.alternative = self.alternative.transform(tw);
17169 _(AST_Switch, function(self, tw) {
17170 self.expression = self.expression.transform(tw);
17171 self.body = do_list(self.body, tw);
17173 _(AST_Case, function(self, tw) {
17174 self.expression = self.expression.transform(tw);
17175 self.body = do_list(self.body, tw);
17177 _(AST_Try, function(self, tw) {
17178 self.body = do_list(self.body, tw);
17179 if (self.bcatch) self.bcatch = self.bcatch.transform(tw);
17180 if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
17182 _(AST_Catch, function(self, tw) {
17183 self.argname = self.argname.transform(tw);
17184 self.body = do_list(self.body, tw);
17186 _(AST_Definitions, function(self, tw) {
17187 self.definitions = do_list(self.definitions, tw);
17189 _(AST_VarDef, function(self, tw) {
17190 if (self.value) self.value = self.value.transform(tw);
17192 _(AST_Lambda, function(self, tw) {
17193 if (self.name) self.name = self.name.transform(tw);
17194 self.argnames = do_list(self.argnames, tw);
17195 self.body = do_list(self.body, tw);
17197 _(AST_Call, function(self, tw) {
17198 self.expression = self.expression.transform(tw);
17199 self.args = do_list(self.args, tw);
17201 _(AST_Seq, function(self, tw) {
17202 self.car = self.car.transform(tw);
17203 self.cdr = self.cdr.transform(tw);
17205 _(AST_Dot, function(self, tw) {
17206 self.expression = self.expression.transform(tw);
17208 _(AST_Sub, function(self, tw) {
17209 self.expression = self.expression.transform(tw);
17210 self.property = self.property.transform(tw);
17212 _(AST_Unary, function(self, tw) {
17213 self.expression = self.expression.transform(tw);
17215 _(AST_Binary, function(self, tw) {
17216 self.left = self.left.transform(tw);
17217 self.right = self.right.transform(tw);
17219 _(AST_Conditional, function(self, tw) {
17220 self.condition = self.condition.transform(tw);
17221 self.consequent = self.consequent.transform(tw);
17222 self.alternative = self.alternative.transform(tw);
17224 _(AST_Array, function(self, tw) {
17225 self.elements = do_list(self.elements, tw);
17227 _(AST_Object, function(self, tw) {
17228 self.properties = do_list(self.properties, tw);
17230 _(AST_ObjectProperty, function(self, tw) {
17231 self.value = self.value.transform(tw);
17235 function SymbolDef(scope, index, orig) {
17236 this.name = orig.name;
17237 this.orig = [ orig ];
17238 this.scope = scope;
17239 this.references = [];
17240 this.global = false;
17241 this.mangled_name = null;
17242 this.undeclared = false;
17243 this.constant = false;
17244 this.index = index;
17246 SymbolDef.prototype = {
17247 unmangleable: function(options) {
17248 return this.global && !(options && options.toplevel) || this.undeclared || !(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with);
17250 mangle: function(options) {
17251 if (!this.mangled_name && !this.unmangleable(options)) {
17252 var s = this.scope;
17253 if (this.orig[0] instanceof AST_SymbolLambda && !options.screw_ie8) s = s.parent_scope;
17254 this.mangled_name = s.next_mangled(options);
17258 AST_Toplevel.DEFMETHOD("figure_out_scope", function() {
17260 var scope = self.parent_scope = null;
17261 var labels = new Dictionary();
17263 var tw = new TreeWalker(function(node, descend) {
17264 if (node instanceof AST_Scope) {
17265 node.init_scope_vars(nesting);
17266 var save_scope = node.parent_scope = scope;
17267 var save_labels = labels;
17270 labels = new Dictionary();
17272 labels = save_labels;
17273 scope = save_scope;
17277 if (node instanceof AST_Directive) {
17278 node.scope = scope;
17279 push_uniq(scope.directives, node.value);
17282 if (node instanceof AST_With) {
17283 for (var s = scope; s; s = s.parent_scope) s.uses_with = true;
17286 if (node instanceof AST_LabeledStatement) {
17287 var l = node.label;
17288 if (labels.has(l.name)) throw new Error(string_template("Label {name} defined twice", l));
17289 labels.set(l.name, l);
17291 labels.del(l.name);
17294 if (node instanceof AST_Symbol) {
17295 node.scope = scope;
17297 if (node instanceof AST_Label) {
17298 node.thedef = node;
17299 node.init_scope_vars();
17301 if (node instanceof AST_SymbolLambda) {
17302 scope.def_function(node);
17303 } else if (node instanceof AST_SymbolDefun) {
17304 (node.scope = scope.parent_scope).def_function(node);
17305 } else if (node instanceof AST_SymbolVar || node instanceof AST_SymbolConst) {
17306 var def = scope.def_variable(node);
17307 def.constant = node instanceof AST_SymbolConst;
17308 def.init = tw.parent().value;
17309 } else if (node instanceof AST_SymbolCatch) {
17310 scope.def_variable(node);
17312 if (node instanceof AST_LabelRef) {
17313 var sym = labels.get(node.name);
17314 if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
17316 line: node.start.line,
17317 col: node.start.col
17324 var globals = self.globals = new Dictionary();
17325 var tw = new TreeWalker(function(node, descend) {
17326 if (node instanceof AST_Lambda) {
17327 var prev_func = func;
17333 if (node instanceof AST_LabelRef) {
17337 if (node instanceof AST_SymbolRef) {
17338 var name = node.name;
17339 var sym = node.scope.find_variable(name);
17342 if (globals.has(name)) {
17343 g = globals.get(name);
17345 g = new SymbolDef(self, globals.size(), node);
17346 g.undeclared = true;
17348 globals.set(name, g);
17351 if (name == "eval" && tw.parent() instanceof AST_Call) {
17352 for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) s.uses_eval = true;
17354 if (name == "arguments") {
17355 func.uses_arguments = true;
17366 AST_Scope.DEFMETHOD("init_scope_vars", function(nesting) {
17367 this.directives = [];
17368 this.variables = new Dictionary();
17369 this.functions = new Dictionary();
17370 this.uses_with = false;
17371 this.uses_eval = false;
17372 this.parent_scope = null;
17373 this.enclosed = [];
17375 this.nesting = nesting;
17377 AST_Scope.DEFMETHOD("strict", function() {
17378 return this.has_directive("use strict");
17380 AST_Lambda.DEFMETHOD("init_scope_vars", function() {
17381 AST_Scope.prototype.init_scope_vars.apply(this, arguments);
17382 this.uses_arguments = false;
17384 AST_SymbolRef.DEFMETHOD("reference", function() {
17385 var def = this.definition();
17386 def.references.push(this);
17387 var s = this.scope;
17389 push_uniq(s.enclosed, def);
17390 if (s === def.scope) break;
17391 s = s.parent_scope;
17393 this.frame = this.scope.nesting - def.scope.nesting;
17395 AST_Label.DEFMETHOD("init_scope_vars", function() {
17396 this.references = [];
17398 AST_LabelRef.DEFMETHOD("reference", function() {
17399 this.thedef.references.push(this);
17401 AST_Scope.DEFMETHOD("find_variable", function(name) {
17402 if (name instanceof AST_Symbol) name = name.name;
17403 return this.variables.get(name) || this.parent_scope && this.parent_scope.find_variable(name);
17405 AST_Scope.DEFMETHOD("has_directive", function(value) {
17406 return this.parent_scope && this.parent_scope.has_directive(value) || (this.directives.indexOf(value) >= 0 ? this : null);
17408 AST_Scope.DEFMETHOD("def_function", function(symbol) {
17409 this.functions.set(symbol.name, this.def_variable(symbol));
17411 AST_Scope.DEFMETHOD("def_variable", function(symbol) {
17413 if (!this.variables.has(symbol.name)) {
17414 def = new SymbolDef(this, this.variables.size(), symbol);
17415 this.variables.set(symbol.name, def);
17416 def.global = !this.parent_scope;
17418 def = this.variables.get(symbol.name);
17419 def.orig.push(symbol);
17421 return symbol.thedef = def;
17423 AST_Scope.DEFMETHOD("next_mangled", function(options) {
17424 var ext = this.enclosed;
17425 out: while (true) {
17426 var m = base54(++this.cname);
17427 if (!is_identifier(m)) continue;
17428 for (var i = ext.length; --i >= 0; ) {
17430 var name = sym.mangled_name || sym.unmangleable(options) && sym.name;
17431 if (m == name) continue out;
17436 AST_Scope.DEFMETHOD("references", function(sym) {
17437 if (sym instanceof AST_Symbol) sym = sym.definition();
17438 return this.enclosed.indexOf(sym) < 0 ? null : sym;
17440 AST_Symbol.DEFMETHOD("unmangleable", function(options) {
17441 return this.definition().unmangleable(options);
17443 AST_SymbolAccessor.DEFMETHOD("unmangleable", function() {
17446 AST_Label.DEFMETHOD("unmangleable", function() {
17449 AST_Symbol.DEFMETHOD("unreferenced", function() {
17450 return this.definition().references.length == 0 && !(this.scope.uses_eval || this.scope.uses_with);
17452 AST_Symbol.DEFMETHOD("undeclared", function() {
17453 return this.definition().undeclared;
17455 AST_LabelRef.DEFMETHOD("undeclared", function() {
17458 AST_Label.DEFMETHOD("undeclared", function() {
17461 AST_Symbol.DEFMETHOD("definition", function() {
17462 return this.thedef;
17464 AST_Symbol.DEFMETHOD("global", function() {
17465 return this.definition().global;
17467 AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
17468 return defaults(options, {
17476 AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
17477 options = this._default_mangler_options(options);
17479 var to_mangle = [];
17480 var tw = new TreeWalker(function(node, descend) {
17481 if (node instanceof AST_LabeledStatement) {
17482 var save_nesting = lname;
17484 lname = save_nesting;
17487 if (node instanceof AST_Scope) {
17488 var p = tw.parent(), a = [];
17489 node.variables.each(function(symbol) {
17490 if (options.except.indexOf(symbol.name) < 0) {
17494 if (options.sort) a.sort(function(a, b) {
17495 return b.references.length - a.references.length;
17497 to_mangle.push.apply(to_mangle, a);
17500 if (node instanceof AST_Label) {
17502 do name = base54(++lname); while (!is_identifier(name));
17503 node.mangled_name = name;
17508 to_mangle.forEach(function(def) {
17509 def.mangle(options);
17512 AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
17513 options = this._default_mangler_options(options);
17514 var tw = new TreeWalker(function(node) {
17515 if (node instanceof AST_Constant) base54.consider(node.print_to_string()); else if (node instanceof AST_Return) base54.consider("return"); else if (node instanceof AST_Throw) base54.consider("throw"); else if (node instanceof AST_Continue) base54.consider("continue"); else if (node instanceof AST_Break) base54.consider("break"); else if (node instanceof AST_Debugger) base54.consider("debugger"); else if (node instanceof AST_Directive) base54.consider(node.value); else if (node instanceof AST_While) base54.consider("while"); else if (node instanceof AST_Do) base54.consider("do while"); else if (node instanceof AST_If) {
17516 base54.consider("if");
17517 if (node.alternative) base54.consider("else");
17518 } else if (node instanceof AST_Var) base54.consider("var"); else if (node instanceof AST_Const) base54.consider("const"); else if (node instanceof AST_Lambda) base54.consider("function"); else if (node instanceof AST_For) base54.consider("for"); else if (node instanceof AST_ForIn) base54.consider("for in"); else if (node instanceof AST_Switch) base54.consider("switch"); else if (node instanceof AST_Case) base54.consider("case"); else if (node instanceof AST_Default) base54.consider("default"); else if (node instanceof AST_With) base54.consider("with"); else if (node instanceof AST_ObjectSetter) base54.consider("set" + node.key); else if (node instanceof AST_ObjectGetter) base54.consider("get" + node.key); else if (node instanceof AST_ObjectKeyVal) base54.consider(node.key); else if (node instanceof AST_New) base54.consider("new"); else if (node instanceof AST_This) base54.consider("this"); else if (node instanceof AST_Try) base54.consider("try"); else if (node instanceof AST_Catch) base54.consider("catch"); else if (node instanceof AST_Finally) base54.consider("finally"); else if (node instanceof AST_Symbol && node.unmangleable(options)) base54.consider(node.name); else if (node instanceof AST_Unary || node instanceof AST_Binary) base54.consider(node.operator); else if (node instanceof AST_Dot) base54.consider(node.property);
17523 var base54 = function() {
17524 var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
17525 var chars, frequency;
17527 frequency = Object.create(null);
17528 chars = string.split("").map(function(ch) {
17529 return ch.charCodeAt(0);
17531 chars.forEach(function(ch) {
17535 base54.consider = function(str) {
17536 for (var i = str.length; --i >= 0; ) {
17537 var code = str.charCodeAt(i);
17538 if (code in frequency) ++frequency[code];
17541 base54.sort = function() {
17542 chars = mergeSort(chars, function(a, b) {
17543 if (is_digit(a) && !is_digit(b)) return 1;
17544 if (is_digit(b) && !is_digit(a)) return -1;
17545 return frequency[b] - frequency[a];
17548 base54.reset = reset;
17550 base54.get = function() {
17553 base54.freq = function() {
17556 function base54(num) {
17557 var ret = "", base = 54;
17559 ret += String.fromCharCode(chars[num % base]);
17560 num = Math.floor(num / base);
17567 AST_Toplevel.DEFMETHOD("scope_warnings", function(options) {
17568 options = defaults(options, {
17570 unreferenced: true,
17571 assign_to_global: true,
17572 func_arguments: true,
17573 nested_defuns: true,
17576 var tw = new TreeWalker(function(node) {
17577 if (options.undeclared && node instanceof AST_SymbolRef && node.undeclared()) {
17578 AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {
17580 file: node.start.file,
17581 line: node.start.line,
17582 col: node.start.col
17585 if (options.assign_to_global) {
17587 if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) sym = node.left; else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef) sym = node.init;
17588 if (sym && (sym.undeclared() || sym.global() && sym.scope !== sym.definition().scope)) {
17589 AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {
17590 msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
17592 file: sym.start.file,
17593 line: sym.start.line,
17598 if (options.eval && node instanceof AST_SymbolRef && node.undeclared() && node.name == "eval") {
17599 AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);
17601 if (options.unreferenced && (node instanceof AST_SymbolDeclaration || node instanceof AST_Label) && node.unreferenced()) {
17602 AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
17603 type: node instanceof AST_Label ? "Label" : "Symbol",
17605 file: node.start.file,
17606 line: node.start.line,
17607 col: node.start.col
17610 if (options.func_arguments && node instanceof AST_Lambda && node.uses_arguments) {
17611 AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {
17612 name: node.name ? node.name.name : "anonymous",
17613 file: node.start.file,
17614 line: node.start.line,
17615 col: node.start.col
17618 if (options.nested_defuns && node instanceof AST_Defun && !(tw.parent() instanceof AST_Scope)) {
17619 AST_Node.warn('Function {name} declared in nested statement "{type}" [{file}:{line},{col}]', {
17620 name: node.name.name,
17621 type: tw.parent().TYPE,
17622 file: node.start.file,
17623 line: node.start.line,
17624 col: node.start.col
17631 function OutputStream(options) {
17632 options = defaults(options, {
17638 inline_script: false,
17640 max_line_len: 32e3,
17647 preserve_line: false,
17648 negate_iife: !(options && options.beautify)
17650 var indentation = 0;
17651 var current_col = 0;
17652 var current_line = 1;
17653 var current_pos = 0;
17655 function to_ascii(str, identifier) {
17656 return str.replace(/[\u0080-\uffff]/g, function(ch) {
17657 var code = ch.charCodeAt(0).toString(16);
17658 if (code.length <= 2 && !identifier) {
17659 while (code.length < 2) code = "0" + code;
17660 return "\\x" + code;
17662 while (code.length < 4) code = "0" + code;
17663 return "\\u" + code;
17667 function make_string(str) {
17668 var dq = 0, sq = 0;
17669 str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s) {
17705 if (options.ascii_only) str = to_ascii(str);
17706 if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'"; else return '"' + str.replace(/\x22/g, '\\"') + '"';
17708 function encode_string(str) {
17709 var ret = make_string(str);
17710 if (options.inline_script) ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
17713 function make_name(name) {
17714 name = name.toString();
17715 if (options.ascii_only) name = to_ascii(name, true);
17718 function make_indent(back) {
17719 return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
17721 var might_need_space = false;
17722 var might_need_semicolon = false;
17724 function last_char() {
17725 return last.charAt(last.length - 1);
17727 function maybe_newline() {
17728 if (options.max_line_len && current_col > options.max_line_len) print("\n");
17730 var requireSemicolonChars = makePredicate("( [ + * / - , .");
17731 function print(str) {
17733 var ch = str.charAt(0);
17734 if (might_need_semicolon) {
17735 if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
17736 if (options.semicolons || requireSemicolonChars(ch)) {
17746 if (!options.beautify) might_need_space = false;
17748 might_need_semicolon = false;
17751 if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
17752 var target_line = stack[stack.length - 1].start.line;
17753 while (current_line < target_line) {
17758 might_need_space = false;
17761 if (might_need_space) {
17762 var prev = last_char();
17763 if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\") || /^[\+\-\/]$/.test(ch) && ch == prev) {
17768 might_need_space = false;
17770 var a = str.split(/\r?\n/), n = a.length - 1;
17773 current_col += a[n].length;
17775 current_col = a[n].length;
17777 current_pos += str.length;
17781 var space = options.beautify ? function() {
17784 might_need_space = true;
17786 var indent = options.beautify ? function(half) {
17787 if (options.beautify) {
17788 print(make_indent(half ? .5 : 0));
17791 var with_indent = options.beautify ? function(col, cont) {
17792 if (col === true) col = next_indent();
17793 var save_indentation = indentation;
17796 indentation = save_indentation;
17798 } : function(col, cont) {
17801 var newline = options.beautify ? function() {
17804 var semicolon = options.beautify ? function() {
17807 might_need_semicolon = true;
17809 function force_semicolon() {
17810 might_need_semicolon = false;
17813 function next_indent() {
17814 return indentation + options.indent_level;
17816 function with_block(cont) {
17820 with_indent(next_indent(), function() {
17827 function with_parens(cont) {
17833 function with_square(cont) {
17845 if (options.space_colon) space();
17847 var add_mapping = options.source_map ? function(token, name) {
17849 if (token) options.source_map.add(token.file || "?", current_line, current_col, token.line, token.col, !name && token.type == "name" ? token.value : name);
17851 AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
17855 cline: current_line,
17869 indentation: function() {
17870 return indentation;
17872 current_width: function() {
17873 return current_col - indentation;
17875 should_break: function() {
17876 return options.width && this.current_width() >= options.width;
17886 semicolon: semicolon,
17887 force_semicolon: force_semicolon,
17888 to_ascii: to_ascii,
17889 print_name: function(name) {
17890 print(make_name(name));
17892 print_string: function(str) {
17893 print(encode_string(str));
17895 next_indent: next_indent,
17896 with_indent: with_indent,
17897 with_block: with_block,
17898 with_parens: with_parens,
17899 with_square: with_square,
17900 add_mapping: add_mapping,
17901 option: function(opt) {
17902 return options[opt];
17905 return current_line;
17908 return current_col;
17911 return current_pos;
17913 push_node: function(node) {
17916 pop_node: function() {
17917 return stack.pop();
17919 stack: function() {
17922 parent: function(n) {
17923 return stack[stack.length - 2 - (n || 0)];
17928 function DEFPRINT(nodetype, generator) {
17929 nodetype.DEFMETHOD("_codegen", generator);
17931 AST_Node.DEFMETHOD("print", function(stream, force_parens) {
17932 var self = this, generator = self._codegen;
17933 stream.push_node(self);
17934 var needs_parens = self.needs_parens(stream);
17935 var fc = self instanceof AST_Function && stream.option("negate_iife");
17936 if (force_parens || needs_parens && !fc) {
17937 stream.with_parens(function() {
17938 self.add_comments(stream);
17939 self.add_source_map(stream);
17940 generator(self, stream);
17943 self.add_comments(stream);
17944 if (needs_parens && fc) stream.print("!");
17945 self.add_source_map(stream);
17946 generator(self, stream);
17950 AST_Node.DEFMETHOD("print_to_string", function(options) {
17951 var s = OutputStream(options);
17955 AST_Node.DEFMETHOD("add_comments", function(output) {
17956 var c = output.option("comments"), self = this;
17958 var start = self.start;
17959 if (start && !start._comments_dumped) {
17960 start._comments_dumped = true;
17961 var comments = start.comments_before;
17962 if (self instanceof AST_Exit && self.value && self.value.start.comments_before.length > 0) {
17963 comments = (comments || []).concat(self.value.start.comments_before);
17964 self.value.start.comments_before = [];
17967 comments = comments.filter(function(comment) {
17968 return c.test(comment.value);
17970 } else if (typeof c == "function") {
17971 comments = comments.filter(function(comment) {
17972 return c(self, comment);
17975 comments.forEach(function(c) {
17976 if (c.type == "comment1") {
17977 output.print("//" + c.value + "\n");
17979 } else if (c.type == "comment2") {
17980 output.print("/*" + c.value + "*/");
17982 output.print("\n");
17992 function PARENS(nodetype, func) {
17993 nodetype.DEFMETHOD("needs_parens", func);
17995 PARENS(AST_Node, function() {
17998 PARENS(AST_Function, function(output) {
17999 return first_in_statement(output);
18001 PARENS(AST_Object, function(output) {
18002 return first_in_statement(output);
18004 PARENS(AST_Unary, function(output) {
18005 var p = output.parent();
18006 return p instanceof AST_PropAccess && p.expression === this;
18008 PARENS(AST_Seq, function(output) {
18009 var p = output.parent();
18010 return p instanceof AST_Call || p instanceof AST_Unary || p instanceof AST_Binary || p instanceof AST_VarDef || p instanceof AST_Dot || p instanceof AST_Array || p instanceof AST_ObjectProperty || p instanceof AST_Conditional;
18012 PARENS(AST_Binary, function(output) {
18013 var p = output.parent();
18014 if (p instanceof AST_Call && p.expression === this) return true;
18015 if (p instanceof AST_Unary) return true;
18016 if (p instanceof AST_PropAccess && p.expression === this) return true;
18017 if (p instanceof AST_Binary) {
18018 var po = p.operator, pp = PRECEDENCE[po];
18019 var so = this.operator, sp = PRECEDENCE[so];
18020 if (pp > sp || pp == sp && this === p.right && !(so == po && (so == "*" || so == "&&" || so == "||"))) {
18025 PARENS(AST_PropAccess, function(output) {
18026 var p = output.parent();
18027 if (p instanceof AST_New && p.expression === this) {
18029 this.walk(new TreeWalker(function(node) {
18030 if (node instanceof AST_Call) throw p;
18033 if (ex !== p) throw ex;
18038 PARENS(AST_Call, function(output) {
18039 var p = output.parent();
18040 return p instanceof AST_New && p.expression === this;
18042 PARENS(AST_New, function(output) {
18043 var p = output.parent();
18044 if (no_constructor_parens(this, output) && (p instanceof AST_PropAccess || p instanceof AST_Call && p.expression === this)) return true;
18046 PARENS(AST_Number, function(output) {
18047 var p = output.parent();
18048 if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this) return true;
18050 PARENS(AST_NaN, function(output) {
18051 var p = output.parent();
18052 if (p instanceof AST_PropAccess && p.expression === this) return true;
18054 function assign_and_conditional_paren_rules(output) {
18055 var p = output.parent();
18056 if (p instanceof AST_Unary) return true;
18057 if (p instanceof AST_Binary && !(p instanceof AST_Assign)) return true;
18058 if (p instanceof AST_Call && p.expression === this) return true;
18059 if (p instanceof AST_Conditional && p.condition === this) return true;
18060 if (p instanceof AST_PropAccess && p.expression === this) return true;
18062 PARENS(AST_Assign, assign_and_conditional_paren_rules);
18063 PARENS(AST_Conditional, assign_and_conditional_paren_rules);
18064 DEFPRINT(AST_Directive, function(self, output) {
18065 output.print_string(self.value);
18066 output.semicolon();
18068 DEFPRINT(AST_Debugger, function(self, output) {
18069 output.print("debugger");
18070 output.semicolon();
18072 function display_body(body, is_toplevel, output) {
18073 var last = body.length - 1;
18074 body.forEach(function(stmt, i) {
18075 if (!(stmt instanceof AST_EmptyStatement)) {
18077 stmt.print(output);
18078 if (!(i == last && is_toplevel)) {
18080 if (is_toplevel) output.newline();
18085 AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output) {
18086 force_statement(this.body, output);
18088 DEFPRINT(AST_Statement, function(self, output) {
18089 self.body.print(output);
18090 output.semicolon();
18092 DEFPRINT(AST_Toplevel, function(self, output) {
18093 display_body(self.body, true, output);
18096 DEFPRINT(AST_LabeledStatement, function(self, output) {
18097 self.label.print(output);
18099 self.body.print(output);
18101 DEFPRINT(AST_SimpleStatement, function(self, output) {
18102 self.body.print(output);
18103 output.semicolon();
18105 function print_bracketed(body, output) {
18106 if (body.length > 0) output.with_block(function() {
18107 display_body(body, false, output);
18108 }); else output.print("{}");
18110 DEFPRINT(AST_BlockStatement, function(self, output) {
18111 print_bracketed(self.body, output);
18113 DEFPRINT(AST_EmptyStatement, function(self, output) {
18114 output.semicolon();
18116 DEFPRINT(AST_Do, function(self, output) {
18117 output.print("do");
18119 self._do_print_body(output);
18121 output.print("while");
18123 output.with_parens(function() {
18124 self.condition.print(output);
18126 output.semicolon();
18128 DEFPRINT(AST_While, function(self, output) {
18129 output.print("while");
18131 output.with_parens(function() {
18132 self.condition.print(output);
18135 self._do_print_body(output);
18137 DEFPRINT(AST_For, function(self, output) {
18138 output.print("for");
18140 output.with_parens(function() {
18142 if (self.init instanceof AST_Definitions) {
18143 self.init.print(output);
18145 parenthesize_for_noin(self.init, output, true);
18152 if (self.condition) {
18153 self.condition.print(output);
18160 self.step.print(output);
18164 self._do_print_body(output);
18166 DEFPRINT(AST_ForIn, function(self, output) {
18167 output.print("for");
18169 output.with_parens(function() {
18170 self.init.print(output);
18172 output.print("in");
18174 self.object.print(output);
18177 self._do_print_body(output);
18179 DEFPRINT(AST_With, function(self, output) {
18180 output.print("with");
18182 output.with_parens(function() {
18183 self.expression.print(output);
18186 self._do_print_body(output);
18188 AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword) {
18191 output.print("function");
18195 self.name.print(output);
18197 output.with_parens(function() {
18198 self.argnames.forEach(function(arg, i) {
18199 if (i) output.comma();
18204 print_bracketed(self.body, output);
18206 DEFPRINT(AST_Lambda, function(self, output) {
18207 self._do_print(output);
18209 AST_Exit.DEFMETHOD("_do_print", function(output, kind) {
18210 output.print(kind);
18213 this.value.print(output);
18215 output.semicolon();
18217 DEFPRINT(AST_Return, function(self, output) {
18218 self._do_print(output, "return");
18220 DEFPRINT(AST_Throw, function(self, output) {
18221 self._do_print(output, "throw");
18223 AST_LoopControl.DEFMETHOD("_do_print", function(output, kind) {
18224 output.print(kind);
18227 this.label.print(output);
18229 output.semicolon();
18231 DEFPRINT(AST_Break, function(self, output) {
18232 self._do_print(output, "break");
18234 DEFPRINT(AST_Continue, function(self, output) {
18235 self._do_print(output, "continue");
18237 function make_then(self, output) {
18238 if (output.option("bracketize")) {
18239 make_block(self.body, output);
18242 if (!self.body) return output.force_semicolon();
18243 if (self.body instanceof AST_Do && output.option("ie_proof")) {
18244 make_block(self.body, output);
18249 if (b instanceof AST_If) {
18250 if (!b.alternative) {
18251 make_block(self.body, output);
18255 } else if (b instanceof AST_StatementWithBody) {
18259 force_statement(self.body, output);
18261 DEFPRINT(AST_If, function(self, output) {
18262 output.print("if");
18264 output.with_parens(function() {
18265 self.condition.print(output);
18268 if (self.alternative) {
18269 make_then(self, output);
18271 output.print("else");
18273 force_statement(self.alternative, output);
18275 self._do_print_body(output);
18278 DEFPRINT(AST_Switch, function(self, output) {
18279 output.print("switch");
18281 output.with_parens(function() {
18282 self.expression.print(output);
18285 if (self.body.length > 0) output.with_block(function() {
18286 self.body.forEach(function(stmt, i) {
18287 if (i) output.newline();
18288 output.indent(true);
18289 stmt.print(output);
18291 }); else output.print("{}");
18293 AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output) {
18294 if (this.body.length > 0) {
18296 this.body.forEach(function(stmt) {
18298 stmt.print(output);
18303 DEFPRINT(AST_Default, function(self, output) {
18304 output.print("default:");
18305 self._do_print_body(output);
18307 DEFPRINT(AST_Case, function(self, output) {
18308 output.print("case");
18310 self.expression.print(output);
18312 self._do_print_body(output);
18314 DEFPRINT(AST_Try, function(self, output) {
18315 output.print("try");
18317 print_bracketed(self.body, output);
18320 self.bcatch.print(output);
18322 if (self.bfinally) {
18324 self.bfinally.print(output);
18327 DEFPRINT(AST_Catch, function(self, output) {
18328 output.print("catch");
18330 output.with_parens(function() {
18331 self.argname.print(output);
18334 print_bracketed(self.body, output);
18336 DEFPRINT(AST_Finally, function(self, output) {
18337 output.print("finally");
18339 print_bracketed(self.body, output);
18341 AST_Definitions.DEFMETHOD("_do_print", function(output, kind) {
18342 output.print(kind);
18344 this.definitions.forEach(function(def, i) {
18345 if (i) output.comma();
18348 var p = output.parent();
18349 var in_for = p instanceof AST_For || p instanceof AST_ForIn;
18350 var avoid_semicolon = in_for && p.init === this;
18351 if (!avoid_semicolon) output.semicolon();
18353 DEFPRINT(AST_Var, function(self, output) {
18354 self._do_print(output, "var");
18356 DEFPRINT(AST_Const, function(self, output) {
18357 self._do_print(output, "const");
18359 function parenthesize_for_noin(node, output, noin) {
18360 if (!noin) node.print(output); else try {
18361 node.walk(new TreeWalker(function(node) {
18362 if (node instanceof AST_Binary && node.operator == "in") throw output;
18364 node.print(output);
18366 if (ex !== output) throw ex;
18367 node.print(output, true);
18370 DEFPRINT(AST_VarDef, function(self, output) {
18371 self.name.print(output);
18376 var p = output.parent(1);
18377 var noin = p instanceof AST_For || p instanceof AST_ForIn;
18378 parenthesize_for_noin(self.value, output, noin);
18381 DEFPRINT(AST_Call, function(self, output) {
18382 self.expression.print(output);
18383 if (self instanceof AST_New && no_constructor_parens(self, output)) return;
18384 output.with_parens(function() {
18385 self.args.forEach(function(expr, i) {
18386 if (i) output.comma();
18387 expr.print(output);
18391 DEFPRINT(AST_New, function(self, output) {
18392 output.print("new");
18394 AST_Call.prototype._codegen(self, output);
18396 AST_Seq.DEFMETHOD("_do_print", function(output) {
18397 this.car.print(output);
18400 if (output.should_break()) {
18404 this.cdr.print(output);
18407 DEFPRINT(AST_Seq, function(self, output) {
18408 self._do_print(output);
18410 DEFPRINT(AST_Dot, function(self, output) {
18411 var expr = self.expression;
18412 expr.print(output);
18413 if (expr instanceof AST_Number && expr.getValue() >= 0) {
18414 if (!/[xa-f.]/i.test(output.last())) {
18419 output.add_mapping(self.end);
18420 output.print_name(self.property);
18422 DEFPRINT(AST_Sub, function(self, output) {
18423 self.expression.print(output);
18425 self.property.print(output);
18428 DEFPRINT(AST_UnaryPrefix, function(self, output) {
18429 var op = self.operator;
18431 if (/^[a-z]/i.test(op)) output.space();
18432 self.expression.print(output);
18434 DEFPRINT(AST_UnaryPostfix, function(self, output) {
18435 self.expression.print(output);
18436 output.print(self.operator);
18438 DEFPRINT(AST_Binary, function(self, output) {
18439 self.left.print(output);
18441 output.print(self.operator);
18443 self.right.print(output);
18445 DEFPRINT(AST_Conditional, function(self, output) {
18446 self.condition.print(output);
18450 self.consequent.print(output);
18453 self.alternative.print(output);
18455 DEFPRINT(AST_Array, function(self, output) {
18456 output.with_square(function() {
18457 var a = self.elements, len = a.length;
18458 if (len > 0) output.space();
18459 a.forEach(function(exp, i) {
18460 if (i) output.comma();
18463 if (len > 0) output.space();
18466 DEFPRINT(AST_Object, function(self, output) {
18467 if (self.properties.length > 0) output.with_block(function() {
18468 self.properties.forEach(function(prop, i) {
18474 prop.print(output);
18477 }); else output.print("{}");
18479 DEFPRINT(AST_ObjectKeyVal, function(self, output) {
18480 var key = self.key;
18481 if (output.option("quote_keys")) {
18482 output.print_string(key + "");
18483 } else if ((typeof key == "number" || !output.option("beautify") && +key + "" == key) && parseFloat(key) >= 0) {
18484 output.print(make_num(key));
18485 } else if (!is_identifier(key)) {
18486 output.print_string(key);
18488 output.print_name(key);
18491 self.value.print(output);
18493 DEFPRINT(AST_ObjectSetter, function(self, output) {
18494 output.print("set");
18495 self.value._do_print(output, true);
18497 DEFPRINT(AST_ObjectGetter, function(self, output) {
18498 output.print("get");
18499 self.value._do_print(output, true);
18501 DEFPRINT(AST_Symbol, function(self, output) {
18502 var def = self.definition();
18503 output.print_name(def ? def.mangled_name || def.name : self.name);
18505 DEFPRINT(AST_Undefined, function(self, output) {
18506 output.print("void 0");
18508 DEFPRINT(AST_Hole, noop);
18509 DEFPRINT(AST_Infinity, function(self, output) {
18510 output.print("1/0");
18512 DEFPRINT(AST_NaN, function(self, output) {
18513 output.print("0/0");
18515 DEFPRINT(AST_This, function(self, output) {
18516 output.print("this");
18518 DEFPRINT(AST_Constant, function(self, output) {
18519 output.print(self.getValue());
18521 DEFPRINT(AST_String, function(self, output) {
18522 output.print_string(self.getValue());
18524 DEFPRINT(AST_Number, function(self, output) {
18525 output.print(make_num(self.getValue()));
18527 DEFPRINT(AST_RegExp, function(self, output) {
18528 var str = self.getValue().toString();
18529 if (output.option("ascii_only")) str = output.to_ascii(str);
18531 var p = output.parent();
18532 if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self) output.print(" ");
18534 function force_statement(stat, output) {
18535 if (output.option("bracketize")) {
18536 if (!stat || stat instanceof AST_EmptyStatement) output.print("{}"); else if (stat instanceof AST_BlockStatement) stat.print(output); else output.with_block(function() {
18538 stat.print(output);
18542 if (!stat || stat instanceof AST_EmptyStatement) output.force_semicolon(); else stat.print(output);
18545 function first_in_statement(output) {
18546 var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
18548 if (p instanceof AST_Statement && p.body === node) return true;
18549 if (p instanceof AST_Seq && p.car === node || p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) || p instanceof AST_Dot && p.expression === node || p instanceof AST_Sub && p.expression === node || p instanceof AST_Conditional && p.condition === node || p instanceof AST_Binary && p.left === node || p instanceof AST_UnaryPostfix && p.expression === node) {
18557 function no_constructor_parens(self, output) {
18558 return self.args.length == 0 && !output.option("beautify");
18560 function best_of(a) {
18561 var best = a[0], len = best.length;
18562 for (var i = 1; i < a.length; ++i) {
18563 if (a[i].length < len) {
18570 function make_num(num) {
18571 var str = num.toString(10), a = [ str.replace(/^0\./, ".").replace("e+", "e") ], m;
18572 if (Math.floor(num) === num) {
18574 a.push("0x" + num.toString(16).toLowerCase(), "0" + num.toString(8));
18576 a.push("-0x" + (-num).toString(16).toLowerCase(), "-0" + (-num).toString(8));
18578 if (m = /^(.*?)(0+)$/.exec(num)) {
18579 a.push(m[1] + "e" + m[2].length);
18581 } else if (m = /^0?\.(0+)(.*)$/.exec(num)) {
18582 a.push(m[2] + "e-" + (m[1].length + m[2].length), str.substr(str.indexOf(".")));
18586 function make_block(stmt, output) {
18587 if (stmt instanceof AST_BlockStatement) {
18588 stmt.print(output);
18591 output.with_block(function() {
18593 stmt.print(output);
18597 function DEFMAP(nodetype, generator) {
18598 nodetype.DEFMETHOD("add_source_map", function(stream) {
18599 generator(this, stream);
18602 DEFMAP(AST_Node, noop);
18603 function basic_sourcemap_gen(self, output) {
18604 output.add_mapping(self.start);
18606 DEFMAP(AST_Directive, basic_sourcemap_gen);
18607 DEFMAP(AST_Debugger, basic_sourcemap_gen);
18608 DEFMAP(AST_Symbol, basic_sourcemap_gen);
18609 DEFMAP(AST_Jump, basic_sourcemap_gen);
18610 DEFMAP(AST_StatementWithBody, basic_sourcemap_gen);
18611 DEFMAP(AST_LabeledStatement, noop);
18612 DEFMAP(AST_Lambda, basic_sourcemap_gen);
18613 DEFMAP(AST_Switch, basic_sourcemap_gen);
18614 DEFMAP(AST_SwitchBranch, basic_sourcemap_gen);
18615 DEFMAP(AST_BlockStatement, basic_sourcemap_gen);
18616 DEFMAP(AST_Toplevel, noop);
18617 DEFMAP(AST_New, basic_sourcemap_gen);
18618 DEFMAP(AST_Try, basic_sourcemap_gen);
18619 DEFMAP(AST_Catch, basic_sourcemap_gen);
18620 DEFMAP(AST_Finally, basic_sourcemap_gen);
18621 DEFMAP(AST_Definitions, basic_sourcemap_gen);
18622 DEFMAP(AST_Constant, basic_sourcemap_gen);
18623 DEFMAP(AST_ObjectProperty, function(self, output) {
18624 output.add_mapping(self.start, self.key);
18628 function Compressor(options, false_by_default) {
18629 if (!(this instanceof Compressor)) return new Compressor(options, false_by_default);
18630 TreeTransformer.call(this, this.before, this.after);
18631 this.options = defaults(options, {
18632 sequences: !false_by_default,
18633 properties: !false_by_default,
18634 dead_code: !false_by_default,
18635 drop_debugger: !false_by_default,
18637 unsafe_comps: false,
18638 conditionals: !false_by_default,
18639 comparisons: !false_by_default,
18640 evaluate: !false_by_default,
18641 booleans: !false_by_default,
18642 loops: !false_by_default,
18643 unused: !false_by_default,
18644 hoist_funs: !false_by_default,
18646 if_return: !false_by_default,
18647 join_vars: !false_by_default,
18648 cascade: !false_by_default,
18649 side_effects: !false_by_default,
18655 Compressor.prototype = new TreeTransformer();
18656 merge(Compressor.prototype, {
18657 option: function(key) {
18658 return this.options[key];
18661 if (this.options.warnings) AST_Node.warn.apply(AST_Node, arguments);
18663 before: function(node, descend, in_list) {
18664 if (node._squeezed) return node;
18665 if (node instanceof AST_Scope) {
18666 node.drop_unused(this);
18667 node = node.hoist_declarations(this);
18669 descend(node, this);
18670 node = node.optimize(this);
18671 if (node instanceof AST_Scope) {
18672 var save_warnings = this.options.warnings;
18673 this.options.warnings = false;
18674 node.drop_unused(this);
18675 this.options.warnings = save_warnings;
18677 node._squeezed = true;
18682 function OPT(node, optimizer) {
18683 node.DEFMETHOD("optimize", function(compressor) {
18685 if (self._optimized) return self;
18686 var opt = optimizer(self, compressor);
18687 opt._optimized = true;
18688 if (opt === self) return opt;
18689 return opt.transform(compressor);
18692 OPT(AST_Node, function(self, compressor) {
18695 AST_Node.DEFMETHOD("equivalent_to", function(node) {
18696 return this.print_to_string() == node.print_to_string();
18698 function make_node(ctor, orig, props) {
18699 if (!props) props = {};
18701 if (!props.start) props.start = orig.start;
18702 if (!props.end) props.end = orig.end;
18704 return new ctor(props);
18706 function make_node_from_constant(compressor, val, orig) {
18707 if (val instanceof AST_Node) return val.transform(compressor);
18708 switch (typeof val) {
18710 return make_node(AST_String, orig, {
18712 }).optimize(compressor);
18715 return make_node(isNaN(val) ? AST_NaN : AST_Number, orig, {
18717 }).optimize(compressor);
18720 return make_node(val ? AST_True : AST_False, orig).optimize(compressor);
18723 return make_node(AST_Undefined, orig).optimize(compressor);
18726 if (val === null) {
18727 return make_node(AST_Null, orig).optimize(compressor);
18729 if (val instanceof RegExp) {
18730 return make_node(AST_RegExp, orig).optimize(compressor);
18732 throw new Error(string_template("Can't handle constant of type: {type}", {
18737 function as_statement_array(thing) {
18738 if (thing === null) return [];
18739 if (thing instanceof AST_BlockStatement) return thing.body;
18740 if (thing instanceof AST_EmptyStatement) return [];
18741 if (thing instanceof AST_Statement) return [ thing ];
18742 throw new Error("Can't convert thing to statement array");
18744 function is_empty(thing) {
18745 if (thing === null) return true;
18746 if (thing instanceof AST_EmptyStatement) return true;
18747 if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
18750 function loop_body(x) {
18751 if (x instanceof AST_Switch) return x;
18752 if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
18753 return x.body instanceof AST_BlockStatement ? x.body : x;
18757 function tighten_body(statements, compressor) {
18761 statements = eliminate_spurious_blocks(statements);
18762 if (compressor.option("dead_code")) {
18763 statements = eliminate_dead_code(statements, compressor);
18765 if (compressor.option("if_return")) {
18766 statements = handle_if_return(statements, compressor);
18768 if (compressor.option("sequences")) {
18769 statements = sequencesize(statements, compressor);
18771 if (compressor.option("join_vars")) {
18772 statements = join_consecutive_vars(statements, compressor);
18776 function eliminate_spurious_blocks(statements) {
18777 var seen_dirs = [];
18778 return statements.reduce(function(a, stat) {
18779 if (stat instanceof AST_BlockStatement) {
18781 a.push.apply(a, eliminate_spurious_blocks(stat.body));
18782 } else if (stat instanceof AST_EmptyStatement) {
18784 } else if (stat instanceof AST_Directive) {
18785 if (seen_dirs.indexOf(stat.value) < 0) {
18787 seen_dirs.push(stat.value);
18797 function handle_if_return(statements, compressor) {
18798 var self = compressor.self();
18799 var in_lambda = self instanceof AST_Lambda;
18801 loop: for (var i = statements.length; --i >= 0; ) {
18802 var stat = statements[i];
18804 case in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0:
18808 case stat instanceof AST_If:
18809 if (stat.body instanceof AST_Return) {
18810 if ((in_lambda && ret.length == 0 || ret[0] instanceof AST_Return && !ret[0].value) && !stat.body.value && !stat.alternative) {
18812 var cond = make_node(AST_SimpleStatement, stat.condition, {
18813 body: stat.condition
18818 if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) {
18820 stat = stat.clone();
18821 stat.alternative = ret[0];
18822 ret[0] = stat.transform(compressor);
18825 if ((ret.length == 0 || ret[0] instanceof AST_Return) && stat.body.value && !stat.alternative && in_lambda) {
18827 stat = stat.clone();
18828 stat.alternative = ret[0] || make_node(AST_Return, stat, {
18829 value: make_node(AST_Undefined, stat)
18831 ret[0] = stat.transform(compressor);
18834 if (!stat.body.value && in_lambda) {
18836 stat = stat.clone();
18837 stat.condition = stat.condition.negate(compressor);
18838 stat.body = make_node(AST_BlockStatement, stat, {
18839 body: as_statement_array(stat.alternative).concat(ret)
18841 stat.alternative = null;
18842 ret = [ stat.transform(compressor) ];
18845 if (ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement && (!stat.alternative || stat.alternative instanceof AST_SimpleStatement)) {
18847 ret.push(make_node(AST_Return, ret[0], {
18848 value: make_node(AST_Undefined, ret[0])
18849 }).transform(compressor));
18850 ret = as_statement_array(stat.alternative).concat(ret);
18855 var ab = aborts(stat.body);
18856 var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
18857 if (ab && (ab instanceof AST_Return && !ab.value && in_lambda || ab instanceof AST_Continue && self === loop_body(lct) || ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct)) {
18859 remove(ab.label.thedef.references, ab.label);
18862 var body = as_statement_array(stat.body).slice(0, -1);
18863 stat = stat.clone();
18864 stat.condition = stat.condition.negate(compressor);
18865 stat.body = make_node(AST_BlockStatement, stat, {
18868 stat.alternative = make_node(AST_BlockStatement, stat, {
18871 ret = [ stat.transform(compressor) ];
18874 var ab = aborts(stat.alternative);
18875 var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
18876 if (ab && (ab instanceof AST_Return && !ab.value && in_lambda || ab instanceof AST_Continue && self === loop_body(lct) || ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct)) {
18878 remove(ab.label.thedef.references, ab.label);
18881 stat = stat.clone();
18882 stat.body = make_node(AST_BlockStatement, stat.body, {
18883 body: as_statement_array(stat.body).concat(ret)
18885 stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
18886 body: as_statement_array(stat.alternative).slice(0, -1)
18888 ret = [ stat.transform(compressor) ];
18901 function eliminate_dead_code(statements, compressor) {
18902 var has_quit = false;
18903 var orig = statements.length;
18904 var self = compressor.self();
18905 statements = statements.reduce(function(a, stat) {
18907 extract_declarations_from_unreachable_code(compressor, stat, a);
18909 if (stat instanceof AST_LoopControl) {
18910 var lct = compressor.loopcontrol_target(stat.label);
18911 if (stat instanceof AST_Break && lct instanceof AST_BlockStatement && loop_body(lct) === self || stat instanceof AST_Continue && loop_body(lct) === self) {
18913 remove(stat.label.thedef.references, stat.label);
18921 if (aborts(stat)) has_quit = true;
18925 CHANGED = statements.length != orig;
18928 function sequencesize(statements, compressor) {
18929 if (statements.length < 2) return statements;
18930 var seq = [], ret = [];
18931 function push_seq() {
18932 seq = AST_Seq.from_array(seq);
18933 if (seq) ret.push(make_node(AST_SimpleStatement, seq, {
18938 statements.forEach(function(stat) {
18939 if (stat instanceof AST_SimpleStatement) seq.push(stat.body); else push_seq(), ret.push(stat);
18942 ret = sequencesize_2(ret, compressor);
18943 CHANGED = ret.length != statements.length;
18946 function sequencesize_2(statements, compressor) {
18947 function cons_seq(right) {
18949 var left = prev.body;
18950 if (left instanceof AST_Seq) {
18953 left = AST_Seq.cons(left, right);
18955 return left.transform(compressor);
18957 var ret = [], prev = null;
18958 statements.forEach(function(stat) {
18960 if (stat instanceof AST_For) {
18963 prev.body.walk(new TreeWalker(function(node) {
18964 if (node instanceof AST_Binary && node.operator == "in") throw opera;
18966 if (stat.init && !(stat.init instanceof AST_Definitions)) {
18967 stat.init = cons_seq(stat.init);
18968 } else if (!stat.init) {
18969 stat.init = prev.body;
18973 if (ex !== opera) throw ex;
18975 } else if (stat instanceof AST_If) {
18976 stat.condition = cons_seq(stat.condition);
18977 } else if (stat instanceof AST_With) {
18978 stat.expression = cons_seq(stat.expression);
18979 } else if (stat instanceof AST_Exit && stat.value) {
18980 stat.value = cons_seq(stat.value);
18981 } else if (stat instanceof AST_Exit) {
18982 stat.value = cons_seq(make_node(AST_Undefined, stat));
18983 } else if (stat instanceof AST_Switch) {
18984 stat.expression = cons_seq(stat.expression);
18988 prev = stat instanceof AST_SimpleStatement ? stat : null;
18992 function join_consecutive_vars(statements, compressor) {
18994 return statements.reduce(function(a, stat) {
18995 if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {
18996 prev.definitions = prev.definitions.concat(stat.definitions);
18998 } else if (stat instanceof AST_For && prev instanceof AST_Definitions && (!stat.init || stat.init.TYPE == prev.TYPE)) {
19002 stat.init.definitions = prev.definitions.concat(stat.init.definitions);
19016 function extract_declarations_from_unreachable_code(compressor, stat, target) {
19017 compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
19018 stat.walk(new TreeWalker(function(node) {
19019 if (node instanceof AST_Definitions) {
19020 compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
19021 node.remove_initializers();
19025 if (node instanceof AST_Defun) {
19029 if (node instanceof AST_Scope) {
19035 var unary_bool = [ "!", "delete" ];
19036 var binary_bool = [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ];
19037 def(AST_Node, function() {
19040 def(AST_UnaryPrefix, function() {
19041 return member(this.operator, unary_bool);
19043 def(AST_Binary, function() {
19044 return member(this.operator, binary_bool) || (this.operator == "&&" || this.operator == "||") && this.left.is_boolean() && this.right.is_boolean();
19046 def(AST_Conditional, function() {
19047 return this.consequent.is_boolean() && this.alternative.is_boolean();
19049 def(AST_Assign, function() {
19050 return this.operator == "=" && this.right.is_boolean();
19052 def(AST_Seq, function() {
19053 return this.cdr.is_boolean();
19055 def(AST_True, function() {
19058 def(AST_False, function() {
19061 })(function(node, func) {
19062 node.DEFMETHOD("is_boolean", func);
19065 def(AST_Node, function() {
19068 def(AST_String, function() {
19071 def(AST_UnaryPrefix, function() {
19072 return this.operator == "typeof";
19074 def(AST_Binary, function(compressor) {
19075 return this.operator == "+" && (this.left.is_string(compressor) || this.right.is_string(compressor));
19077 def(AST_Assign, function(compressor) {
19078 return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
19080 def(AST_Seq, function(compressor) {
19081 return this.cdr.is_string(compressor);
19083 def(AST_Conditional, function(compressor) {
19084 return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
19086 def(AST_Call, function(compressor) {
19087 return compressor.option("unsafe") && this.expression instanceof AST_SymbolRef && this.expression.name == "String" && this.expression.undeclared();
19089 })(function(node, func) {
19090 node.DEFMETHOD("is_string", func);
19092 function best_of(ast1, ast2) {
19093 return ast1.print_to_string().length > ast2.print_to_string().length ? ast2 : ast1;
19096 AST_Node.DEFMETHOD("evaluate", function(compressor) {
19097 if (!compressor.option("evaluate")) return [ this ];
19099 var val = this._eval(), ast = make_node_from_constant(compressor, val, this);
19100 return [ best_of(ast, this), val ];
19102 if (ex !== def) throw ex;
19106 def(AST_Statement, function() {
19107 throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
19109 def(AST_Function, function() {
19112 function ev(node) {
19113 return node._eval();
19115 def(AST_Node, function() {
19118 def(AST_Constant, function() {
19119 return this.getValue();
19121 def(AST_UnaryPrefix, function() {
19122 var e = this.expression;
19123 switch (this.operator) {
19128 if (e instanceof AST_Function) return typeof function() {};
19130 if (e instanceof RegExp) throw def;
19141 if (e === 0) throw def;
19149 def(AST_Binary, function() {
19150 var left = this.left, right = this.right;
19151 switch (this.operator) {
19153 return ev(left) && ev(right);
19156 return ev(left) || ev(right);
19159 return ev(left) | ev(right);
19162 return ev(left) & ev(right);
19165 return ev(left) ^ ev(right);
19168 return ev(left) + ev(right);
19171 return ev(left) * ev(right);
19174 return ev(left) / ev(right);
19177 return ev(left) % ev(right);
19180 return ev(left) - ev(right);
19183 return ev(left) << ev(right);
19186 return ev(left) >> ev(right);
19189 return ev(left) >>> ev(right);
19192 return ev(left) == ev(right);
19195 return ev(left) === ev(right);
19198 return ev(left) != ev(right);
19201 return ev(left) !== ev(right);
19204 return ev(left) < ev(right);
19207 return ev(left) <= ev(right);
19210 return ev(left) > ev(right);
19213 return ev(left) >= ev(right);
19216 return ev(left) in ev(right);
19219 return ev(left) instanceof ev(right);
19223 def(AST_Conditional, function() {
19224 return ev(this.condition) ? ev(this.consequent) : ev(this.alternative);
19226 def(AST_SymbolRef, function() {
19227 var d = this.definition();
19228 if (d && d.constant && d.init) return ev(d.init);
19231 })(function(node, func) {
19232 node.DEFMETHOD("_eval", func);
19235 function basic_negation(exp) {
19236 return make_node(AST_UnaryPrefix, exp, {
19241 def(AST_Node, function() {
19242 return basic_negation(this);
19244 def(AST_Statement, function() {
19245 throw new Error("Cannot negate a statement");
19247 def(AST_Function, function() {
19248 return basic_negation(this);
19250 def(AST_UnaryPrefix, function() {
19251 if (this.operator == "!") return this.expression;
19252 return basic_negation(this);
19254 def(AST_Seq, function(compressor) {
19255 var self = this.clone();
19256 self.cdr = self.cdr.negate(compressor);
19259 def(AST_Conditional, function(compressor) {
19260 var self = this.clone();
19261 self.consequent = self.consequent.negate(compressor);
19262 self.alternative = self.alternative.negate(compressor);
19263 return best_of(basic_negation(this), self);
19265 def(AST_Binary, function(compressor) {
19266 var self = this.clone(), op = this.operator;
19267 if (compressor.option("unsafe_comps")) {
19270 self.operator = ">";
19274 self.operator = ">=";
19278 self.operator = "<";
19282 self.operator = "<=";
19288 self.operator = "!=";
19292 self.operator = "==";
19296 self.operator = "!==";
19300 self.operator = "===";
19304 self.operator = "||";
19305 self.left = self.left.negate(compressor);
19306 self.right = self.right.negate(compressor);
19307 return best_of(basic_negation(this), self);
19310 self.operator = "&&";
19311 self.left = self.left.negate(compressor);
19312 self.right = self.right.negate(compressor);
19313 return best_of(basic_negation(this), self);
19315 return basic_negation(this);
19317 })(function(node, func) {
19318 node.DEFMETHOD("negate", function(compressor) {
19319 return func.call(this, compressor);
19323 def(AST_Node, function() {
19326 def(AST_EmptyStatement, function() {
19329 def(AST_Constant, function() {
19332 def(AST_This, function() {
19335 def(AST_Block, function() {
19336 for (var i = this.body.length; --i >= 0; ) {
19337 if (this.body[i].has_side_effects()) return true;
19341 def(AST_SimpleStatement, function() {
19342 return this.body.has_side_effects();
19344 def(AST_Defun, function() {
19347 def(AST_Function, function() {
19350 def(AST_Binary, function() {
19351 return this.left.has_side_effects() || this.right.has_side_effects();
19353 def(AST_Assign, function() {
19356 def(AST_Conditional, function() {
19357 return this.condition.has_side_effects() || this.consequent.has_side_effects() || this.alternative.has_side_effects();
19359 def(AST_Unary, function() {
19360 return this.operator == "delete" || this.operator == "++" || this.operator == "--" || this.expression.has_side_effects();
19362 def(AST_SymbolRef, function() {
19365 def(AST_Object, function() {
19366 for (var i = this.properties.length; --i >= 0; ) if (this.properties[i].has_side_effects()) return true;
19369 def(AST_ObjectProperty, function() {
19370 return this.value.has_side_effects();
19372 def(AST_Array, function() {
19373 for (var i = this.elements.length; --i >= 0; ) if (this.elements[i].has_side_effects()) return true;
19376 def(AST_PropAccess, function() {
19379 def(AST_Seq, function() {
19380 return this.car.has_side_effects() || this.cdr.has_side_effects();
19382 })(function(node, func) {
19383 node.DEFMETHOD("has_side_effects", func);
19385 function aborts(thing) {
19386 return thing && thing.aborts();
19389 def(AST_Statement, function() {
19392 def(AST_Jump, function() {
19395 function block_aborts() {
19396 var n = this.body.length;
19397 return n > 0 && aborts(this.body[n - 1]);
19399 def(AST_BlockStatement, block_aborts);
19400 def(AST_SwitchBranch, block_aborts);
19401 def(AST_If, function() {
19402 return this.alternative && aborts(this.body) && aborts(this.alternative);
19404 })(function(node, func) {
19405 node.DEFMETHOD("aborts", func);
19407 OPT(AST_Directive, function(self, compressor) {
19408 if (self.scope.has_directive(self.value) !== self.scope) {
19409 return make_node(AST_EmptyStatement, self);
19413 OPT(AST_Debugger, function(self, compressor) {
19414 if (compressor.option("drop_debugger")) return make_node(AST_EmptyStatement, self);
19417 OPT(AST_LabeledStatement, function(self, compressor) {
19418 if (self.body instanceof AST_Break && compressor.loopcontrol_target(self.body.label) === self.body) {
19419 return make_node(AST_EmptyStatement, self);
19421 return self.label.references.length == 0 ? self.body : self;
19423 OPT(AST_Block, function(self, compressor) {
19424 self.body = tighten_body(self.body, compressor);
19427 OPT(AST_BlockStatement, function(self, compressor) {
19428 self.body = tighten_body(self.body, compressor);
19429 switch (self.body.length) {
19431 return self.body[0];
19434 return make_node(AST_EmptyStatement, self);
19438 AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
19440 if (compressor.option("unused") && !(self instanceof AST_Toplevel) && !self.uses_eval) {
19442 var initializations = new Dictionary();
19444 var tw = new TreeWalker(function(node, descend) {
19445 if (node !== self) {
19446 if (node instanceof AST_Defun) {
19447 initializations.add(node.name.name, node);
19450 if (node instanceof AST_Definitions && scope === self) {
19451 node.definitions.forEach(function(def) {
19453 initializations.add(def.name.name, def.value);
19454 if (def.value.has_side_effects()) {
19455 def.value.walk(tw);
19461 if (node instanceof AST_SymbolRef) {
19462 push_uniq(in_use, node.definition());
19465 if (node instanceof AST_Scope) {
19466 var save_scope = scope;
19469 scope = save_scope;
19475 for (var i = 0; i < in_use.length; ++i) {
19476 in_use[i].orig.forEach(function(decl) {
19477 var init = initializations.get(decl.name);
19478 if (init) init.forEach(function(init) {
19479 var tw = new TreeWalker(function(node) {
19480 if (node instanceof AST_SymbolRef) {
19481 push_uniq(in_use, node.definition());
19488 var tt = new TreeTransformer(function before(node, descend, in_list) {
19489 if (node instanceof AST_Lambda) {
19490 for (var a = node.argnames, i = a.length; --i >= 0; ) {
19492 if (sym.unreferenced()) {
19494 compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
19496 file: sym.start.file,
19497 line: sym.start.line,
19503 if (node instanceof AST_Defun && node !== self) {
19504 if (!member(node.name.definition(), in_use)) {
19505 compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
19506 name: node.name.name,
19507 file: node.name.start.file,
19508 line: node.name.start.line,
19509 col: node.name.start.col
19511 return make_node(AST_EmptyStatement, node);
19515 if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
19516 var def = node.definitions.filter(function(def) {
19517 if (member(def.name.definition(), in_use)) return true;
19519 name: def.name.name,
19520 file: def.name.start.file,
19521 line: def.name.start.line,
19522 col: def.name.start.col
19524 if (def.value && def.value.has_side_effects()) {
19525 def._unused_side_effects = true;
19526 compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
19529 compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
19532 def = mergeSort(def, function(a, b) {
19533 if (!a.value && b.value) return -1;
19534 if (!b.value && a.value) return 1;
19537 var side_effects = [];
19538 for (var i = 0; i < def.length; ) {
19540 if (x._unused_side_effects) {
19541 side_effects.push(x.value);
19544 if (side_effects.length > 0) {
19545 side_effects.push(x.value);
19546 x.value = AST_Seq.from_array(side_effects);
19552 if (side_effects.length > 0) {
19553 side_effects = make_node(AST_BlockStatement, node, {
19554 body: [ make_node(AST_SimpleStatement, node, {
19555 body: AST_Seq.from_array(side_effects)
19559 side_effects = null;
19561 if (def.length == 0 && !side_effects) {
19562 return make_node(AST_EmptyStatement, node);
19564 if (def.length == 0) {
19565 return side_effects;
19567 node.definitions = def;
19568 if (side_effects) {
19569 side_effects.body.unshift(node);
19570 node = side_effects;
19574 if (node instanceof AST_For && node.init instanceof AST_BlockStatement) {
19575 descend(node, this);
19576 var body = node.init.body.slice(0, -1);
19577 node.init = node.init.body.slice(-1)[0].body;
19579 return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
19583 if (node instanceof AST_Scope && node !== self) return node;
19585 self.transform(tt);
19588 AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
19589 var hoist_funs = compressor.option("hoist_funs");
19590 var hoist_vars = compressor.option("hoist_vars");
19592 if (hoist_funs || hoist_vars) {
19595 var vars = new Dictionary(), vars_found = 0, var_decl = 0;
19596 self.walk(new TreeWalker(function(node) {
19597 if (node instanceof AST_Scope && node !== self) return true;
19598 if (node instanceof AST_Var) {
19603 hoist_vars = hoist_vars && var_decl > 1;
19604 var tt = new TreeTransformer(function before(node) {
19605 if (node !== self) {
19606 if (node instanceof AST_Directive) {
19608 return make_node(AST_EmptyStatement, node);
19610 if (node instanceof AST_Defun && hoist_funs) {
19611 hoisted.push(node);
19612 return make_node(AST_EmptyStatement, node);
19614 if (node instanceof AST_Var && hoist_vars) {
19615 node.definitions.forEach(function(def) {
19616 vars.set(def.name.name, def);
19619 var seq = node.to_assignments();
19620 var p = tt.parent();
19621 if (p instanceof AST_ForIn && p.init === node) {
19622 if (seq == null) return node.definitions[0].name;
19625 if (p instanceof AST_For && p.init === node) {
19628 if (!seq) return make_node(AST_EmptyStatement, node);
19629 return make_node(AST_SimpleStatement, node, {
19633 if (node instanceof AST_Scope) return node;
19636 self = self.transform(tt);
19637 if (vars_found > 0) {
19639 vars.each(function(def, name) {
19640 if (self instanceof AST_Lambda && find_if(function(x) {
19641 return x.name == def.name.name;
19642 }, self.argnames)) {
19648 vars.set(name, def);
19651 if (defs.length > 0) {
19652 for (var i = 0; i < self.body.length; ) {
19653 if (self.body[i] instanceof AST_SimpleStatement) {
19654 var expr = self.body[i].body, sym, assign;
19655 if (expr instanceof AST_Assign && expr.operator == "=" && (sym = expr.left) instanceof AST_Symbol && vars.has(sym.name)) {
19656 var def = vars.get(sym.name);
19657 if (def.value) break;
19658 def.value = expr.right;
19661 self.body.splice(i, 1);
19664 if (expr instanceof AST_Seq && (assign = expr.car) instanceof AST_Assign && assign.operator == "=" && (sym = assign.left) instanceof AST_Symbol && vars.has(sym.name)) {
19665 var def = vars.get(sym.name);
19666 if (def.value) break;
19667 def.value = assign.right;
19670 self.body[i].body = expr.cdr;
19674 if (self.body[i] instanceof AST_EmptyStatement) {
19675 self.body.splice(i, 1);
19678 if (self.body[i] instanceof AST_BlockStatement) {
19679 var tmp = [ i, 1 ].concat(self.body[i].body);
19680 self.body.splice.apply(self.body, tmp);
19685 defs = make_node(AST_Var, self, {
19688 hoisted.push(defs);
19691 self.body = dirs.concat(hoisted, self.body);
19695 OPT(AST_SimpleStatement, function(self, compressor) {
19696 if (compressor.option("side_effects")) {
19697 if (!self.body.has_side_effects()) {
19698 compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
19699 return make_node(AST_EmptyStatement, self);
19704 OPT(AST_DWLoop, function(self, compressor) {
19705 var cond = self.condition.evaluate(compressor);
19706 self.condition = cond[0];
19707 if (!compressor.option("loops")) return self;
19708 if (cond.length > 1) {
19710 return make_node(AST_For, self, {
19713 } else if (self instanceof AST_While) {
19714 if (compressor.option("dead_code")) {
19716 extract_declarations_from_unreachable_code(compressor, self.body, a);
19717 return make_node(AST_BlockStatement, self, {
19725 function if_break_in_loop(self, compressor) {
19726 function drop_it(rest) {
19727 rest = as_statement_array(rest);
19728 if (self.body instanceof AST_BlockStatement) {
19729 self.body = self.body.clone();
19730 self.body.body = rest.concat(self.body.body.slice(1));
19731 self.body = self.body.transform(compressor);
19733 self.body = make_node(AST_BlockStatement, self.body, {
19735 }).transform(compressor);
19737 if_break_in_loop(self, compressor);
19739 var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
19740 if (first instanceof AST_If) {
19741 if (first.body instanceof AST_Break && compressor.loopcontrol_target(first.body.label) === self) {
19742 if (self.condition) {
19743 self.condition = make_node(AST_Binary, self.condition, {
19744 left: self.condition,
19746 right: first.condition.negate(compressor)
19749 self.condition = first.condition.negate(compressor);
19751 drop_it(first.alternative);
19752 } else if (first.alternative instanceof AST_Break && compressor.loopcontrol_target(first.alternative.label) === self) {
19753 if (self.condition) {
19754 self.condition = make_node(AST_Binary, self.condition, {
19755 left: self.condition,
19757 right: first.condition
19760 self.condition = first.condition;
19762 drop_it(first.body);
19766 OPT(AST_While, function(self, compressor) {
19767 if (!compressor.option("loops")) return self;
19768 self = AST_DWLoop.prototype.optimize.call(self, compressor);
19769 if (self instanceof AST_While) {
19770 if_break_in_loop(self, compressor);
19771 self = make_node(AST_For, self, self).transform(compressor);
19775 OPT(AST_For, function(self, compressor) {
19776 var cond = self.condition;
19778 cond = cond.evaluate(compressor);
19779 self.condition = cond[0];
19781 if (!compressor.option("loops")) return self;
19783 if (cond.length > 1 && !cond[1]) {
19784 if (compressor.option("dead_code")) {
19786 if (self.init instanceof AST_Statement) {
19788 } else if (self.init) {
19789 a.push(make_node(AST_SimpleStatement, self.init, {
19793 extract_declarations_from_unreachable_code(compressor, self.body, a);
19794 return make_node(AST_BlockStatement, self, {
19800 if_break_in_loop(self, compressor);
19803 OPT(AST_If, function(self, compressor) {
19804 if (!compressor.option("conditionals")) return self;
19805 var cond = self.condition.evaluate(compressor);
19806 self.condition = cond[0];
19807 if (cond.length > 1) {
19809 compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
19810 if (compressor.option("dead_code")) {
19812 if (self.alternative) {
19813 extract_declarations_from_unreachable_code(compressor, self.alternative, a);
19816 return make_node(AST_BlockStatement, self, {
19818 }).transform(compressor);
19821 compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
19822 if (compressor.option("dead_code")) {
19824 extract_declarations_from_unreachable_code(compressor, self.body, a);
19825 if (self.alternative) a.push(self.alternative);
19826 return make_node(AST_BlockStatement, self, {
19828 }).transform(compressor);
19832 if (is_empty(self.alternative)) self.alternative = null;
19833 var negated = self.condition.negate(compressor);
19834 var negated_is_best = best_of(self.condition, negated) === negated;
19835 if (self.alternative && negated_is_best) {
19836 negated_is_best = false;
19837 self.condition = negated;
19838 var tmp = self.body;
19839 self.body = self.alternative || make_node(AST_EmptyStatement);
19840 self.alternative = tmp;
19842 if (is_empty(self.body) && is_empty(self.alternative)) {
19843 return make_node(AST_SimpleStatement, self.condition, {
19844 body: self.condition
19845 }).transform(compressor);
19847 if (self.body instanceof AST_SimpleStatement && self.alternative instanceof AST_SimpleStatement) {
19848 return make_node(AST_SimpleStatement, self, {
19849 body: make_node(AST_Conditional, self, {
19850 condition: self.condition,
19851 consequent: self.body.body,
19852 alternative: self.alternative.body
19854 }).transform(compressor);
19856 if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
19857 if (negated_is_best) return make_node(AST_SimpleStatement, self, {
19858 body: make_node(AST_Binary, self, {
19861 right: self.body.body
19863 }).transform(compressor);
19864 return make_node(AST_SimpleStatement, self, {
19865 body: make_node(AST_Binary, self, {
19867 left: self.condition,
19868 right: self.body.body
19870 }).transform(compressor);
19872 if (self.body instanceof AST_EmptyStatement && self.alternative && self.alternative instanceof AST_SimpleStatement) {
19873 return make_node(AST_SimpleStatement, self, {
19874 body: make_node(AST_Binary, self, {
19876 left: self.condition,
19877 right: self.alternative.body
19879 }).transform(compressor);
19881 if (self.body instanceof AST_Exit && self.alternative instanceof AST_Exit && self.body.TYPE == self.alternative.TYPE) {
19882 return make_node(self.body.CTOR, self, {
19883 value: make_node(AST_Conditional, self, {
19884 condition: self.condition,
19885 consequent: self.body.value || make_node(AST_Undefined, self.body).optimize(compressor),
19886 alternative: self.alternative.value || make_node(AST_Undefined, self.alternative).optimize(compressor)
19888 }).transform(compressor);
19890 if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) {
19891 self.condition = make_node(AST_Binary, self.condition, {
19893 left: self.condition,
19894 right: self.body.condition
19895 }).transform(compressor);
19896 self.body = self.body.body;
19898 if (aborts(self.body)) {
19899 if (self.alternative) {
19900 var alt = self.alternative;
19901 self.alternative = null;
19902 return make_node(AST_BlockStatement, self, {
19903 body: [ self, alt ]
19904 }).transform(compressor);
19907 if (aborts(self.alternative)) {
19908 var body = self.body;
19909 self.body = self.alternative;
19910 self.condition = negated_is_best ? negated : self.condition.negate(compressor);
19911 self.alternative = null;
19912 return make_node(AST_BlockStatement, self, {
19913 body: [ self, body ]
19914 }).transform(compressor);
19918 OPT(AST_Switch, function(self, compressor) {
19919 if (self.body.length == 0 && compressor.option("conditionals")) {
19920 return make_node(AST_SimpleStatement, self, {
19921 body: self.expression
19922 }).transform(compressor);
19925 var last_branch = self.body[self.body.length - 1];
19927 var stat = last_branch.body[last_branch.body.length - 1];
19928 if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self) last_branch.body.pop();
19929 if (last_branch instanceof AST_Default && last_branch.body.length == 0) {
19936 var exp = self.expression.evaluate(compressor);
19937 out: if (exp.length == 2) try {
19938 self.expression = exp[0];
19939 if (!compressor.option("dead_code")) break out;
19940 var value = exp[1];
19942 var in_block = false;
19943 var started = false;
19944 var stopped = false;
19945 var ruined = false;
19946 var tt = new TreeTransformer(function(node, descend, in_list) {
19947 if (node instanceof AST_Lambda || node instanceof AST_SimpleStatement) {
19949 } else if (node instanceof AST_Switch && node === self) {
19950 node = node.clone();
19951 descend(node, this);
19952 return ruined ? node : make_node(AST_BlockStatement, node, {
19953 body: node.body.reduce(function(a, branch) {
19954 return a.concat(branch.body);
19956 }).transform(compressor);
19957 } else if (node instanceof AST_If || node instanceof AST_Try) {
19960 descend(node, this);
19963 } else if (node instanceof AST_StatementWithBody || node instanceof AST_Switch) {
19964 var save = in_block;
19966 descend(node, this);
19969 } else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) {
19974 if (in_block) return node;
19976 return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
19977 } else if (node instanceof AST_SwitchBranch && this.parent() === self) {
19978 if (stopped) return MAP.skip;
19979 if (node instanceof AST_Case) {
19980 var exp = node.expression.evaluate(compressor);
19981 if (exp.length < 2) {
19984 if (exp[1] === value || started) {
19986 if (aborts(node)) stopped = true;
19987 descend(node, this);
19992 descend(node, this);
19996 tt.stack = compressor.stack.slice();
19997 self = self.transform(tt);
19999 if (ex !== self) throw ex;
20003 OPT(AST_Case, function(self, compressor) {
20004 self.body = tighten_body(self.body, compressor);
20007 OPT(AST_Try, function(self, compressor) {
20008 self.body = tighten_body(self.body, compressor);
20011 AST_Definitions.DEFMETHOD("remove_initializers", function() {
20012 this.definitions.forEach(function(def) {
20016 AST_Definitions.DEFMETHOD("to_assignments", function() {
20017 var assignments = this.definitions.reduce(function(a, def) {
20019 var name = make_node(AST_SymbolRef, def.name, def.name);
20020 a.push(make_node(AST_Assign, def, {
20028 if (assignments.length == 0) return null;
20029 return AST_Seq.from_array(assignments);
20031 OPT(AST_Definitions, function(self, compressor) {
20032 if (self.definitions.length == 0) return make_node(AST_EmptyStatement, self);
20035 OPT(AST_Function, function(self, compressor) {
20036 self = AST_Lambda.prototype.optimize.call(self, compressor);
20037 if (compressor.option("unused")) {
20038 if (self.name && self.name.unreferenced()) {
20044 OPT(AST_Call, function(self, compressor) {
20045 if (compressor.option("unsafe")) {
20046 var exp = self.expression;
20047 if (exp instanceof AST_SymbolRef && exp.undeclared()) {
20048 switch (exp.name) {
20050 if (self.args.length != 1) {
20051 return make_node(AST_Array, self, {
20052 elements: self.args
20058 if (self.args.length == 0) {
20059 return make_node(AST_Object, self, {
20066 if (self.args.length == 0) return make_node(AST_String, self, {
20069 return make_node(AST_Binary, self, {
20070 left: self.args[0],
20072 right: make_node(AST_String, self, {
20078 if (all(self.args, function(x) {
20079 return x instanceof AST_String;
20082 var code = "(function(" + self.args.slice(0, -1).map(function(arg) {
20084 }).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
20085 var ast = parse(code);
20086 ast.figure_out_scope();
20087 var comp = new Compressor(compressor.options);
20088 ast = ast.transform(comp);
20089 ast.figure_out_scope();
20090 ast.mangle_names();
20091 var fun = ast.body[0].body.expression;
20092 var args = fun.argnames.map(function(arg, i) {
20093 return make_node(AST_String, self.args[i], {
20094 value: arg.print_to_string()
20097 var code = OutputStream();
20098 AST_BlockStatement.prototype._codegen.call(fun, fun, code);
20099 code = code.toString().replace(/^\{|\}$/g, "");
20100 args.push(make_node(AST_String, self.args[self.args.length - 1], {
20106 if (ex instanceof JS_Parse_Error) {
20107 compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
20108 compressor.warn(ex.toString());
20116 } else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
20117 return make_node(AST_Binary, self, {
20118 left: make_node(AST_String, self, {
20122 right: exp.expression
20123 }).transform(compressor);
20126 if (compressor.option("side_effects")) {
20127 if (self.expression instanceof AST_Function && self.args.length == 0 && !AST_Block.prototype.has_side_effects.call(self.expression)) {
20128 return make_node(AST_Undefined, self).transform(compressor);
20133 OPT(AST_New, function(self, compressor) {
20134 if (compressor.option("unsafe")) {
20135 var exp = self.expression;
20136 if (exp instanceof AST_SymbolRef && exp.undeclared()) {
20137 switch (exp.name) {
20143 return make_node(AST_Call, self, self).transform(compressor);
20149 OPT(AST_Seq, function(self, compressor) {
20150 if (!compressor.option("side_effects")) return self;
20151 if (!self.car.has_side_effects()) {
20153 if (!(self.cdr instanceof AST_SymbolRef && self.cdr.name == "eval" && self.cdr.undeclared() && (p = compressor.parent()) instanceof AST_Call && p.expression === self)) {
20157 if (compressor.option("cascade")) {
20158 if (self.car instanceof AST_Assign && !self.car.left.has_side_effects() && self.car.left.equivalent_to(self.cdr)) {
20161 if (!self.car.has_side_effects() && !self.cdr.has_side_effects() && self.car.equivalent_to(self.cdr)) {
20167 AST_Unary.DEFMETHOD("lift_sequences", function(compressor) {
20168 if (compressor.option("sequences")) {
20169 if (this.expression instanceof AST_Seq) {
20170 var seq = this.expression;
20171 var x = seq.to_array();
20172 this.expression = x.pop();
20174 seq = AST_Seq.from_array(x).transform(compressor);
20180 OPT(AST_UnaryPostfix, function(self, compressor) {
20181 return self.lift_sequences(compressor);
20183 OPT(AST_UnaryPrefix, function(self, compressor) {
20184 self = self.lift_sequences(compressor);
20185 var e = self.expression;
20186 if (compressor.option("booleans") && compressor.in_boolean_context()) {
20187 switch (self.operator) {
20189 if (e instanceof AST_UnaryPrefix && e.operator == "!") {
20190 return e.expression;
20195 compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
20196 return make_node(AST_True, self);
20198 if (e instanceof AST_Binary && self.operator == "!") {
20199 self = best_of(self, e.negate(compressor));
20202 return self.evaluate(compressor)[0];
20204 AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
20205 if (compressor.option("sequences")) {
20206 if (this.left instanceof AST_Seq) {
20207 var seq = this.left;
20208 var x = seq.to_array();
20209 this.left = x.pop();
20211 seq = AST_Seq.from_array(x).transform(compressor);
20214 if (this.right instanceof AST_Seq && !(this.operator == "||" || this.operator == "&&") && !this.left.has_side_effects()) {
20215 var seq = this.right;
20216 var x = seq.to_array();
20217 this.right = x.pop();
20219 seq = AST_Seq.from_array(x).transform(compressor);
20225 var commutativeOperators = makePredicate("== === != !== * & | ^");
20226 OPT(AST_Binary, function(self, compressor) {
20227 function reverse(op, force) {
20228 if (force || !(self.left.has_side_effects() || self.right.has_side_effects())) {
20229 if (op) self.operator = op;
20230 var tmp = self.left;
20231 self.left = self.right;
20235 if (commutativeOperators(self.operator)) {
20236 if (self.right instanceof AST_Constant && !(self.left instanceof AST_Constant)) {
20237 reverse(null, true);
20240 self = self.lift_sequences(compressor);
20241 if (compressor.option("comparisons")) switch (self.operator) {
20244 if (self.left.is_string(compressor) && self.right.is_string(compressor) || self.left.is_boolean() && self.right.is_boolean()) {
20245 self.operator = self.operator.substr(0, 2);
20250 if (self.left instanceof AST_String && self.left.value == "undefined" && self.right instanceof AST_UnaryPrefix && self.right.operator == "typeof" && compressor.option("unsafe")) {
20251 if (!(self.right.expression instanceof AST_SymbolRef) || !self.right.expression.undeclared()) {
20252 self.right = self.right.expression;
20253 self.left = make_node(AST_Undefined, self.left).optimize(compressor);
20254 if (self.operator.length == 2) self.operator += "=";
20259 if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
20261 var ll = self.left.evaluate(compressor);
20262 var rr = self.right.evaluate(compressor);
20263 if (ll.length > 1 && !ll[1] || rr.length > 1 && !rr[1]) {
20264 compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
20265 return make_node(AST_False, self);
20267 if (ll.length > 1 && ll[1]) {
20270 if (rr.length > 1 && rr[1]) {
20276 var ll = self.left.evaluate(compressor);
20277 var rr = self.right.evaluate(compressor);
20278 if (ll.length > 1 && ll[1] || rr.length > 1 && rr[1]) {
20279 compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
20280 return make_node(AST_True, self);
20282 if (ll.length > 1 && !ll[1]) {
20285 if (rr.length > 1 && !rr[1]) {
20291 var ll = self.left.evaluate(compressor);
20292 var rr = self.right.evaluate(compressor);
20293 if (ll.length > 1 && ll[0] instanceof AST_String && ll[1] || rr.length > 1 && rr[0] instanceof AST_String && rr[1]) {
20294 compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
20295 return make_node(AST_True, self);
20299 var exp = self.evaluate(compressor);
20300 if (exp.length > 1) {
20301 if (best_of(exp[0], self) !== self) return exp[0];
20303 if (compressor.option("comparisons")) {
20304 if (!(compressor.parent() instanceof AST_Binary) || compressor.parent() instanceof AST_Assign) {
20305 var negated = make_node(AST_UnaryPrefix, self, {
20307 expression: self.negate(compressor)
20309 self = best_of(self, negated);
20311 switch (self.operator) {
20321 if (self.operator == "+" && self.right instanceof AST_String && self.right.getValue() === "" && self.left instanceof AST_Binary && self.left.operator == "+" && self.left.is_string(compressor)) {
20326 OPT(AST_SymbolRef, function(self, compressor) {
20327 if (self.undeclared()) {
20328 var defines = compressor.option("global_defs");
20329 if (defines && defines.hasOwnProperty(self.name)) {
20330 return make_node_from_constant(compressor, defines[self.name], self);
20332 switch (self.name) {
20334 return make_node(AST_Undefined, self);
20337 return make_node(AST_NaN, self);
20340 return make_node(AST_Infinity, self);
20345 OPT(AST_Undefined, function(self, compressor) {
20346 if (compressor.option("unsafe")) {
20347 var scope = compressor.find_parent(AST_Scope);
20348 var undef = scope.find_variable("undefined");
20350 var ref = make_node(AST_SymbolRef, self, {
20361 var ASSIGN_OPS = [ "+", "-", "/", "*", "%", ">>", "<<", ">>>", "|", "^", "&" ];
20362 OPT(AST_Assign, function(self, compressor) {
20363 self = self.lift_sequences(compressor);
20364 if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary && self.right.left instanceof AST_SymbolRef && self.right.left.name == self.left.name && member(self.right.operator, ASSIGN_OPS)) {
20365 self.operator = self.right.operator + "=";
20366 self.right = self.right.right;
20370 OPT(AST_Conditional, function(self, compressor) {
20371 if (!compressor.option("conditionals")) return self;
20372 if (self.condition instanceof AST_Seq) {
20373 var car = self.condition.car;
20374 self.condition = self.condition.cdr;
20375 return AST_Seq.cons(car, self);
20377 var cond = self.condition.evaluate(compressor);
20378 if (cond.length > 1) {
20380 compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
20381 return self.consequent;
20383 compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
20384 return self.alternative;
20387 var negated = cond[0].negate(compressor);
20388 if (best_of(cond[0], negated) === negated) {
20389 self = make_node(AST_Conditional, self, {
20390 condition: negated,
20391 consequent: self.alternative,
20392 alternative: self.consequent
20395 var consequent = self.consequent;
20396 var alternative = self.alternative;
20397 if (consequent instanceof AST_Assign && alternative instanceof AST_Assign && consequent.operator == alternative.operator && consequent.left.equivalent_to(alternative.left)) {
20398 self = make_node(AST_Assign, self, {
20399 operator: consequent.operator,
20400 left: consequent.left,
20401 right: make_node(AST_Conditional, self, {
20402 condition: self.condition,
20403 consequent: consequent.right,
20404 alternative: alternative.right
20410 OPT(AST_Boolean, function(self, compressor) {
20411 if (compressor.option("booleans")) {
20412 var p = compressor.parent();
20413 if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
20414 compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
20415 operator: p.operator,
20417 file: p.start.file,
20418 line: p.start.line,
20421 return make_node(AST_Number, self, {
20425 return make_node(AST_UnaryPrefix, self, {
20427 expression: make_node(AST_Number, self, {
20428 value: 1 - self.value
20434 OPT(AST_Sub, function(self, compressor) {
20435 var prop = self.property;
20436 if (prop instanceof AST_String && compressor.option("properties")) {
20437 prop = prop.getValue();
20438 if (compressor.option("screw_ie8") && RESERVED_WORDS(prop) || !RESERVED_WORDS(prop) && is_identifier_string(prop)) {
20439 return make_node(AST_Dot, self, {
20440 expression: self.expression,
20447 function literals_in_boolean_context(self, compressor) {
20448 if (compressor.option("booleans") && compressor.in_boolean_context()) {
20449 return make_node(AST_True, self);
20453 OPT(AST_Array, literals_in_boolean_context);
20454 OPT(AST_Object, literals_in_boolean_context);
20455 OPT(AST_RegExp, literals_in_boolean_context);
20458 function SourceMap(options) {
20459 options = defaults(options, {
20464 var generator = new MOZ_SourceMap.SourceMapGenerator({
20465 file: options.file,
20466 sourceRoot: options.root
20468 var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig);
20469 function add(source, gen_line, gen_col, orig_line, orig_col, name) {
20471 var info = orig_map.originalPositionFor({
20475 source = info.source;
20476 orig_line = info.line;
20477 orig_col = info.column;
20480 generator.addMapping({
20498 toString: function() {
20499 return generator.toString();
20506 TryStatement: function(M) {
20507 return new AST_Try({
20508 start: my_start_token(M),
20509 end: my_end_token(M),
20510 body: from_moz(M.block).body,
20511 bcatch: from_moz(M.handlers[0]),
20512 bfinally: M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
20515 CatchClause: function(M) {
20516 return new AST_Catch({
20517 start: my_start_token(M),
20518 end: my_end_token(M),
20519 argname: from_moz(M.param),
20520 body: from_moz(M.body).body
20523 ObjectExpression: function(M) {
20524 return new AST_Object({
20525 start: my_start_token(M),
20526 end: my_end_token(M),
20527 properties: M.properties.map(function(prop) {
20528 var key = prop.key;
20529 var name = key.type == "Identifier" ? key.name : key.value;
20531 start: my_start_token(key),
20532 end: my_end_token(prop.value),
20534 value: from_moz(prop.value)
20536 switch (prop.kind) {
20538 return new AST_ObjectKeyVal(args);
20541 args.value.name = from_moz(key);
20542 return new AST_ObjectSetter(args);
20545 args.value.name = from_moz(key);
20546 return new AST_ObjectGetter(args);
20551 SequenceExpression: function(M) {
20552 return AST_Seq.from_array(M.expressions.map(from_moz));
20554 MemberExpression: function(M) {
20555 return new (M.computed ? AST_Sub : AST_Dot)({
20556 start: my_start_token(M),
20557 end: my_end_token(M),
20558 property: M.computed ? from_moz(M.property) : M.property.name,
20559 expression: from_moz(M.object)
20562 SwitchCase: function(M) {
20563 return new (M.test ? AST_Case : AST_Default)({
20564 start: my_start_token(M),
20565 end: my_end_token(M),
20566 expression: from_moz(M.test),
20567 body: M.consequent.map(from_moz)
20570 Literal: function(M) {
20571 var val = M.value, args = {
20572 start: my_start_token(M),
20573 end: my_end_token(M)
20575 if (val === null) return new AST_Null(args);
20576 switch (typeof val) {
20579 return new AST_String(args);
20583 return new AST_Number(args);
20586 return new (val ? AST_True : AST_False)(args);
20590 return new AST_RegExp(args);
20593 UnaryExpression: From_Moz_Unary,
20594 UpdateExpression: From_Moz_Unary,
20595 Identifier: function(M) {
20596 var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
20597 return new (M.name == "this" ? AST_This : p.type == "LabeledStatement" ? AST_Label : p.type == "VariableDeclarator" && p.id === M ? p.kind == "const" ? AST_SymbolConst : AST_SymbolVar : p.type == "FunctionExpression" ? p.id === M ? AST_SymbolLambda : AST_SymbolFunarg : p.type == "FunctionDeclaration" ? p.id === M ? AST_SymbolDefun : AST_SymbolFunarg : p.type == "CatchClause" ? AST_SymbolCatch : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef : AST_SymbolRef)({
20598 start: my_start_token(M),
20599 end: my_end_token(M),
20604 function From_Moz_Unary(M) {
20605 var prefix = "prefix" in M ? M.prefix : M.type == "UnaryExpression" ? true : false;
20606 return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
20607 start: my_start_token(M),
20608 end: my_end_token(M),
20609 operator: M.operator,
20610 expression: from_moz(M.argument)
20613 var ME_TO_MOZ = {};
20614 map("Node", AST_Node);
20615 map("Program", AST_Toplevel, "body@body");
20616 map("Function", AST_Function, "id>name, params@argnames, body%body");
20617 map("EmptyStatement", AST_EmptyStatement);
20618 map("BlockStatement", AST_BlockStatement, "body@body");
20619 map("ExpressionStatement", AST_SimpleStatement, "expression>body");
20620 map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
20621 map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
20622 map("BreakStatement", AST_Break, "label>label");
20623 map("ContinueStatement", AST_Continue, "label>label");
20624 map("WithStatement", AST_With, "object>expression, body>body");
20625 map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body");
20626 map("ReturnStatement", AST_Return, "argument>value");
20627 map("ThrowStatement", AST_Throw, "argument>value");
20628 map("WhileStatement", AST_While, "test>condition, body>body");
20629 map("DoWhileStatement", AST_Do, "test>condition, body>body");
20630 map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body");
20631 map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
20632 map("DebuggerStatement", AST_Debugger);
20633 map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body");
20634 map("VariableDeclaration", AST_Var, "declarations@definitions");
20635 map("VariableDeclarator", AST_VarDef, "id>name, init>value");
20636 map("ThisExpression", AST_This);
20637 map("ArrayExpression", AST_Array, "elements@elements");
20638 map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body");
20639 map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
20640 map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
20641 map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
20642 map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
20643 map("NewExpression", AST_New, "callee>expression, arguments@args");
20644 map("CallExpression", AST_Call, "callee>expression, arguments@args");
20645 function my_start_token(moznode) {
20646 return new AST_Token({
20647 file: moznode.loc && moznode.loc.source,
20648 line: moznode.loc && moznode.loc.start.line,
20649 col: moznode.loc && moznode.loc.start.column,
20650 pos: moznode.start,
20651 endpos: moznode.start
20654 function my_end_token(moznode) {
20655 return new AST_Token({
20656 file: moznode.loc && moznode.loc.source,
20657 line: moznode.loc && moznode.loc.end.line,
20658 col: moznode.loc && moznode.loc.end.column,
20660 endpos: moznode.end
20663 function map(moztype, mytype, propmap) {
20664 var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
20665 moz_to_me += "return new mytype({\n" + "start: my_start_token(M),\n" + "end: my_end_token(M)";
20666 if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop) {
20667 var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
20668 if (!m) throw new Error("Can't understand property map: " + prop);
20669 var moz = "M." + m[1], how = m[2], my = m[3];
20670 moz_to_me += ",\n" + my + ": ";
20672 moz_to_me += moz + ".map(from_moz)";
20673 } else if (how == ">") {
20674 moz_to_me += "from_moz(" + moz + ")";
20675 } else if (how == "=") {
20677 } else if (how == "%") {
20678 moz_to_me += "from_moz(" + moz + ").body";
20679 } else throw new Error("Can't understand operator in propmap: " + prop);
20681 moz_to_me += "\n})}";
20682 moz_to_me = new Function("mytype", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(mytype, my_start_token, my_end_token, from_moz);
20683 return MOZ_TO_ME[moztype] = moz_to_me;
20685 var FROM_MOZ_STACK = null;
20686 function from_moz(node) {
20687 FROM_MOZ_STACK.push(node);
20688 var ret = node != null ? MOZ_TO_ME[node.type](node) : null;
20689 FROM_MOZ_STACK.pop();
20692 AST_Node.from_mozilla_ast = function(node) {
20693 var save_stack = FROM_MOZ_STACK;
20694 FROM_MOZ_STACK = [];
20695 var ast = from_moz(node);
20696 FROM_MOZ_STACK = save_stack;
20700 exports["array_to_hash"] = array_to_hash;
20701 exports["slice"] = slice;
20702 exports["characters"] = characters;
20703 exports["member"] = member;
20704 exports["find_if"] = find_if;
20705 exports["repeat_string"] = repeat_string;
20706 exports["DefaultsError"] = DefaultsError;
20707 exports["defaults"] = defaults;
20708 exports["merge"] = merge;
20709 exports["noop"] = noop;
20710 exports["MAP"] = MAP;
20711 exports["push_uniq"] = push_uniq;
20712 exports["string_template"] = string_template;
20713 exports["remove"] = remove;
20714 exports["mergeSort"] = mergeSort;
20715 exports["set_difference"] = set_difference;
20716 exports["set_intersection"] = set_intersection;
20717 exports["makePredicate"] = makePredicate;
20718 exports["all"] = all;
20719 exports["Dictionary"] = Dictionary;
20720 exports["DEFNODE"] = DEFNODE;
20721 exports["AST_Token"] = AST_Token;
20722 exports["AST_Node"] = AST_Node;
20723 exports["AST_Statement"] = AST_Statement;
20724 exports["AST_Debugger"] = AST_Debugger;
20725 exports["AST_Directive"] = AST_Directive;
20726 exports["AST_SimpleStatement"] = AST_SimpleStatement;
20727 exports["walk_body"] = walk_body;
20728 exports["AST_Block"] = AST_Block;
20729 exports["AST_BlockStatement"] = AST_BlockStatement;
20730 exports["AST_EmptyStatement"] = AST_EmptyStatement;
20731 exports["AST_StatementWithBody"] = AST_StatementWithBody;
20732 exports["AST_LabeledStatement"] = AST_LabeledStatement;
20733 exports["AST_DWLoop"] = AST_DWLoop;
20734 exports["AST_Do"] = AST_Do;
20735 exports["AST_While"] = AST_While;
20736 exports["AST_For"] = AST_For;
20737 exports["AST_ForIn"] = AST_ForIn;
20738 exports["AST_With"] = AST_With;
20739 exports["AST_Scope"] = AST_Scope;
20740 exports["AST_Toplevel"] = AST_Toplevel;
20741 exports["AST_Lambda"] = AST_Lambda;
20742 exports["AST_Accessor"] = AST_Accessor;
20743 exports["AST_Function"] = AST_Function;
20744 exports["AST_Defun"] = AST_Defun;
20745 exports["AST_Jump"] = AST_Jump;
20746 exports["AST_Exit"] = AST_Exit;
20747 exports["AST_Return"] = AST_Return;
20748 exports["AST_Throw"] = AST_Throw;
20749 exports["AST_LoopControl"] = AST_LoopControl;
20750 exports["AST_Break"] = AST_Break;
20751 exports["AST_Continue"] = AST_Continue;
20752 exports["AST_If"] = AST_If;
20753 exports["AST_Switch"] = AST_Switch;
20754 exports["AST_SwitchBranch"] = AST_SwitchBranch;
20755 exports["AST_Default"] = AST_Default;
20756 exports["AST_Case"] = AST_Case;
20757 exports["AST_Try"] = AST_Try;
20758 exports["AST_Catch"] = AST_Catch;
20759 exports["AST_Finally"] = AST_Finally;
20760 exports["AST_Definitions"] = AST_Definitions;
20761 exports["AST_Var"] = AST_Var;
20762 exports["AST_Const"] = AST_Const;
20763 exports["AST_VarDef"] = AST_VarDef;
20764 exports["AST_Call"] = AST_Call;
20765 exports["AST_New"] = AST_New;
20766 exports["AST_Seq"] = AST_Seq;
20767 exports["AST_PropAccess"] = AST_PropAccess;
20768 exports["AST_Dot"] = AST_Dot;
20769 exports["AST_Sub"] = AST_Sub;
20770 exports["AST_Unary"] = AST_Unary;
20771 exports["AST_UnaryPrefix"] = AST_UnaryPrefix;
20772 exports["AST_UnaryPostfix"] = AST_UnaryPostfix;
20773 exports["AST_Binary"] = AST_Binary;
20774 exports["AST_Conditional"] = AST_Conditional;
20775 exports["AST_Assign"] = AST_Assign;
20776 exports["AST_Array"] = AST_Array;
20777 exports["AST_Object"] = AST_Object;
20778 exports["AST_ObjectProperty"] = AST_ObjectProperty;
20779 exports["AST_ObjectKeyVal"] = AST_ObjectKeyVal;
20780 exports["AST_ObjectSetter"] = AST_ObjectSetter;
20781 exports["AST_ObjectGetter"] = AST_ObjectGetter;
20782 exports["AST_Symbol"] = AST_Symbol;
20783 exports["AST_SymbolAccessor"] = AST_SymbolAccessor;
20784 exports["AST_SymbolDeclaration"] = AST_SymbolDeclaration;
20785 exports["AST_SymbolVar"] = AST_SymbolVar;
20786 exports["AST_SymbolConst"] = AST_SymbolConst;
20787 exports["AST_SymbolFunarg"] = AST_SymbolFunarg;
20788 exports["AST_SymbolDefun"] = AST_SymbolDefun;
20789 exports["AST_SymbolLambda"] = AST_SymbolLambda;
20790 exports["AST_SymbolCatch"] = AST_SymbolCatch;
20791 exports["AST_Label"] = AST_Label;
20792 exports["AST_SymbolRef"] = AST_SymbolRef;
20793 exports["AST_LabelRef"] = AST_LabelRef;
20794 exports["AST_This"] = AST_This;
20795 exports["AST_Constant"] = AST_Constant;
20796 exports["AST_String"] = AST_String;
20797 exports["AST_Number"] = AST_Number;
20798 exports["AST_RegExp"] = AST_RegExp;
20799 exports["AST_Atom"] = AST_Atom;
20800 exports["AST_Null"] = AST_Null;
20801 exports["AST_NaN"] = AST_NaN;
20802 exports["AST_Undefined"] = AST_Undefined;
20803 exports["AST_Hole"] = AST_Hole;
20804 exports["AST_Infinity"] = AST_Infinity;
20805 exports["AST_Boolean"] = AST_Boolean;
20806 exports["AST_False"] = AST_False;
20807 exports["AST_True"] = AST_True;
20808 exports["TreeWalker"] = TreeWalker;
20809 exports["KEYWORDS"] = KEYWORDS;
20810 exports["KEYWORDS_ATOM"] = KEYWORDS_ATOM;
20811 exports["RESERVED_WORDS"] = RESERVED_WORDS;
20812 exports["KEYWORDS_BEFORE_EXPRESSION"] = KEYWORDS_BEFORE_EXPRESSION;
20813 exports["OPERATOR_CHARS"] = OPERATOR_CHARS;
20814 exports["RE_HEX_NUMBER"] = RE_HEX_NUMBER;
20815 exports["RE_OCT_NUMBER"] = RE_OCT_NUMBER;
20816 exports["RE_DEC_NUMBER"] = RE_DEC_NUMBER;
20817 exports["OPERATORS"] = OPERATORS;
20818 exports["WHITESPACE_CHARS"] = WHITESPACE_CHARS;
20819 exports["PUNC_BEFORE_EXPRESSION"] = PUNC_BEFORE_EXPRESSION;
20820 exports["PUNC_CHARS"] = PUNC_CHARS;
20821 exports["REGEXP_MODIFIERS"] = REGEXP_MODIFIERS;
20822 exports["UNICODE"] = UNICODE;
20823 exports["is_letter"] = is_letter;
20824 exports["is_digit"] = is_digit;
20825 exports["is_alphanumeric_char"] = is_alphanumeric_char;
20826 exports["is_unicode_combining_mark"] = is_unicode_combining_mark;
20827 exports["is_unicode_connector_punctuation"] = is_unicode_connector_punctuation;
20828 exports["is_identifier"] = is_identifier;
20829 exports["is_identifier_start"] = is_identifier_start;
20830 exports["is_identifier_char"] = is_identifier_char;
20831 exports["is_identifier_string"] = is_identifier_string;
20832 exports["parse_js_number"] = parse_js_number;
20833 exports["JS_Parse_Error"] = JS_Parse_Error;
20834 exports["js_error"] = js_error;
20835 exports["is_token"] = is_token;
20836 exports["EX_EOF"] = EX_EOF;
20837 exports["tokenizer"] = tokenizer;
20838 exports["UNARY_PREFIX"] = UNARY_PREFIX;
20839 exports["UNARY_POSTFIX"] = UNARY_POSTFIX;
20840 exports["ASSIGNMENT"] = ASSIGNMENT;
20841 exports["PRECEDENCE"] = PRECEDENCE;
20842 exports["STATEMENTS_WITH_LABELS"] = STATEMENTS_WITH_LABELS;
20843 exports["ATOMIC_START_TOKEN"] = ATOMIC_START_TOKEN;
20844 exports["parse"] = parse;
20845 exports["TreeTransformer"] = TreeTransformer;
20846 exports["SymbolDef"] = SymbolDef;
20847 exports["base54"] = base54;
20848 exports["OutputStream"] = OutputStream;
20849 exports["Compressor"] = Compressor;
20850 exports["SourceMap"] = SourceMap;
20851 })({}, function() {
20855 var UglifyJS = exports.UglifyJS;
20857 UglifyJS.AST_Node.warn_function = function(txt) {
20858 logger.error("uglifyjs2 WARN: " + txt);
20861 //JRB: MODIFIED FROM UGLIFY SOURCE
20862 //to take a name for the file, and then set toplevel.filename to be that name.
20863 exports.minify = function(files, options, name) {
20864 options = UglifyJS.defaults(options, {
20865 outSourceMap : null,
20867 inSourceMap : null,
20868 fromString : false,
20874 if (typeof files == "string")
20878 var toplevel = null;
20879 files.forEach(function(file){
20880 var code = options.fromString
20882 : rjsFile.readFile(file, "utf8");
20883 toplevel = UglifyJS.parse(code, {
20884 filename: options.fromString ? name : file,
20890 if (options.compress) {
20891 var compress = { warnings: options.warnings };
20892 UglifyJS.merge(compress, options.compress);
20893 toplevel.figure_out_scope();
20894 var sq = UglifyJS.Compressor(compress);
20895 toplevel = toplevel.transform(sq);
20899 if (options.mangle) {
20900 toplevel.figure_out_scope();
20901 toplevel.compute_char_frequency();
20902 toplevel.mangle_names(options.mangle);
20908 if (options.inSourceMap) {
20909 inMap = rjsFile.readFile(options.inSourceMap, "utf8");
20911 if (options.outSourceMap) map = UglifyJS.SourceMap({
20912 file: options.outSourceMap,
20914 root: options.sourceRoot
20916 var output = { source_map: map };
20917 if (options.output) {
20918 UglifyJS.merge(output, options.output);
20920 var stream = UglifyJS.OutputStream(output);
20921 toplevel.print(stream);
20923 code : stream + "",
20928 // exports.describe_ast = function() {
20929 // function doitem(ctor) {
20931 // ctor.SUBCLASSES.forEach(function(ctor){
20932 // sub[ctor.TYPE] = doitem(ctor);
20935 // if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS;
20936 // if (ctor.SUBCLASSES.length > 0) ret.sub = sub;
20939 // return doitem(UglifyJS.AST_Node).sub;
20942 exports.describe_ast = function() {
20943 var out = UglifyJS.OutputStream({ beautify: true });
20944 function doitem(ctor) {
20945 out.print("AST_" + ctor.TYPE);
20946 var props = ctor.SELF_PROPS.filter(function(prop){
20947 return !/^\$/.test(prop);
20949 if (props.length > 0) {
20951 out.with_parens(function(){
20952 props.forEach(function(prop, i){
20953 if (i) out.space();
20958 if (ctor.documentation) {
20960 out.print_string(ctor.documentation);
20962 if (ctor.SUBCLASSES.length > 0) {
20964 out.with_block(function(){
20965 ctor.SUBCLASSES.forEach(function(ctor, i){
20973 doitem(UglifyJS.AST_Node);
20979 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
20980 * Available via the MIT or new BSD license.
20981 * see: http://github.com/jrburke/requirejs for details
20984 /*jslint plusplus: true */
20985 /*global define: false */
20987 define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) {
20990 function arrayToString(ary) {
20993 ary.forEach(function (item, i) {
20994 output += (i > 0 ? ',' : '') + '"' + lang.jsEscape(item) + '"';
21002 //This string is saved off because JSLint complains
21003 //about obj.arguments use, as 'reserved word'
21004 var argPropName = 'arguments';
21006 //From an esprima example for traversing its ast.
21007 function traverse(object, visitor) {
21014 if (visitor.call(null, object) === false) {
21017 for (key in object) {
21018 if (object.hasOwnProperty(key)) {
21019 child = object[key];
21020 if (typeof child === 'object' && child !== null) {
21021 if (traverse(child, visitor) === false) {
21029 //Like traverse, but visitor returning false just
21030 //stops that subtree analysis, not the rest of tree
21032 function traverseBroad(object, visitor) {
21039 if (visitor.call(null, object) === false) {
21042 for (key in object) {
21043 if (object.hasOwnProperty(key)) {
21044 child = object[key];
21045 if (typeof child === 'object' && child !== null) {
21046 traverse(child, visitor);
21053 * Pulls out dependencies from an array literal with just string members.
21054 * If string literals, will just return those string values in an array,
21055 * skipping other items in the array.
21057 * @param {Node} node an AST node.
21059 * @returns {Array} an array of strings.
21060 * If null is returned, then it means the input node was not a valid
21063 function getValidDeps(node) {
21064 if (!node || node.type !== 'ArrayExpression' || !node.elements) {
21070 node.elements.some(function (elem) {
21071 if (elem.type === 'Literal') {
21072 deps.push(elem.value);
21076 return deps.length ? deps : undefined;
21080 * Main parse function. Returns a string of any valid require or
21081 * define/require.def calls as part of one JavaScript source string.
21082 * @param {String} moduleName the module name that represents this file.
21083 * It is used to create a default define if there is not one already for the
21084 * file. This allows properly tracing dependencies for builds. Otherwise, if
21085 * the file just has a require() call, the file dependencies will not be
21086 * properly reflected: the file will come before its dependencies.
21087 * @param {String} moduleName
21088 * @param {String} fileName
21089 * @param {String} fileContents
21090 * @param {Object} options optional options. insertNeedsDefine: true will
21091 * add calls to require.needsDefine() if appropriate.
21092 * @returns {String} JS source string or null, if no require or
21093 * define/require.def calls are found.
21095 function parse(moduleName, fileName, fileContents, options) {
21096 options = options || {};
21098 //Set up source input
21099 var i, moduleCall, depString,
21103 needsDefine = true,
21104 astRoot = esprima.parse(fileContents);
21106 parse.recurse(astRoot, function (callName, config, name, deps) {
21111 if (callName === 'define' && (!name || name === moduleName)) {
21112 needsDefine = false;
21116 //If there is no module name, the dependencies are for
21117 //this file/default module name.
21118 moduleDeps = moduleDeps.concat(deps);
21126 //If define was found, no need to dive deeper, unless
21127 //the config explicitly wants to dig deeper.
21128 return !!options.findNestedDependencies;
21131 if (options.insertNeedsDefine && needsDefine) {
21132 result += 'require.needsDefine("' + moduleName + '");';
21135 if (moduleDeps.length || moduleList.length) {
21136 for (i = 0; i < moduleList.length; i++) {
21137 moduleCall = moduleList[i];
21142 //If this is the main module for this file, combine any
21143 //"anonymous" dependencies (could come from a nested require
21144 //call) with this module.
21145 if (moduleCall.name === moduleName) {
21146 moduleCall.deps = moduleCall.deps.concat(moduleDeps);
21150 depString = arrayToString(moduleCall.deps);
21151 result += 'define("' + moduleCall.name + '",' +
21154 if (moduleDeps.length) {
21158 depString = arrayToString(moduleDeps);
21159 result += 'define("' + moduleName + '",' + depString + ');';
21163 return result || null;
21166 parse.traverse = traverse;
21167 parse.traverseBroad = traverseBroad;
21170 * Handles parsing a file recursively for require calls.
21171 * @param {Array} parentNode the AST node to start with.
21172 * @param {Function} onMatch function to call on a parse match.
21173 * @param {Object} [options] This is normally the build config options if
21176 parse.recurse = function (object, onMatch, options) {
21177 //Like traverse, but skips if branches that would not be processed
21178 //after has application that results in tests of true or false boolean
21181 hasHas = options && options.has;
21187 //If has replacement has resulted in if(true){} or if(false){}, take
21188 //the appropriate branch and skip the other one.
21189 if (hasHas && object.type === 'IfStatement' && object.test.type &&
21190 object.test.type === 'Literal') {
21191 if (object.test.value) {
21192 //Take the if branch
21193 this.recurse(object.consequent, onMatch, options);
21195 //Take the else branch
21196 this.recurse(object.alternate, onMatch, options);
21199 if (this.parseNode(object, onMatch) === false) {
21202 for (key in object) {
21203 if (object.hasOwnProperty(key)) {
21204 child = object[key];
21205 if (typeof child === 'object' && child !== null) {
21206 this.recurse(child, onMatch, options);
21214 * Determines if the file defines the require/define module API.
21215 * Specifically, it looks for the `define.amd = ` expression.
21216 * @param {String} fileName
21217 * @param {String} fileContents
21218 * @returns {Boolean}
21220 parse.definesRequire = function (fileName, fileContents) {
21223 traverse(esprima.parse(fileContents), function (node) {
21224 if (parse.hasDefineAmd(node)) {
21236 * Finds require("") calls inside a CommonJS anonymous module wrapped in a
21237 * define(function(require, exports, module){}) wrapper. These dependencies
21238 * will be added to a modified define() call that lists the dependencies
21239 * on the outside of the function.
21240 * @param {String} fileName
21241 * @param {String|Object} fileContents: a string of contents, or an already
21243 * @returns {Array} an array of module names that are dependencies. Always
21244 * returns an array, but could be of length zero.
21246 parse.getAnonDeps = function (fileName, fileContents) {
21247 var astRoot = typeof fileContents === 'string' ?
21248 esprima.parse(fileContents) : fileContents,
21249 defFunc = this.findAnonDefineFactory(astRoot);
21251 return parse.getAnonDepsFromNode(defFunc);
21255 * Finds require("") calls inside a CommonJS anonymous module wrapped
21256 * in a define function, given an AST node for the definition function.
21257 * @param {Node} node the AST node for the definition function.
21258 * @returns {Array} and array of dependency names. Can be of zero length.
21260 parse.getAnonDepsFromNode = function (node) {
21265 this.findRequireDepNames(node, deps);
21267 //If no deps, still add the standard CommonJS require, exports,
21268 //module, in that order, to the deps, but only if specified as
21269 //function args. In particular, if exports is used, it is favored
21270 //over the return value of the function, so only add it if asked.
21271 funcArgLength = node.params && node.params.length;
21272 if (funcArgLength) {
21273 deps = (funcArgLength > 1 ? ["require", "exports", "module"] :
21274 ["require"]).concat(deps);
21280 parse.isDefineNodeWithArgs = function (node) {
21281 return node && node.type === 'CallExpression' &&
21282 node.callee && node.callee.type === 'Identifier' &&
21283 node.callee.name === 'define' && node[argPropName];
21287 * Finds the function in define(function (require, exports, module){});
21288 * @param {Array} node
21289 * @returns {Boolean}
21291 parse.findAnonDefineFactory = function (node) {
21294 traverse(node, function (node) {
21297 if (parse.isDefineNodeWithArgs(node)) {
21299 //Just the factory function passed to define
21300 arg0 = node[argPropName][0];
21301 if (arg0 && arg0.type === 'FunctionExpression') {
21306 //A string literal module ID followed by the factory function.
21307 arg1 = node[argPropName][1];
21308 if (arg0.type === 'Literal' &&
21309 arg1 && arg1.type === 'FunctionExpression') {
21320 * Finds any config that is passed to requirejs. That includes calls to
21321 * require/requirejs.config(), as well as require({}, ...) and
21322 * requirejs({}, ...)
21323 * @param {String} fileContents
21325 * @returns {Object} a config details object with the following properties:
21326 * - config: {Object} the config object found. Can be undefined if no
21328 * - range: {Array} the start index and end index in the contents where
21329 * the config was found. Can be undefined if no config found.
21330 * Can throw an error if the config in the file cannot be evaluated in
21331 * a build context to valid JavaScript.
21333 parse.findConfig = function (fileContents) {
21334 /*jslint evil: true */
21335 var jsConfig, foundConfig, stringData, foundRange, quote, quoteMatch,
21336 quoteRegExp = /(:\s|\[\s*)(['"])/,
21337 astRoot = esprima.parse(fileContents, {
21341 traverse(astRoot, function (node) {
21343 requireType = parse.hasRequire(node);
21345 if (requireType && (requireType === 'require' ||
21346 requireType === 'requirejs' ||
21347 requireType === 'requireConfig' ||
21348 requireType === 'requirejsConfig')) {
21350 arg = node[argPropName] && node[argPropName][0];
21352 if (arg && arg.type === 'ObjectExpression') {
21353 stringData = parse.nodeToString(fileContents, arg);
21354 jsConfig = stringData.value;
21355 foundRange = stringData.range;
21359 arg = parse.getRequireObjectLiteral(node);
21361 stringData = parse.nodeToString(fileContents, arg);
21362 jsConfig = stringData.value;
21363 foundRange = stringData.range;
21371 quoteMatch = quoteRegExp.exec(jsConfig);
21372 quote = (quoteMatch && quoteMatch[2]) || '"';
21373 foundConfig = eval('(' + jsConfig + ')');
21377 config: foundConfig,
21383 /** Returns the node for the object literal assigned to require/requirejs,
21384 * for holding a declarative config.
21386 parse.getRequireObjectLiteral = function (node) {
21387 if (node.id && node.id.type === 'Identifier' &&
21388 (node.id.name === 'require' || node.id.name === 'requirejs') &&
21389 node.init && node.init.type === 'ObjectExpression') {
21395 * Renames require/requirejs/define calls to be ns + '.' + require/requirejs/define
21396 * Does *not* do .config calls though. See pragma.namespace for the complete
21397 * set of namespace transforms. This function is used because require calls
21398 * inside a define() call should not be renamed, so a simple regexp is not
21400 * @param {String} fileContents the contents to transform.
21401 * @param {String} ns the namespace, *not* including trailing dot.
21402 * @return {String} the fileContents with the namespace applied
21404 parse.renameNamespace = function (fileContents, ns) {
21407 astRoot = esprima.parse(fileContents, {
21411 parse.recurse(astRoot, function (callName, config, name, deps, node) {
21412 locs.push(node.loc);
21413 //Do not recurse into define functions, they should be using
21415 return callName !== 'define';
21419 lines = fileContents.split('\n');
21421 //Go backwards through the found locs, adding in the namespace name
21424 locs.forEach(function (loc) {
21425 var startIndex = loc.start.column,
21426 //start.line is 1-based, not 0 based.
21427 lineIndex = loc.start.line - 1,
21428 line = lines[lineIndex];
21430 lines[lineIndex] = line.substring(0, startIndex) +
21432 line.substring(startIndex,
21436 fileContents = lines.join('\n');
21439 return fileContents;
21443 * Finds all dependencies specified in dependency arrays and inside
21444 * simplified commonjs wrappers.
21445 * @param {String} fileName
21446 * @param {String} fileContents
21448 * @returns {Array} an array of dependency strings. The dependencies
21449 * have not been normalized, they may be relative IDs.
21451 parse.findDependencies = function (fileName, fileContents, options) {
21452 var dependencies = [],
21453 astRoot = esprima.parse(fileContents);
21455 parse.recurse(astRoot, function (callName, config, name, deps) {
21457 dependencies = dependencies.concat(deps);
21461 return dependencies;
21465 * Finds only CJS dependencies, ones that are the form
21466 * require('stringLiteral')
21468 parse.findCjsDependencies = function (fileName, fileContents) {
21469 var dependencies = [];
21471 traverse(esprima.parse(fileContents), function (node) {
21474 if (node && node.type === 'CallExpression' && node.callee &&
21475 node.callee.type === 'Identifier' &&
21476 node.callee.name === 'require' && node[argPropName] &&
21477 node[argPropName].length === 1) {
21478 arg = node[argPropName][0];
21479 if (arg.type === 'Literal') {
21480 dependencies.push(arg.value);
21485 return dependencies;
21488 //function define() {}
21489 parse.hasDefDefine = function (node) {
21490 return node.type === 'FunctionDeclaration' && node.id &&
21491 node.id.type === 'Identifier' && node.id.name === 'define';
21495 parse.hasDefineAmd = function (node) {
21496 return node && node.type === 'AssignmentExpression' &&
21497 node.left && node.left.type === 'MemberExpression' &&
21498 node.left.object && node.left.object.name === 'define' &&
21499 node.left.property && node.left.property.name === 'amd';
21502 //define.amd reference, as in: if (define.amd)
21503 parse.refsDefineAmd = function (node) {
21504 return node && node.type === 'MemberExpression' &&
21505 node.object && node.object.name === 'define' &&
21506 node.object.type === 'Identifier' &&
21507 node.property && node.property.name === 'amd' &&
21508 node.property.type === 'Identifier';
21511 //require(), requirejs(), require.config() and requirejs.config()
21512 parse.hasRequire = function (node) {
21514 c = node && node.callee;
21516 if (node && node.type === 'CallExpression' && c) {
21517 if (c.type === 'Identifier' &&
21518 (c.name === 'require' ||
21519 c.name === 'requirejs')) {
21520 //A require/requirejs({}, ...) call
21522 } else if (c.type === 'MemberExpression' &&
21524 c.object.type === 'Identifier' &&
21525 (c.object.name === 'require' ||
21526 c.object.name === 'requirejs') &&
21527 c.property && c.property.name === 'config') {
21528 // require/requirejs.config({}) call
21529 callName = c.object.name + 'Config';
21537 parse.hasDefine = function (node) {
21538 return node && node.type === 'CallExpression' && node.callee &&
21539 node.callee.type === 'Identifier' &&
21540 node.callee.name === 'define';
21544 * If there is a named define in the file, returns the name. Does not
21545 * scan for mulitple names, just the first one.
21547 parse.getNamedDefine = function (fileContents) {
21549 traverse(esprima.parse(fileContents), function (node) {
21550 if (node && node.type === 'CallExpression' && node.callee &&
21551 node.callee.type === 'Identifier' &&
21552 node.callee.name === 'define' &&
21553 node[argPropName] && node[argPropName][0] &&
21554 node[argPropName][0].type === 'Literal') {
21555 name = node[argPropName][0].value;
21564 * Determines if define(), require({}|[]) or requirejs was called in the
21565 * file. Also finds out if define() is declared and if define.amd is called.
21567 parse.usesAmdOrRequireJs = function (fileName, fileContents) {
21570 traverse(esprima.parse(fileContents), function (node) {
21571 var type, callName, arg;
21573 if (parse.hasDefDefine(node)) {
21574 //function define() {}
21575 type = 'declaresDefine';
21576 } else if (parse.hasDefineAmd(node)) {
21577 type = 'defineAmd';
21579 callName = parse.hasRequire(node);
21581 arg = node[argPropName] && node[argPropName][0];
21582 if (arg && (arg.type === 'ObjectExpression' ||
21583 arg.type === 'ArrayExpression')) {
21586 } else if (parse.hasDefine(node)) {
21603 * Determines if require(''), exports.x =, module.exports =,
21604 * __dirname, __filename are used. So, not strictly traditional CommonJS,
21605 * also checks for Node variants.
21607 parse.usesCommonJs = function (fileName, fileContents) {
21609 assignsExports = false;
21612 traverse(esprima.parse(fileContents), function (node) {
21614 exp = node.expression || node.init;
21616 if (node.type === 'Identifier' &&
21617 (node.name === '__dirname' || node.name === '__filename')) {
21618 type = node.name.substring(2);
21619 } else if (node.type === 'VariableDeclarator' && node.id &&
21620 node.id.type === 'Identifier' &&
21621 node.id.name === 'exports') {
21622 //Hmm, a variable assignment for exports, so does not use cjs
21624 type = 'varExports';
21625 } else if (exp && exp.type === 'AssignmentExpression' && exp.left &&
21626 exp.left.type === 'MemberExpression' && exp.left.object) {
21627 if (exp.left.object.name === 'module' && exp.left.property &&
21628 exp.left.property.name === 'exports') {
21629 type = 'moduleExports';
21630 } else if (exp.left.object.name === 'exports' &&
21631 exp.left.property) {
21635 } else if (node && node.type === 'CallExpression' && node.callee &&
21636 node.callee.type === 'Identifier' &&
21637 node.callee.name === 'require' && node[argPropName] &&
21638 node[argPropName].length === 1 &&
21639 node[argPropName][0].type === 'Literal') {
21644 if (type === 'varExports') {
21645 assignsExports = true;
21646 } else if (type !== 'exports' || !assignsExports) {
21659 parse.findRequireDepNames = function (node, deps) {
21660 traverse(node, function (node) {
21663 if (node && node.type === 'CallExpression' && node.callee &&
21664 node.callee.type === 'Identifier' &&
21665 node.callee.name === 'require' &&
21666 node[argPropName] && node[argPropName].length === 1) {
21668 arg = node[argPropName][0];
21669 if (arg.type === 'Literal') {
21670 deps.push(arg.value);
21677 * Determines if a specific node is a valid require or define/require.def
21679 * @param {Array} node
21680 * @param {Function} onMatch a function to call when a match is found.
21681 * It is passed the match name, and the config, name, deps possible args.
21682 * The config, name and deps args are not normalized.
21684 * @returns {String} a JS source string with the valid require/define call.
21687 parse.parseNode = function (node, onMatch) {
21688 var name, deps, cjsDeps, arg, factory, exp, refsDefine, bodyNode,
21689 args = node && node[argPropName],
21690 callName = parse.hasRequire(node);
21692 if (callName === 'require' || callName === 'requirejs') {
21693 //A plain require/requirejs call
21694 arg = node[argPropName] && node[argPropName][0];
21695 if (arg.type !== 'ArrayExpression') {
21696 if (arg.type === 'ObjectExpression') {
21697 //A config call, try the second arg.
21698 arg = node[argPropName][1];
21702 deps = getValidDeps(arg);
21707 return onMatch("require", null, null, deps, node);
21708 } else if (parse.hasDefine(node) && args && args.length) {
21713 if (name.type === 'ArrayExpression') {
21714 //No name, adjust args
21718 } else if (name.type === 'FunctionExpression') {
21719 //Just the factory, no name or deps
21721 name = deps = null;
21722 } else if (name.type !== 'Literal') {
21723 //An object literal, just null out
21724 name = deps = factory = null;
21727 if (name && name.type === 'Literal' && deps) {
21728 if (deps.type === 'FunctionExpression') {
21729 //deps is the factory
21732 } else if (deps.type === 'ObjectExpression') {
21733 //deps is object literal, null out
21734 deps = factory = null;
21735 } else if (deps.type === 'Identifier' && args.length === 2) {
21736 // define('id', factory)
21737 deps = factory = null;
21741 if (deps && deps.type === 'ArrayExpression') {
21742 deps = getValidDeps(deps);
21743 } else if (factory && factory.type === 'FunctionExpression') {
21744 //If no deps and a factory function, could be a commonjs sugar
21745 //wrapper, scan the function for dependencies.
21746 cjsDeps = parse.getAnonDepsFromNode(factory);
21747 if (cjsDeps.length) {
21750 } else if (deps || factory) {
21751 //Does not match the shape of an AMD call.
21755 //Just save off the name as a string instead of an AST object.
21756 if (name && name.type === 'Literal') {
21760 return onMatch("define", null, name, deps, node);
21761 } else if (node.type === 'CallExpression' && node.callee &&
21762 node.callee.type === 'FunctionExpression' &&
21763 node.callee.body && node.callee.body.body &&
21764 node.callee.body.body.length === 1 &&
21765 node.callee.body.body[0].type === 'IfStatement') {
21766 bodyNode = node.callee.body.body[0];
21767 //Look for a define(Identifier) case, but only if inside an
21768 //if that has a define.amd test
21769 if (bodyNode.consequent && bodyNode.consequent.body) {
21770 exp = bodyNode.consequent.body[0];
21771 if (exp.type === 'ExpressionStatement' && exp.expression &&
21772 parse.hasDefine(exp.expression) &&
21773 exp.expression.arguments &&
21774 exp.expression.arguments.length === 1 &&
21775 exp.expression.arguments[0].type === 'Identifier') {
21777 //Calls define(Identifier) as first statement in body.
21778 //Confirm the if test references define.amd
21779 traverse(bodyNode.test, function (node) {
21780 if (parse.refsDefineAmd(node)) {
21787 return onMatch("define", null, null, null, exp.expression);
21795 * Converts an AST node into a JS source string by extracting
21796 * the node's location from the given contents string. Assumes
21797 * esprima.parse() with loc was done.
21798 * @param {String} contents
21799 * @param {Object} node
21800 * @returns {String} a JS source string.
21802 parse.nodeToString = function (contents, node) {
21803 var loc = node.loc,
21804 lines = contents.split('\n'),
21805 firstLine = loc.start.line > 1 ?
21806 lines.slice(0, loc.start.line - 1).join('\n') + '\n' :
21808 preamble = firstLine +
21809 lines[loc.start.line - 1].substring(0, loc.start.column),
21810 extracted = lines[loc.start.line - 1].substring(loc.start.column) +
21812 lines.slice(loc.start.line, loc.end.line - 1).join('\n') +
21814 lines[loc.end.line - 1].substring(0, loc.end.column);
21820 preamble.length + extracted.length
21826 * Extracts license comments from JS text.
21827 * @param {String} fileName
21828 * @param {String} contents
21829 * @returns {String} a string of license comments.
21831 parse.getLicenseComments = function (fileName, contents) {
21832 var commentNode, refNode, subNode, value, i, j,
21833 //xpconnect's Reflect does not support comment or range, but
21834 //prefer continued operation vs strict parity of operation,
21835 //as license comments can be expressed in other ways, like
21836 //via wrap args, or linked via sourcemaps.
21837 ast = esprima.parse(contents, {
21843 lineEnd = contents.indexOf('\r') === -1 ? '\n' : '\r\n';
21845 if (ast.comments) {
21846 for (i = 0; i < ast.comments.length; i++) {
21847 commentNode = ast.comments[i];
21849 if (commentNode.type === 'Line') {
21850 value = '//' + commentNode.value + lineEnd;
21851 refNode = commentNode;
21853 if (i + 1 >= ast.comments.length) {
21856 //Look for immediately adjacent single line comments
21857 //since it could from a multiple line comment made out
21858 //of single line comments. Like this comment.
21859 for (j = i + 1; j < ast.comments.length; j++) {
21860 subNode = ast.comments[j];
21861 if (subNode.type === 'Line' &&
21862 subNode.range[0] === refNode.range[1] + 1) {
21863 //Adjacent single line comment. Collect it.
21864 value += '//' + subNode.value + lineEnd;
21867 //No more single line comment blocks. Break out
21868 //and continue outer looping.
21876 value = '/*' + commentNode.value + '*/' + lineEnd + lineEnd;
21879 if (!existsMap[value] && (value.indexOf('license') !== -1 ||
21880 (commentNode.type === 'Block' &&
21881 value.indexOf('/*!') === 0) ||
21882 value.indexOf('opyright') !== -1 ||
21883 value.indexOf('(c)') !== -1)) {
21886 existsMap[value] = true;
21898 * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved.
21899 * Available via the MIT or new BSD license.
21900 * see: http://github.com/jrburke/requirejs for details
21905 define('transform', [ './esprimaAdapter', './parse', 'logger', 'lang'],
21906 function (esprima, parse, logger, lang) {
21909 baseIndentRegExp = /^([ \t]+)/,
21910 indentRegExp = /\{[\r\n]+([ \t]+)/,
21911 keyRegExp = /^[_A-Za-z]([A-Za-z\d_]*)$/,
21912 bulkIndentRegExps = {
21917 function applyIndent(str, indent, lineReturn) {
21918 var regExp = bulkIndentRegExps[lineReturn];
21919 return str.replace(regExp, '$&' + indent);
21923 toTransport: function (namespace, moduleName, path, contents, onFound, options) {
21924 options = options || {};
21926 var astRoot, contentLines, modLine,
21933 astRoot = esprima.parse(contents, {
21937 logger.trace('toTransport skipping ' + path + ': ' +
21942 //Find the define calls and their position in the files.
21943 parse.traverseBroad(astRoot, function (node) {
21944 var args, firstArg, firstArgLoc, factoryNode,
21945 needsId, depAction, foundId,
21946 sourceUrlData, range,
21947 namespaceExists = false;
21949 namespaceExists = namespace &&
21950 node.type === 'CallExpression' &&
21951 node.callee && node.callee.object &&
21952 node.callee.object.type === 'Identifier' &&
21953 node.callee.object.name === namespace &&
21954 node.callee.property.type === 'Identifier' &&
21955 node.callee.property.name === 'define';
21957 if (namespaceExists || parse.isDefineNodeWithArgs(node)) {
21958 //The arguments are where its at.
21959 args = node.arguments;
21960 if (!args || !args.length) {
21964 firstArg = args[0];
21965 firstArgLoc = firstArg.loc;
21967 if (args.length === 1) {
21968 if (firstArg.type === 'Identifier') {
21969 //The define(factory) case, but
21970 //only allow it if one Identifier arg,
21971 //to limit impact of false positives.
21973 depAction = 'empty';
21974 } else if (firstArg.type === 'FunctionExpression') {
21975 //define(function(){})
21976 factoryNode = firstArg;
21978 depAction = 'scan';
21979 } else if (firstArg.type === 'ObjectExpression') {
21982 depAction = 'skip';
21983 } else if (firstArg.type === 'Literal' &&
21984 typeof firstArg.value === 'number') {
21987 depAction = 'skip';
21988 } else if (firstArg.type === 'UnaryExpression' &&
21989 firstArg.operator === '-' &&
21990 firstArg.argument &&
21991 firstArg.argument.type === 'Literal' &&
21992 typeof firstArg.argument.value === 'number') {
21993 //define('-12345');
21995 depAction = 'skip';
21996 } else if (firstArg.type === 'MemberExpression' &&
21998 firstArg.property &&
21999 firstArg.property.type === 'Identifier') {
22000 //define(this.key);
22002 depAction = 'empty';
22004 } else if (firstArg.type === 'ArrayExpression') {
22007 depAction = 'skip';
22008 } else if (firstArg.type === 'Literal' &&
22009 typeof firstArg.value === 'string') {
22010 //define('string', ....)
22011 //Already has an ID.
22013 if (args.length === 2 &&
22014 args[1].type === 'FunctionExpression') {
22015 //Needs dependency scanning.
22016 factoryNode = args[1];
22017 depAction = 'scan';
22019 depAction = 'skip';
22022 //Unknown define entity, keep looking, even
22023 //in the subtree for this node.
22030 depAction: depAction,
22031 namespaceExists: namespaceExists,
22033 defineLoc: node.loc,
22034 firstArgLoc: firstArgLoc,
22035 factoryNode: factoryNode,
22036 sourceUrlData: sourceUrlData
22039 //Only transform ones that do not have IDs. If it has an
22040 //ID but no dependency array, assume it is something like
22041 //a phonegap implementation, that has its own internal
22042 //define that cannot handle dependency array constructs,
22043 //and if it is a named module, then it means it has been
22044 //set for transport form.
22045 if (range.needsId) {
22047 logger.trace(path + ' has more than one anonymous ' +
22048 'define. May be a built file from another ' +
22049 'build system like, Ender. Skipping normalization.');
22054 defineInfos.push(range);
22056 } else if (depAction === 'scan') {
22058 if (scanCount > 1) {
22059 //Just go back to an array that just has the
22060 //anon one, since this is an already optimized
22061 //file like the phonegap one.
22063 defineInfos = foundAnon ? [foundAnon] : [];
22067 defineInfos.push(range);
22073 if (!defineInfos.length) {
22077 //Reverse the matches, need to start from the bottom of
22078 //the file to modify it, so that the ranges are still true
22080 defineInfos.reverse();
22082 contentLines = contents.split('\n');
22084 modLine = function (loc, contentInsertion) {
22085 var startIndex = loc.start.column,
22086 //start.line is 1-based, not 0 based.
22087 lineIndex = loc.start.line - 1,
22088 line = contentLines[lineIndex];
22089 contentLines[lineIndex] = line.substring(0, startIndex) +
22091 line.substring(startIndex,
22095 defineInfos.forEach(function (info) {
22097 contentInsertion = '',
22100 //Do the modifications "backwards", in other words, start with the
22101 //one that is farthest down and work up, so that the ranges in the
22102 //defineInfos still apply. So that means deps, id, then namespace.
22103 if (info.needsId && moduleName) {
22104 contentInsertion += "'" + moduleName + "',";
22107 if (info.depAction === 'scan') {
22108 deps = parse.getAnonDepsFromNode(info.factoryNode);
22111 depString = '[' + deps.map(function (dep) {
22112 return "'" + dep + "'";
22119 if (info.factoryNode) {
22120 //Already have a named module, need to insert the
22121 //dependencies after the name.
22122 modLine(info.factoryNode.loc, depString);
22124 contentInsertion += depString;
22128 if (contentInsertion) {
22129 modLine(info.firstArgLoc, contentInsertion);
22132 //Do namespace last so that ui does not mess upthe parenRange
22134 if (namespace && !info.namespaceExists) {
22135 modLine(info.defineLoc, namespace + '.');
22138 //Notify any listener for the found info
22144 contents = contentLines.join('\n');
22146 if (options.useSourceUrl) {
22147 contents = 'eval("' + lang.jsEscape(contents) +
22148 '\\n//# sourceURL=' + (path.indexOf('/') === 0 ? '' : '/') +
22157 * Modify the contents of a require.config/requirejs.config call. This
22158 * call will LOSE any existing comments that are in the config string.
22160 * @param {String} fileContents String that may contain a config call
22161 * @param {Function} onConfig Function called when the first config
22162 * call is found. It will be passed an Object which is the current
22163 * config, and the onConfig function should return an Object to use
22165 * @return {String} the fileContents with the config changes applied.
22167 modifyConfig: function (fileContents, onConfig) {
22168 var details = parse.findConfig(fileContents),
22169 config = details.config;
22172 config = onConfig(config);
22174 return transform.serializeConfig(config,
22179 quote: details.quote
22184 return fileContents;
22187 serializeConfig: function (config, fileContents, start, end, options) {
22188 //Calculate base level of indent
22189 var indent, match, configString, outDentRegExp,
22191 startString = fileContents.substring(0, start),
22192 existingConfigString = fileContents.substring(start, end),
22193 lineReturn = existingConfigString.indexOf('\r') === -1 ? '\n' : '\r\n',
22194 lastReturnIndex = startString.lastIndexOf('\n');
22196 //Get the basic amount of indent for the require config call.
22197 if (lastReturnIndex === -1) {
22198 lastReturnIndex = 0;
22201 match = baseIndentRegExp.exec(startString.substring(lastReturnIndex + 1, start));
22202 if (match && match[1]) {
22203 baseIndent = match[1];
22206 //Calculate internal indentation for config
22207 match = indentRegExp.exec(existingConfigString);
22208 if (match && match[1]) {
22212 if (!indent || indent.length < baseIndent) {
22215 indent = indent.substring(baseIndent.length);
22218 outDentRegExp = new RegExp('(' + lineReturn + ')' + indent, 'g');
22220 configString = transform.objectToString(config, {
22222 lineReturn: lineReturn,
22223 outDentRegExp: outDentRegExp,
22224 quote: options && options.quote
22227 //Add in the base indenting level.
22228 configString = applyIndent(configString, baseIndent, lineReturn);
22230 return startString + configString + fileContents.substring(end);
22234 * Tries converting a JS object to a string. This will likely suck, and
22235 * is tailored to the type of config expected in a loader config call.
22236 * So, hasOwnProperty fields, strings, numbers, arrays and functions,
22237 * no weird recursively referenced stuff.
22238 * @param {Object} obj the object to convert
22239 * @param {Object} options options object with the following values:
22240 * {String} indent the indentation to use for each level
22241 * {String} lineReturn the type of line return to use
22242 * {outDentRegExp} outDentRegExp the regexp to use to outdent functions
22243 * {String} quote the quote type to use, ' or ". Optional. Default is "
22244 * @param {String} totalIndent the total indent to print for this level
22245 * @return {String} a string representation of the object.
22247 objectToString: function (obj, options, totalIndent) {
22248 var startBrace, endBrace, nextIndent,
22251 lineReturn = options.lineReturn,
22252 indent = options.indent,
22253 outDentRegExp = options.outDentRegExp,
22254 quote = options.quote || '"';
22256 totalIndent = totalIndent || '';
22257 nextIndent = totalIndent + indent;
22259 if (obj === null) {
22261 } else if (obj === undefined) {
22262 value = 'undefined';
22263 } else if (typeof obj === 'number' || typeof obj === 'boolean') {
22265 } else if (typeof obj === 'string') {
22266 //Use double quotes in case the config may also work as JSON.
22267 value = quote + lang.jsEscape(obj) + quote;
22268 } else if (lang.isArray(obj)) {
22269 lang.each(obj, function (item, i) {
22270 value += (i !== 0 ? ',' + lineReturn : '' ) +
22272 transform.objectToString(item,
22279 } else if (lang.isFunction(obj) || lang.isRegExp(obj)) {
22280 //The outdent regexp just helps pretty up the conversion
22281 //just in node. Rhino strips comments and does a different
22282 //indent scheme for Function toString, so not really helpful
22284 value = obj.toString().replace(outDentRegExp, '$1');
22287 lang.eachProp(obj, function (v, prop) {
22288 value += (first ? '': ',' + lineReturn) +
22290 (keyRegExp.test(prop) ? prop : quote + lang.jsEscape(prop) + quote )+
22292 transform.objectToString(v,
22302 value = startBrace +
22305 lineReturn + totalIndent +
22316 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
22317 * Available via the MIT or new BSD license.
22318 * see: http://github.com/jrburke/requirejs for details
22321 /*jslint regexp: true, plusplus: true */
22322 /*global define: false */
22324 define('pragma', ['parse', 'logger'], function (parse, logger) {
22328 function create(obj, mixin) {
22329 Temp.prototype = obj;
22330 var temp = new Temp(), prop;
22332 //Avoid any extra memory hanging around
22333 Temp.prototype = null;
22336 for (prop in mixin) {
22337 if (mixin.hasOwnProperty(prop) && !temp.hasOwnProperty(prop)) {
22338 temp[prop] = mixin[prop];
22343 return temp; // Object
22347 conditionalRegExp: /(exclude|include)Start\s*\(\s*["'](\w+)["']\s*,(.*)\)/,
22348 useStrictRegExp: /['"]use strict['"];/g,
22349 hasRegExp: /has\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
22350 configRegExp: /(^|[^\.])(requirejs|require)(\.config)\s*\(/g,
22351 nsWrapRegExp: /\/\*requirejs namespace: true \*\//,
22352 apiDefRegExp: /var requirejs, require, define;/,
22353 defineCheckRegExp: /typeof\s+define\s*===\s*["']function["']\s*&&\s*define\s*\.\s*amd/g,
22354 defineStringCheckRegExp: /typeof\s+define\s*===\s*["']function["']\s*&&\s*define\s*\[\s*["']amd["']\s*\]/g,
22355 defineTypeFirstCheckRegExp: /\s*["']function["']\s*===\s*typeof\s+define\s*&&\s*define\s*\.\s*amd/g,
22356 defineJQueryRegExp: /typeof\s+define\s*===\s*["']function["']\s*&&\s*define\s*\.\s*amd\s*&&\s*define\s*\.\s*amd\s*\.\s*jQuery/g,
22357 defineHasRegExp: /typeof\s+define\s*==(=)?\s*['"]function['"]\s*&&\s*typeof\s+define\.amd\s*==(=)?\s*['"]object['"]\s*&&\s*define\.amd/g,
22358 defineTernaryRegExp: /typeof\s+define\s*===\s*['"]function["']\s*&&\s*define\s*\.\s*amd\s*\?\s*define/,
22359 amdefineRegExp: /if\s*\(\s*typeof define\s*\!==\s*'function'\s*\)\s*\{\s*[^\{\}]+amdefine[^\{\}]+\}/g,
22361 removeStrict: function (contents, config) {
22362 return config.useStrict ? contents : contents.replace(pragma.useStrictRegExp, '');
22365 namespace: function (fileContents, ns, onLifecycleName) {
22367 //Namespace require/define calls
22368 fileContents = fileContents.replace(pragma.configRegExp, '$1' + ns + '.$2$3(');
22371 fileContents = parse.renameNamespace(fileContents, ns);
22373 //Namespace define ternary use:
22374 fileContents = fileContents.replace(pragma.defineTernaryRegExp,
22375 "typeof " + ns + ".define === 'function' && " + ns + ".define.amd ? " + ns + ".define");
22377 //Namespace define jquery use:
22378 fileContents = fileContents.replace(pragma.defineJQueryRegExp,
22379 "typeof " + ns + ".define === 'function' && " + ns + ".define.amd && " + ns + ".define.amd.jQuery");
22381 //Namespace has.js define use:
22382 fileContents = fileContents.replace(pragma.defineHasRegExp,
22383 "typeof " + ns + ".define === 'function' && typeof " + ns + ".define.amd === 'object' && " + ns + ".define.amd");
22385 //Namespace define checks.
22386 //Do these ones last, since they are a subset of the more specific
22388 fileContents = fileContents.replace(pragma.defineCheckRegExp,
22389 "typeof " + ns + ".define === 'function' && " + ns + ".define.amd");
22390 fileContents = fileContents.replace(pragma.defineStringCheckRegExp,
22391 "typeof " + ns + ".define === 'function' && " + ns + ".define['amd']");
22392 fileContents = fileContents.replace(pragma.defineTypeFirstCheckRegExp,
22393 "'function' === typeof " + ns + ".define && " + ns + ".define.amd");
22395 //Check for require.js with the require/define definitions
22396 if (pragma.apiDefRegExp.test(fileContents) &&
22397 fileContents.indexOf("if (!" + ns + " || !" + ns + ".requirejs)") === -1) {
22398 //Wrap the file contents in a typeof check, and a function
22399 //to contain the API globals.
22400 fileContents = "var " + ns + ";(function () { if (!" + ns + " || !" + ns + ".requirejs) {\n" +
22401 "if (!" + ns + ") { " + ns + ' = {}; } else { require = ' + ns + '; }\n' +
22404 ns + ".requirejs = requirejs;" +
22405 ns + ".require = require;" +
22406 ns + ".define = define;\n" +
22410 //Finally, if the file wants a special wrapper because it ties
22411 //in to the requirejs internals in a way that would not fit
22412 //the above matches, do that. Look for /*requirejs namespace: true*/
22413 if (pragma.nsWrapRegExp.test(fileContents)) {
22414 //Remove the pragma.
22415 fileContents = fileContents.replace(pragma.nsWrapRegExp, '');
22417 //Alter the contents.
22418 fileContents = '(function () {\n' +
22419 'var require = ' + ns + '.require,' +
22420 'requirejs = ' + ns + '.requirejs,' +
22421 'define = ' + ns + '.define;\n' +
22427 return fileContents;
22431 * processes the fileContents for some //>> conditional statements
22433 process: function (fileName, fileContents, config, onLifecycleName, pluginCollector) {
22434 /*jslint evil: true */
22435 var foundIndex = -1, startIndex = 0, lineEndIndex, conditionLine,
22436 matches, type, marker, condition, isTrue, endRegExp, endMatches,
22437 endMarkerIndex, shouldInclude, startLength, lifecycleHas, deps,
22438 i, dep, moduleName, collectorMod,
22439 lifecyclePragmas, pragmas = config.pragmas, hasConfig = config.has,
22440 //Legacy arg defined to help in dojo conversion script. Remove later
22441 //when dojo no longer needs conversion:
22444 //Mix in a specific lifecycle scoped object, to allow targeting
22445 //some pragmas/has tests to only when files are saved, or at different
22446 //lifecycle events. Do not bother with kwArgs in this section, since
22447 //the old dojo kwArgs were for all points in the build lifecycle.
22448 if (onLifecycleName) {
22449 lifecyclePragmas = config['pragmas' + onLifecycleName];
22450 lifecycleHas = config['has' + onLifecycleName];
22452 if (lifecyclePragmas) {
22453 pragmas = create(pragmas || {}, lifecyclePragmas);
22456 if (lifecycleHas) {
22457 hasConfig = create(hasConfig || {}, lifecycleHas);
22461 //Replace has references if desired
22463 fileContents = fileContents.replace(pragma.hasRegExp, function (match, test) {
22464 if (hasConfig.hasOwnProperty(test)) {
22465 return !!hasConfig[test];
22471 if (!config.skipPragmas) {
22473 while ((foundIndex = fileContents.indexOf("//>>", startIndex)) !== -1) {
22474 //Found a conditional. Get the conditional line.
22475 lineEndIndex = fileContents.indexOf("\n", foundIndex);
22476 if (lineEndIndex === -1) {
22477 lineEndIndex = fileContents.length - 1;
22480 //Increment startIndex past the line so the next conditional search can be done.
22481 startIndex = lineEndIndex + 1;
22483 //Break apart the conditional.
22484 conditionLine = fileContents.substring(foundIndex, lineEndIndex + 1);
22485 matches = conditionLine.match(pragma.conditionalRegExp);
22488 marker = matches[2];
22489 condition = matches[3];
22491 //See if the condition is true.
22493 isTrue = !!eval("(" + condition + ")");
22495 throw "Error in file: " +
22497 ". Conditional comment: " +
22499 " failed with this error: " + e;
22502 //Find the endpoint marker.
22503 endRegExp = new RegExp('\\/\\/\\>\\>\\s*' + type + 'End\\(\\s*[\'"]' + marker + '[\'"]\\s*\\)', "g");
22504 endMatches = endRegExp.exec(fileContents.substring(startIndex, fileContents.length));
22506 endMarkerIndex = startIndex + endRegExp.lastIndex - endMatches[0].length;
22508 //Find the next line return based on the match position.
22509 lineEndIndex = fileContents.indexOf("\n", endMarkerIndex);
22510 if (lineEndIndex === -1) {
22511 lineEndIndex = fileContents.length - 1;
22514 //Should we include the segment?
22515 shouldInclude = ((type === "exclude" && !isTrue) || (type === "include" && isTrue));
22517 //Remove the conditional comments, and optionally remove the content inside
22518 //the conditional comments.
22519 startLength = startIndex - foundIndex;
22520 fileContents = fileContents.substring(0, foundIndex) +
22521 (shouldInclude ? fileContents.substring(startIndex, endMarkerIndex) : "") +
22522 fileContents.substring(lineEndIndex + 1, fileContents.length);
22524 //Move startIndex to foundIndex, since that is the new position in the file
22525 //where we need to look for more conditionals in the next while loop pass.
22526 startIndex = foundIndex;
22528 throw "Error in file: " +
22530 ". Cannot find end marker for conditional comment: " +
22538 //If need to find all plugin resources to optimize, do that now,
22539 //before namespacing, since the namespacing will change the API
22541 //If there is a plugin collector, scan the file for plugin resources.
22542 if (config.optimizeAllPluginResources && pluginCollector) {
22544 deps = parse.findDependencies(fileName, fileContents);
22546 for (i = 0; i < deps.length; i++) {
22548 if (dep.indexOf('!') !== -1) {
22549 moduleName = dep.split('!')[0];
22550 collectorMod = pluginCollector[moduleName];
22551 if (!collectorMod) {
22552 collectorMod = pluginCollector[moduleName] = [];
22554 collectorMod.push(dep);
22559 logger.error('Parse error looking for plugin resources in ' +
22560 fileName + ', skipping.');
22564 //Strip amdefine use for node-shared modules.
22565 fileContents = fileContents.replace(pragma.amdefineRegExp, '');
22568 if (onLifecycleName === 'OnSave' && config.namespace) {
22569 fileContents = pragma.namespace(fileContents, config.namespace, onLifecycleName);
22573 return pragma.removeStrict(fileContents, config);
22579 if(env === 'browser') {
22581 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
22582 * Available via the MIT or new BSD license.
22583 * see: http://github.com/jrburke/requirejs for details
22586 /*jslint strict: false */
22587 /*global define: false */
22589 define('browser/optimize', {});
22593 if(env === 'node') {
22595 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
22596 * Available via the MIT or new BSD license.
22597 * see: http://github.com/jrburke/requirejs for details
22600 /*jslint strict: false */
22601 /*global define: false */
22603 define('node/optimize', {});
22607 if(env === 'rhino') {
22609 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
22610 * Available via the MIT or new BSD license.
22611 * see: http://github.com/jrburke/requirejs for details
22614 /*jslint sloppy: true, plusplus: true */
22615 /*global define, java, Packages, com */
22617 define('rhino/optimize', ['logger', 'env!env/file'], function (logger, file) {
22619 //Add .reduce to Rhino so UglifyJS can run in Rhino,
22620 //inspired by https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
22621 //but rewritten for brevity, and to be good enough for use by UglifyJS.
22622 if (!Array.prototype.reduce) {
22623 Array.prototype.reduce = function (fn /*, initialValue */) {
22625 length = this.length,
22628 if (arguments.length >= 2) {
22629 accumulator = arguments[1];
22632 while (!(i in this)) {
22635 accumulator = this[i++];
22639 for (; i < length; i++) {
22641 accumulator = fn.call(undefined, accumulator, this[i], i, this);
22645 return accumulator;
22649 var JSSourceFilefromCode, optimize,
22650 mapRegExp = /"file":"[^"]+"/;
22652 //Bind to Closure compiler, but if it is not available, do not sweat it.
22654 JSSourceFilefromCode = java.lang.Class.forName('com.google.javascript.jscomp.JSSourceFile').getMethod('fromCode', [java.lang.String, java.lang.String]);
22657 //Helper for closure compiler, because of weird Java-JavaScript interactions.
22658 function closurefromCode(filename, content) {
22659 return JSSourceFilefromCode.invoke(null, [filename, content]);
22663 function getFileWriter(fileName, encoding) {
22664 var outFile = new java.io.File(fileName), outWriter, parentDir;
22666 parentDir = outFile.getAbsoluteFile().getParentFile();
22667 if (!parentDir.exists()) {
22668 if (!parentDir.mkdirs()) {
22669 throw "Could not create directory: " + parentDir.getAbsolutePath();
22674 outWriter = new java.io.OutputStreamWriter(new java.io.FileOutputStream(outFile), encoding);
22676 outWriter = new java.io.OutputStreamWriter(new java.io.FileOutputStream(outFile));
22679 return new java.io.BufferedWriter(outWriter);
22683 closure: function (fileName, fileContents, outFileName, keepLines, config) {
22684 config = config || {};
22685 var result, mappings, optimized, compressed, baseName, writer,
22686 outBaseName, outFileNameMap, outFileNameMapContent,
22687 jscomp = Packages.com.google.javascript.jscomp,
22688 flags = Packages.com.google.common.flags,
22690 externSourceFile = closurefromCode("fakeextern.js", " "),
22691 //Set up source input
22692 jsSourceFile = closurefromCode(String(fileName), String(fileContents)),
22693 options, option, FLAG_compilation_level, compiler,
22694 Compiler = Packages.com.google.javascript.jscomp.Compiler;
22696 logger.trace("Minifying file: " + fileName);
22698 baseName = (new java.io.File(fileName)).getName();
22701 options = new jscomp.CompilerOptions();
22702 for (option in config.CompilerOptions) {
22703 // options are false by default and jslint wanted an if statement in this for loop
22704 if (config.CompilerOptions[option]) {
22705 options[option] = config.CompilerOptions[option];
22709 options.prettyPrint = keepLines || options.prettyPrint;
22711 FLAG_compilation_level = jscomp.CompilationLevel[config.CompilationLevel || 'SIMPLE_OPTIMIZATIONS'];
22712 FLAG_compilation_level.setOptionsForCompilationLevel(options);
22714 if (config.generateSourceMaps) {
22715 mappings = new java.util.ArrayList();
22717 mappings.add(new com.google.javascript.jscomp.SourceMap.LocationMapping(fileName, baseName + ".src"));
22718 options.setSourceMapLocationMappings(mappings);
22719 options.setSourceMapOutputPath(fileName + ".map");
22722 //Trigger the compiler
22723 Compiler.setLoggingLevel(Packages.java.util.logging.Level[config.loggingLevel || 'WARNING']);
22724 compiler = new Compiler();
22726 result = compiler.compile(externSourceFile, jsSourceFile, options);
22727 if (result.success) {
22728 optimized = String(compiler.toSource());
22730 if (config.generateSourceMaps && result.sourceMap && outFileName) {
22731 outBaseName = (new java.io.File(outFileName)).getName();
22733 file.saveUtf8File(outFileName + ".src", fileContents);
22735 outFileNameMap = outFileName + ".map";
22736 writer = getFileWriter(outFileNameMap, "utf-8");
22737 result.sourceMap.appendTo(writer, outFileName);
22740 //Not sure how better to do this, but right now the .map file
22741 //leaks the full OS path in the "file" property. Manually
22742 //modify it to not do that.
22743 file.saveFile(outFileNameMap,
22744 file.readFile(outFileNameMap).replace(mapRegExp, '"file":"' + baseName + '"'));
22746 fileContents = optimized + "\n//# sourceMappingURL=" + outBaseName + ".map";
22748 fileContents = optimized;
22750 return fileContents;
22752 throw new Error('Cannot closure compile file: ' + fileName + '. Skipping it.');
22755 return fileContents;
22763 if(env === 'xpconnect') {
22764 define('xpconnect/optimize', {});
22767 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
22768 * Available via the MIT or new BSD license.
22769 * see: http://github.com/jrburke/requirejs for details
22772 /*jslint plusplus: true, nomen: true, regexp: true */
22773 /*global define: false */
22775 define('optimize', [ 'lang', 'logger', 'env!env/optimize', 'env!env/file', 'parse',
22776 'pragma', 'uglifyjs/index', 'uglifyjs2',
22778 function (lang, logger, envOptimize, file, parse,
22779 pragma, uglify, uglify2,
22784 cssImportRegExp = /\@import\s+(url\()?\s*([^);]+)\s*(\))?([\w, ]*)(;)?/ig,
22785 cssCommentImportRegExp = /\/\*[^\*]*@import[^\*]*\*\//g,
22786 cssUrlRegExp = /\url\(\s*([^\)]+)\s*\)?/g,
22787 SourceMapGenerator = sourceMap.SourceMapGenerator,
22788 SourceMapConsumer =sourceMap.SourceMapConsumer;
22791 * If an URL from a CSS url value contains start/end quotes, remove them.
22792 * This is not done in the regexp, since my regexp fu is not that strong,
22793 * and the CSS spec allows for ' and " in the URL if they are backslash escaped.
22794 * @param {String} url
22796 function cleanCssUrlQuotes(url) {
22797 //Make sure we are not ending in whitespace.
22798 //Not very confident of the css regexps above that there will not be ending
22800 url = url.replace(/\s+$/, "");
22802 if (url.charAt(0) === "'" || url.charAt(0) === "\"") {
22803 url = url.substring(1, url.length - 1);
22810 * Inlines nested stylesheets that have @import calls in them.
22811 * @param {String} fileName the file name
22812 * @param {String} fileContents the file contents
22813 * @param {String} cssImportIgnore comma delimited string of files to ignore
22814 * @param {String} cssPrefix string to be prefixed before relative URLs
22815 * @param {Object} included an object used to track the files already imported
22817 function flattenCss(fileName, fileContents, cssImportIgnore, cssPrefix, included) {
22818 //Find the last slash in the name.
22819 fileName = fileName.replace(lang.backSlashRegExp, "/");
22820 var endIndex = fileName.lastIndexOf("/"),
22821 //Make a file path based on the last slash.
22822 //If no slash, so must be just a file name. Use empty string then.
22823 filePath = (endIndex !== -1) ? fileName.substring(0, endIndex + 1) : "",
22824 //store a list of merged files
22828 //First make a pass by removing an commented out @import calls.
22829 fileContents = fileContents.replace(cssCommentImportRegExp, '');
22831 //Make sure we have a delimited ignore list to make matching faster
22832 if (cssImportIgnore && cssImportIgnore.charAt(cssImportIgnore.length - 1) !== ",") {
22833 cssImportIgnore += ",";
22836 fileContents = fileContents.replace(cssImportRegExp, function (fullMatch, urlStart, importFileName, urlEnd, mediaTypes) {
22837 //Only process media type "all" or empty media type rules.
22838 if (mediaTypes && ((mediaTypes.replace(/^\s\s*/, '').replace(/\s\s*$/, '')) !== "all")) {
22839 skippedList.push(fileName);
22843 importFileName = cleanCssUrlQuotes(importFileName);
22845 //Ignore the file import if it is part of an ignore list.
22846 if (cssImportIgnore && cssImportIgnore.indexOf(importFileName + ",") !== -1) {
22850 //Make sure we have a unix path for the rest of the operation.
22851 importFileName = importFileName.replace(lang.backSlashRegExp, "/");
22854 //if a relative path, then tack on the filePath.
22855 //If it is not a relative path, then the readFile below will fail,
22856 //and we will just skip that import.
22857 var fullImportFileName = importFileName.charAt(0) === "/" ? importFileName : filePath + importFileName,
22858 importContents = file.readFile(fullImportFileName), i,
22859 importEndIndex, importPath, fixedUrlMatch, colonIndex, parts, flat;
22861 //Skip the file if it has already been included.
22862 if (included[fullImportFileName]) {
22865 included[fullImportFileName] = true;
22867 //Make sure to flatten any nested imports.
22868 flat = flattenCss(fullImportFileName, importContents, cssImportIgnore, cssPrefix, included);
22869 importContents = flat.fileContents;
22871 if (flat.importList.length) {
22872 importList.push.apply(importList, flat.importList);
22874 if (flat.skippedList.length) {
22875 skippedList.push.apply(skippedList, flat.skippedList);
22878 //Make the full import path
22879 importEndIndex = importFileName.lastIndexOf("/");
22881 //Make a file path based on the last slash.
22882 //If no slash, so must be just a file name. Use empty string then.
22883 importPath = (importEndIndex !== -1) ? importFileName.substring(0, importEndIndex + 1) : "";
22885 //fix url() on relative import (#5)
22886 importPath = importPath.replace(/^\.\//, '');
22888 //Modify URL paths to match the path represented by this file.
22889 importContents = importContents.replace(cssUrlRegExp, function (fullMatch, urlMatch) {
22890 fixedUrlMatch = cleanCssUrlQuotes(urlMatch);
22891 fixedUrlMatch = fixedUrlMatch.replace(lang.backSlashRegExp, "/");
22893 //Only do the work for relative URLs. Skip things that start with / or have
22895 colonIndex = fixedUrlMatch.indexOf(":");
22896 if (fixedUrlMatch.charAt(0) !== "/" && (colonIndex === -1 || colonIndex > fixedUrlMatch.indexOf("/"))) {
22897 //It is a relative URL, tack on the cssPrefix and path prefix
22898 urlMatch = cssPrefix + importPath + fixedUrlMatch;
22901 logger.trace(importFileName + "\n URL not a relative URL, skipping: " + urlMatch);
22904 //Collapse .. and .
22905 parts = urlMatch.split("/");
22906 for (i = parts.length - 1; i > 0; i--) {
22907 if (parts[i] === ".") {
22908 parts.splice(i, 1);
22909 } else if (parts[i] === "..") {
22910 if (i !== 0 && parts[i - 1] !== "..") {
22911 parts.splice(i - 1, 2);
22917 return "url(" + parts.join("/") + ")";
22920 importList.push(fullImportFileName);
22921 return importContents;
22923 logger.warn(fileName + "\n Cannot inline css import, skipping: " + importFileName);
22929 importList : importList,
22930 skippedList: skippedList,
22931 fileContents : fileContents
22937 * Optimizes a file that contains JavaScript content. Optionally collects
22938 * plugin resources mentioned in a file, and then passes the content
22939 * through an minifier if one is specified via config.optimize.
22941 * @param {String} fileName the name of the file to optimize
22942 * @param {String} fileContents the contents to optimize. If this is
22943 * a null value, then fileName will be used to read the fileContents.
22944 * @param {String} outFileName the name of the file to use for the
22945 * saved optimized content.
22946 * @param {Object} config the build config object.
22947 * @param {Array} [pluginCollector] storage for any plugin resources
22950 jsFile: function (fileName, fileContents, outFileName, config, pluginCollector) {
22951 if (!fileContents) {
22952 fileContents = file.readFile(fileName);
22955 fileContents = optimize.js(fileName, fileContents, outFileName, config, pluginCollector);
22957 file.saveUtf8File(outFileName, fileContents);
22961 * Optimizes a file that contains JavaScript content. Optionally collects
22962 * plugin resources mentioned in a file, and then passes the content
22963 * through an minifier if one is specified via config.optimize.
22965 * @param {String} fileName the name of the file that matches the
22967 * @param {String} fileContents the string of JS to optimize.
22968 * @param {Object} [config] the build config object.
22969 * @param {Array} [pluginCollector] storage for any plugin resources
22972 js: function (fileName, fileContents, outFileName, config, pluginCollector) {
22973 var optFunc, optConfig,
22974 parts = (String(config.optimize)).split('.'),
22975 optimizerName = parts[0],
22976 keepLines = parts[1] === 'keepLines',
22977 licenseContents = '';
22979 config = config || {};
22981 //Apply pragmas/namespace renaming
22982 fileContents = pragma.process(fileName, fileContents, config, 'OnSave', pluginCollector);
22984 //Optimize the JS files if asked.
22985 if (optimizerName && optimizerName !== 'none') {
22986 optFunc = envOptimize[optimizerName] || optimize.optimizers[optimizerName];
22988 throw new Error('optimizer with name of "' +
22990 '" not found for this environment');
22993 optConfig = config[optimizerName] || {};
22994 if (config.generateSourceMaps) {
22995 optConfig.generateSourceMaps = !!config.generateSourceMaps;
22999 if (config.preserveLicenseComments) {
23000 //Pull out any license comments for prepending after optimization.
23002 licenseContents = parse.getLicenseComments(fileName, fileContents);
23004 throw new Error('Cannot parse file: ' + fileName + ' for comments. Skipping it. Error is:\n' + e.toString());
23008 fileContents = licenseContents + optFunc(fileName,
23014 if (config.throwWhen && config.throwWhen.optimize) {
23022 return fileContents;
23026 * Optimizes one CSS file, inlining @import calls, stripping comments, and
23027 * optionally removes line returns.
23028 * @param {String} fileName the path to the CSS file to optimize
23029 * @param {String} outFileName the path to save the optimized file.
23030 * @param {Object} config the config object with the optimizeCss and
23031 * cssImportIgnore options.
23033 cssFile: function (fileName, outFileName, config) {
23035 //Read in the file. Make sure we have a JS string.
23036 var originalFileContents = file.readFile(fileName),
23037 flat = flattenCss(fileName, originalFileContents, config.cssImportIgnore, config.cssPrefix, {}),
23038 //Do not use the flattened CSS if there was one that was skipped.
23039 fileContents = flat.skippedList.length ? originalFileContents : flat.fileContents,
23040 startIndex, endIndex, buildText, comment;
23042 if (flat.skippedList.length) {
23043 logger.warn('Cannot inline @imports for ' + fileName +
23044 ',\nthe following files had media queries in them:\n' +
23045 flat.skippedList.join('\n'));
23048 //Do comment removal.
23050 if (config.optimizeCss.indexOf(".keepComments") === -1) {
23052 //Get rid of comments.
23053 while ((startIndex = fileContents.indexOf("/*", startIndex)) !== -1) {
23054 endIndex = fileContents.indexOf("*/", startIndex + 2);
23055 if (endIndex === -1) {
23056 throw "Improper comment in CSS file: " + fileName;
23058 comment = fileContents.substring(startIndex, endIndex);
23060 if (config.preserveLicenseComments &&
23061 (comment.indexOf('license') !== -1 ||
23062 comment.indexOf('opyright') !== -1 ||
23063 comment.indexOf('(c)') !== -1)) {
23064 //Keep the comment, just increment the startIndex
23065 startIndex = endIndex;
23067 fileContents = fileContents.substring(0, startIndex) + fileContents.substring(endIndex + 2, fileContents.length);
23072 //Get rid of newlines.
23073 if (config.optimizeCss.indexOf(".keepLines") === -1) {
23074 fileContents = fileContents.replace(/[\r\n]/g, "");
23075 fileContents = fileContents.replace(/\s+/g, " ");
23076 fileContents = fileContents.replace(/\{\s/g, "{");
23077 fileContents = fileContents.replace(/\s\}/g, "}");
23079 //Remove multiple empty lines.
23080 fileContents = fileContents.replace(/(\r\n)+/g, "\r\n");
23081 fileContents = fileContents.replace(/(\n)+/g, "\n");
23084 fileContents = originalFileContents;
23085 logger.error("Could not optimized CSS file: " + fileName + ", error: " + e);
23088 file.saveUtf8File(outFileName, fileContents);
23090 //text output to stdout and/or written to build.txt file
23091 buildText = "\n"+ outFileName.replace(config.dir, "") +"\n----------------\n";
23092 flat.importList.push(fileName);
23093 buildText += flat.importList.map(function(path){
23094 return path.replace(config.dir, "");
23098 importList: flat.importList,
23099 buildText: buildText +"\n"
23104 * Optimizes CSS files, inlining @import calls, stripping comments, and
23105 * optionally removes line returns.
23106 * @param {String} startDir the path to the top level directory
23107 * @param {Object} config the config object with the optimizeCss and
23108 * cssImportIgnore options.
23110 css: function (startDir, config) {
23111 var buildText = "",
23113 shouldRemove = config.dir && config.removeCombined,
23114 i, fileName, result, fileList;
23115 if (config.optimizeCss.indexOf("standard") !== -1) {
23116 fileList = file.getFilteredFileList(startDir, /\.css$/, true);
23118 for (i = 0; i < fileList.length; i++) {
23119 fileName = fileList[i];
23120 logger.trace("Optimizing (" + config.optimizeCss + ") CSS file: " + fileName);
23121 result = optimize.cssFile(fileName, fileName, config);
23122 buildText += result.buildText;
23123 if (shouldRemove) {
23124 result.importList.pop();
23125 importList = importList.concat(result.importList);
23130 if (shouldRemove) {
23131 importList.forEach(function (path) {
23132 if (file.exists(path)) {
23133 file.deleteFile(path);
23142 uglify: function (fileName, fileContents, outFileName, keepLines, config) {
23143 var parser = uglify.parser,
23144 processor = uglify.uglify,
23145 ast, errMessage, errMatch;
23147 config = config || {};
23149 logger.trace("Uglifying file: " + fileName);
23152 ast = parser.parse(fileContents, config.strict_semicolons);
23153 if (config.no_mangle !== true) {
23154 ast = processor.ast_mangle(ast, config);
23156 ast = processor.ast_squeeze(ast, config);
23158 fileContents = processor.gen_code(ast, config);
23160 if (config.max_line_length) {
23161 fileContents = processor.split_lines(fileContents, config.max_line_length);
23164 //Add trailing semicolon to match uglifyjs command line version
23165 fileContents += ';';
23167 errMessage = e.toString();
23168 errMatch = /\nError(\r)?\n/.exec(errMessage);
23170 errMessage = errMessage.substring(0, errMatch.index);
23172 throw new Error('Cannot uglify file: ' + fileName + '. Skipping it. Error is:\n' + errMessage);
23174 return fileContents;
23176 uglify2: function (fileName, fileContents, outFileName, keepLines, config) {
23177 var result, existingMap, resultMap, finalMap, sourceIndex,
23179 existingMapPath = outFileName + '.map',
23180 baseName = fileName && fileName.split('/').pop();
23182 config = config || {};
23184 lang.mixin(uconfig, config, true);
23186 uconfig.fromString = true;
23188 if (config.generateSourceMaps && outFileName) {
23189 uconfig.outSourceMap = baseName;
23191 if (file.exists(existingMapPath)) {
23192 uconfig.inSourceMap = existingMapPath;
23193 existingMap = JSON.parse(file.readFile(existingMapPath));
23197 logger.trace("Uglify2 file: " + fileName);
23200 //var tempContents = fileContents.replace(/\/\/\# sourceMappingURL=.*$/, '');
23201 result = uglify2.minify(fileContents, uconfig, baseName + '.src.js');
23202 if (uconfig.outSourceMap && result.map) {
23203 resultMap = result.map;
23205 resultMap = JSON.parse(resultMap);
23206 finalMap = SourceMapGenerator.fromSourceMap(new SourceMapConsumer(resultMap));
23207 finalMap.applySourceMap(new SourceMapConsumer(existingMap));
23208 resultMap = finalMap.toString();
23210 file.saveFile(outFileName + '.src.js', fileContents);
23212 file.saveFile(outFileName + '.map', resultMap);
23213 fileContents = result.code + "\n//# sourceMappingURL=" + baseName + ".map";
23215 fileContents = result.code;
23218 throw new Error('Cannot uglify2 file: ' + fileName + '. Skipping it. Error is:\n' + e.toString());
23220 return fileContents;
23228 * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
23229 * Available via the MIT or new BSD license.
23230 * see: http://github.com/jrburke/requirejs for details
23233 * This file patches require.js to communicate with the build system.
23236 //Using sloppy since this uses eval for some code like plugins,
23237 //which may not be strict mode compliant. So if use strict is used
23238 //below they will have strict rules applied and may cause an error.
23239 /*jslint sloppy: true, nomen: true, plusplus: true, regexp: true */
23240 /*global require, define: true */
23242 //NOT asking for require as a dependency since the goal is to modify the
23243 //global require below
23244 define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'commonJs', 'prim'], function (
23254 var allowRun = true,
23255 hasProp = lang.hasProp,
23256 falseProp = lang.falseProp,
23257 getOwn = lang.getOwn;
23259 //This method should be called when the patches to require should take hold.
23260 return function () {
23267 pluginBuilderRegExp = /(["']?)pluginBuilder(["']?)\s*[=\:]\s*["']([^'"\s]+)["']/,
23268 oldNewContext = require.s.newContext,
23271 //create local undefined values for module and exports,
23272 //so that when files are evaled in this function they do not
23273 //see the node values used for r.js
23278 * Reset "global" build caches that are kept around between
23279 * build layer builds. Useful to do when there are multiple
23280 * top level requirejs.optimize() calls.
23282 require._cacheReset = function () {
23283 //Stored raw text caches, used by browser use.
23284 require._cachedRawText = {};
23285 //Stored cached file contents for reuse in other layers.
23286 require._cachedFileContents = {};
23287 //Store which cached files contain a require definition.
23288 require._cachedDefinesRequireUrls = {};
23290 require._cacheReset();
23293 * Makes sure the URL is something that can be supported by the
23294 * optimization tool.
23295 * @param {String} url
23296 * @returns {Boolean}
23298 require._isSupportedBuildUrl = function (url) {
23299 //Ignore URLs with protocols, hosts or question marks, means either network
23300 //access is needed to fetch it or it is too dynamic. Note that
23301 //on Windows, full paths are used for some urls, which include
23302 //the drive, like c:/something, so need to test for something other
23303 //than just a colon.
23304 if (url.indexOf("://") === -1 && url.indexOf("?") === -1 &&
23305 url.indexOf('empty:') !== 0 && url.indexOf('//') !== 0) {
23308 if (!layer.ignoredUrls[url]) {
23309 if (url.indexOf('empty:') === -1) {
23310 logger.info('Cannot optimize network URL, skipping: ' + url);
23312 layer.ignoredUrls[url] = true;
23318 function normalizeUrlWithBase(context, moduleName, url) {
23319 //Adjust the URL if it was not transformed to use baseUrl.
23320 if (require.jsExtRegExp.test(moduleName)) {
23321 url = (context.config.dir || context.config.dirBaseUrl) + url;
23326 //Overrides the new context call to add existing tracking features.
23327 require.s.newContext = function (name) {
23328 var context = oldNewContext(name),
23329 oldEnable = context.enable,
23330 moduleProto = context.Module.prototype,
23331 oldInit = moduleProto.init,
23332 oldCallPlugin = moduleProto.callPlugin;
23334 //Only do this for the context used for building.
23335 if (name === '_') {
23336 //For build contexts, do everything sync
23337 context.nextTick = function (fn) {
23341 context.needFullExec = {};
23342 context.fullExec = {};
23343 context.plugins = {};
23344 context.buildShimExports = {};
23346 //Override the shim exports function generator to just
23347 //spit out strings that can be used in the stringified
23349 context.makeShimExports = function (value) {
23351 return '(function (global) {\n' +
23352 ' return function () {\n' +
23353 ' var ret, fn;\n' +
23355 (' fn = ' + value.init.toString() + ';\n' +
23356 ' ret = fn.apply(global, arguments);\n') : '') +
23358 ' return ret || global.' + value.exports + ';\n' :
23359 ' return ret;\n') +
23367 context.enable = function (depMap, parent) {
23368 var id = depMap.id,
23369 parentId = parent && parent.map.id,
23370 needFullExec = context.needFullExec,
23371 fullExec = context.fullExec,
23372 mod = getOwn(context.registry, id);
23374 if (mod && !mod.defined) {
23375 if (parentId && getOwn(needFullExec, parentId)) {
23376 needFullExec[id] = true;
23379 } else if ((getOwn(needFullExec, id) && falseProp(fullExec, id)) ||
23380 (parentId && getOwn(needFullExec, parentId) &&
23381 falseProp(fullExec, id))) {
23382 context.require.undef(id);
23385 return oldEnable.apply(context, arguments);
23388 //Override load so that the file paths can be collected.
23389 context.load = function (moduleName, url) {
23390 /*jslint evil: true */
23391 var contents, pluginBuilderMatch, builderName,
23394 //Do not mark the url as fetched if it is
23395 //not an empty: URL, used by the optimizer.
23396 //In that case we need to be sure to call
23397 //load() for each module that is mapped to
23398 //empty: so that dependencies are satisfied
23400 if (url.indexOf('empty:') === 0) {
23401 delete context.urlFetched[url];
23404 //Only handle urls that can be inlined, so that means avoiding some
23405 //URLs like ones that require network access or may be too dynamic,
23407 if (require._isSupportedBuildUrl(url)) {
23408 //Adjust the URL if it was not transformed to use baseUrl.
23409 url = normalizeUrlWithBase(context, moduleName, url);
23411 //Save the module name to path and path to module name mappings.
23412 layer.buildPathMap[moduleName] = url;
23413 layer.buildFileToModule[url] = moduleName;
23415 if (hasProp(context.plugins, moduleName)) {
23416 //plugins need to have their source evaled as-is.
23417 context.needFullExec[moduleName] = true;
23420 prim().start(function () {
23421 if (hasProp(require._cachedFileContents, url) &&
23422 (falseProp(context.needFullExec, moduleName) ||
23423 getOwn(context.fullExec, moduleName))) {
23424 contents = require._cachedFileContents[url];
23426 //If it defines require, mark it so it can be hoisted.
23427 //Done here and in the else below, before the
23428 //else block removes code from the contents.
23430 if (!layer.existingRequireUrl && require._cachedDefinesRequireUrls[url]) {
23431 layer.existingRequireUrl = url;
23434 //Load the file contents, process for conditionals, then
23436 return require._cacheReadAsync(url).then(function (text) {
23439 if (context.config.cjsTranslate &&
23440 (!context.config.shim || !lang.hasProp(context.config.shim, moduleName))) {
23441 contents = commonJs.convert(url, contents);
23444 //If there is a read filter, run it now.
23445 if (context.config.onBuildRead) {
23446 contents = context.config.onBuildRead(moduleName, url, contents);
23449 contents = pragma.process(url, contents, context.config, 'OnExecute');
23451 //Find out if the file contains a require() definition. Need to know
23452 //this so we can inject plugins right after it, but before they are needed,
23453 //and to make sure this file is first, so that define calls work.
23455 if (!layer.existingRequireUrl && parse.definesRequire(url, contents)) {
23456 layer.existingRequireUrl = url;
23457 require._cachedDefinesRequireUrls[url] = true;
23460 throw new Error('Parse error using esprima ' +
23461 'for file: ' + url + '\n' + e1);
23463 }).then(function () {
23464 if (hasProp(context.plugins, moduleName)) {
23465 //This is a loader plugin, check to see if it has a build extension,
23466 //otherwise the plugin will act as the plugin builder too.
23467 pluginBuilderMatch = pluginBuilderRegExp.exec(contents);
23468 if (pluginBuilderMatch) {
23469 //Load the plugin builder for the plugin contents.
23470 builderName = context.makeModuleMap(pluginBuilderMatch[3],
23471 context.makeModuleMap(moduleName),
23474 return require._cacheReadAsync(context.nameToUrl(builderName));
23478 }).then(function (text) {
23481 //Parse out the require and define calls.
23482 //Do this even for plugins in case they have their own
23483 //dependencies that may be separate to how the pluginBuilder works.
23485 if (falseProp(context.needFullExec, moduleName)) {
23486 contents = parse(moduleName, url, contents, {
23487 insertNeedsDefine: true,
23488 has: context.config.has,
23489 findNestedDependencies: context.config.findNestedDependencies
23493 throw new Error('Parse error using esprima ' +
23494 'for file: ' + url + '\n' + e2);
23497 require._cachedFileContents[url] = contents;
23500 }).then(function () {
23506 //If have a string shim config, and this is
23507 //a fully executed module, try to see if
23508 //it created a variable in this eval scope
23509 if (getOwn(context.needFullExec, moduleName)) {
23510 shim = getOwn(context.config.shim, moduleName);
23511 if (shim && shim.exports) {
23512 shimExports = eval(shim.exports);
23513 if (typeof shimExports !== 'undefined') {
23514 context.buildShimExports[moduleName] = shimExports;
23519 //Need to close out completion of this module
23520 //so that listeners will get notified that it is available.
23521 context.completeLoad(moduleName);
23523 //Track which module could not complete loading.
23524 if (!e.moduleTree) {
23527 e.moduleTree.push(moduleName);
23530 }).then(null, function (eOuter) {
23532 if (!eOuter.fileName) {
23533 eOuter.fileName = url;
23538 //With unsupported URLs still need to call completeLoad to
23540 context.completeLoad(moduleName);
23544 //Marks module has having a name, and optionally executes the
23545 //callback, but only if it meets certain criteria.
23546 context.execCb = function (name, cb, args, exports) {
23547 var buildShimExports = getOwn(layer.context.buildShimExports, name);
23549 if (buildShimExports) {
23550 return buildShimExports;
23551 } else if (cb.__requireJsBuild || getOwn(layer.context.needFullExec, name)) {
23552 return cb.apply(exports, args);
23557 moduleProto.init = function (depMaps) {
23558 if (context.needFullExec[this.map.id]) {
23559 lang.each(depMaps, lang.bind(this, function (depMap) {
23560 if (typeof depMap === 'string') {
23561 depMap = context.makeModuleMap(depMap,
23562 (this.map.isDefine ? this.map : this.map.parentMap));
23565 if (!context.fullExec[depMap.id]) {
23566 context.require.undef(depMap.id);
23571 return oldInit.apply(this, arguments);
23574 moduleProto.callPlugin = function () {
23575 var map = this.map,
23576 pluginMap = context.makeModuleMap(map.prefix),
23577 pluginId = pluginMap.id,
23578 pluginMod = getOwn(context.registry, pluginId);
23580 context.plugins[pluginId] = true;
23581 context.needFullExec[pluginId] = true;
23583 //If the module is not waiting to finish being defined,
23584 //undef it and start over, to get full execution.
23585 if (falseProp(context.fullExec, pluginId) && (!pluginMod || pluginMod.defined)) {
23586 context.require.undef(pluginMap.id);
23589 return oldCallPlugin.apply(this, arguments);
23596 //Clear up the existing context so that the newContext modifications
23597 //above will be active.
23598 delete require.s.contexts._;
23600 /** Reset state for each build layer pass. */
23601 require._buildReset = function () {
23602 var oldContext = require.s.contexts._;
23604 //Clear up the existing context.
23605 delete require.s.contexts._;
23607 //Set up new context, so the layer object can hold onto it.
23610 layer = require._layer = {
23612 buildFileToModule: {},
23613 buildFilePaths: [],
23615 modulesWithNames: {},
23617 existingRequireUrl: "",
23619 context: require.s.contexts._
23622 //Return the previous context in case it is needed, like for
23623 //the basic config object.
23627 require._buildReset();
23629 //Override define() to catch modules that just define an object, so that
23630 //a dummy define call is not put in the build file for them. They do
23631 //not end up getting defined via context.execCb, so we need to catch them
23632 //at the define call.
23635 //This function signature does not have to be exact, just match what we
23637 define = function (name) {
23638 if (typeof name === "string" && falseProp(layer.needsDefine, name)) {
23639 layer.modulesWithNames[name] = true;
23641 return oldDef.apply(require, arguments);
23644 define.amd = oldDef.amd;
23646 //Add some utilities for plugins
23647 require._readFile = file.readFile;
23648 require._fileExists = function (path) {
23649 return file.exists(path);
23652 //Called when execManager runs for a dependency. Used to figure out
23653 //what order of execution.
23654 require.onResourceLoad = function (context, map) {
23658 //If build needed a full execution, indicate it
23659 //has been done now. But only do it if the context is tracking
23660 //that. Only valid for the context used in a build, not for
23661 //other contexts being run, like for useLib, plain requirejs
23662 //use in node/rhino.
23663 if (context.needFullExec && getOwn(context.needFullExec, id)) {
23664 context.fullExec[id] = true;
23669 if (falseProp(layer.pathAdded, id)) {
23670 layer.buildFilePaths.push(id);
23671 //For plugins the real path is not knowable, use the name
23672 //for both module to file and file to module mappings.
23673 layer.buildPathMap[id] = id;
23674 layer.buildFileToModule[id] = id;
23675 layer.modulesWithNames[id] = true;
23676 layer.pathAdded[id] = true;
23678 } else if (map.url && require._isSupportedBuildUrl(map.url)) {
23679 //If the url has not been added to the layer yet, and it
23680 //is from an actual file that was loaded, add it now.
23681 url = normalizeUrlWithBase(context, id, map.url);
23682 if (!layer.pathAdded[url] && getOwn(layer.buildPathMap, id)) {
23683 //Remember the list of dependencies for this layer.
23684 layer.buildFilePaths.push(url);
23685 layer.pathAdded[url] = true;
23690 //Called by output of the parse() function, when a file does not
23691 //explicitly call define, probably just require, but the parse()
23692 //function normalizes on define() for dependency mapping and file
23693 //ordering works correctly.
23694 require.needsDefine = function (moduleName) {
23695 layer.needsDefine[moduleName] = true;
23700 * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
23701 * Available via the MIT or new BSD license.
23702 * see: http://github.com/jrburke/requirejs for details
23706 /*global define: false, console: false */
23708 define('commonJs', ['env!env/file', 'parse'], function (file, parse) {
23711 //Set to false if you do not want this file to log. Useful in environments
23712 //like node where you want the work to happen without noise.
23715 convertDir: function (commonJsPath, savePath) {
23717 jsFileRegExp = /\.js$/,
23718 fileName, convertedFileName, fileContents;
23720 //Get list of files to convert.
23721 fileList = file.getFilteredFileList(commonJsPath, /\w/, true);
23723 //Normalize on front slashes and make sure the paths do not end in a slash.
23724 commonJsPath = commonJsPath.replace(/\\/g, "/");
23725 savePath = savePath.replace(/\\/g, "/");
23726 if (commonJsPath.charAt(commonJsPath.length - 1) === "/") {
23727 commonJsPath = commonJsPath.substring(0, commonJsPath.length - 1);
23729 if (savePath.charAt(savePath.length - 1) === "/") {
23730 savePath = savePath.substring(0, savePath.length - 1);
23733 //Cycle through all the JS files and convert them.
23734 if (!fileList || !fileList.length) {
23735 if (commonJs.useLog) {
23736 if (commonJsPath === "convert") {
23737 //A request just to convert one file.
23738 console.log('\n\n' + commonJs.convert(savePath, file.readFile(savePath)));
23740 console.log("No files to convert in directory: " + commonJsPath);
23744 for (i = 0; i < fileList.length; i++) {
23745 fileName = fileList[i];
23746 convertedFileName = fileName.replace(commonJsPath, savePath);
23749 if (jsFileRegExp.test(fileName)) {
23750 fileContents = file.readFile(fileName);
23751 fileContents = commonJs.convert(fileName, fileContents);
23752 file.saveUtf8File(convertedFileName, fileContents);
23754 //Just copy the file over.
23755 file.copyFile(fileName, convertedFileName, true);
23762 * Does the actual file conversion.
23764 * @param {String} fileName the name of the file.
23766 * @param {String} fileContents the contents of a file :)
23768 * @returns {String} the converted contents
23770 convert: function (fileName, fileContents) {
23771 //Strip out comments.
23774 commonJsProps = parse.usesCommonJs(fileName, fileContents);
23776 //First see if the module is not already RequireJS-formatted.
23777 if (parse.usesAmdOrRequireJs(fileName, fileContents) || !commonJsProps) {
23778 return fileContents;
23781 if (commonJsProps.dirname || commonJsProps.filename) {
23782 preamble = 'var __filename = module.uri || "", ' +
23783 '__dirname = __filename.substring(0, __filename.lastIndexOf("/") + 1); ';
23786 //Construct the wrapper boilerplate.
23787 fileContents = 'define(function (require, exports, module) {' +
23793 console.log("commonJs.convert: COULD NOT CONVERT: " + fileName + ", so skipping it. Error was: " + e);
23794 return fileContents;
23797 return fileContents;
23804 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
23805 * Available via the MIT or new BSD license.
23806 * see: http://github.com/jrburke/requirejs for details
23809 /*jslint plusplus: true, nomen: true, regexp: true */
23810 /*global define, requirejs */
23813 define('build', function (require) {
23816 var build, buildBaseConfig,
23817 lang = require('lang'),
23818 prim = require('prim'),
23819 logger = require('logger'),
23820 file = require('env!env/file'),
23821 parse = require('parse'),
23822 optimize = require('optimize'),
23823 pragma = require('pragma'),
23824 transform = require('transform'),
23825 requirePatch = require('requirePatch'),
23826 env = require('env'),
23827 commonJs = require('commonJs'),
23828 SourceMapGenerator = require('source-map/source-map-generator'),
23829 hasProp = lang.hasProp,
23830 getOwn = lang.getOwn,
23831 falseProp = lang.falseProp,
23832 endsWithSemiColonRegExp = /;\s*$/;
23834 prim.nextTick = function (fn) {
23838 //Now map require to the outermost requirejs, now that we have
23839 //local dependencies for this module. The rest of the require use is
23840 //manipulating the requirejs loader.
23841 require = requirejs;
23843 //Caching function for performance. Attached to
23844 //require so it can be reused in requirePatch.js. _cachedRawText
23845 //set up by requirePatch.js
23846 require._cacheReadAsync = function (path, encoding) {
23849 if (lang.hasProp(require._cachedRawText, path)) {
23851 d.resolve(require._cachedRawText[path]);
23854 return file.readFileAsync(path, encoding).then(function (text) {
23855 require._cachedRawText[path] = text;
23861 buildBaseConfig = {
23865 optimize: "uglify",
23866 optimizeCss: "standard.keepLines",
23869 optimizeAllPluginResources: false,
23870 findNestedDependencies: false,
23871 preserveLicenseComments: true,
23872 //By default, all files/directories are copied, unless
23873 //they match this regexp, by default just excludes .folders
23874 dirExclusionRegExp: file.dirExclusionRegExp,
23875 _buildPathToModuleIndex: {}
23879 * Some JS may not be valid if concatenated with other JS, in particular
23880 * the style of omitting semicolons and rely on ASI. Add a semicolon in
23883 function addSemiColon(text, config) {
23884 if (config.skipSemiColonInsertion || endsWithSemiColonRegExp.test(text)) {
23891 function endsWithSlash(dirName) {
23892 if (dirName.charAt(dirName.length - 1) !== "/") {
23898 //Method used by plugin writeFile calls, defined up here to avoid
23899 //jslint warning about "making a function in a loop".
23900 function makeWriteFile(namespace, layer) {
23901 function writeFile(name, contents) {
23902 logger.trace('Saving plugin-optimized file: ' + name);
23903 file.saveUtf8File(name, contents);
23906 writeFile.asModule = function (moduleName, fileName, contents) {
23907 writeFile(fileName,
23908 build.toTransport(namespace, moduleName, fileName, contents, layer));
23915 * Main API entry point into the build. The args argument can either be
23916 * an array of arguments (like the onese passed on a command-line),
23917 * or it can be a JavaScript object that has the format of a build profile
23920 * If it is an object, then in addition to the normal properties allowed in
23921 * a build profile file, the object should contain one other property:
23923 * The object could also contain a "buildFile" property, which is a string
23924 * that is the file path to a build profile that contains the rest
23925 * of the build profile directives.
23927 * This function does not return a status, it should throw an error if
23928 * there is a problem completing the build.
23930 build = function (args) {
23931 var buildFile, cmdConfig, errorMsg, errorStack, stackMatch, errorTree,
23933 stackRegExp = /( {4}at[^\n]+)\n/,
23934 standardIndent = ' ';
23936 return prim().start(function () {
23937 if (!args || lang.isArray(args)) {
23938 if (!args || args.length < 1) {
23939 logger.error("build.js buildProfile.js\n" +
23940 "where buildProfile.js is the name of the build file (see example.build.js for hints on how to make a build file).");
23944 //Next args can include a build file path as well as other build args.
23945 //build file path comes first. If it does not contain an = then it is
23946 //a build file path. Otherwise, just all build args.
23947 if (args[0].indexOf("=") === -1) {
23948 buildFile = args[0];
23952 //Remaining args are options to the build
23953 cmdConfig = build.convertArrayToObject(args);
23954 cmdConfig.buildFile = buildFile;
23959 return build._run(cmdConfig);
23960 }).then(null, function (e) {
23961 errorMsg = e.toString();
23962 errorTree = e.moduleTree;
23963 stackMatch = stackRegExp.exec(errorMsg);
23966 errorMsg += errorMsg.substring(0, stackMatch.index + stackMatch[0].length + 1);
23969 //If a module tree that shows what module triggered the error,
23971 if (errorTree && errorTree.length > 0) {
23972 errorMsg += '\nIn module tree:\n';
23974 for (i = errorTree.length - 1; i > -1; i--) {
23975 errorMod = errorTree[i];
23977 for (j = errorTree.length - i; j > -1; j--) {
23978 errorMsg += standardIndent;
23980 errorMsg += errorMod + '\n';
23984 logger.error(errorMsg);
23987 errorStack = e.stack;
23989 if (typeof args === 'string' && args.indexOf('stacktrace=true') !== -1) {
23990 errorMsg += '\n' + errorStack;
23992 if (!stackMatch && errorStack) {
23993 //Just trim out the first "at" in the stack.
23994 stackMatch = stackRegExp.exec(errorStack);
23996 errorMsg += '\n' + stackMatch[0] || '';
24001 throw new Error(errorMsg);
24005 build._run = function (cmdConfig) {
24006 var buildPaths, fileName, fileNames,
24008 baseConfig, config,
24009 modules, srcPath, buildContext,
24010 destPath, moduleMap, parentModuleMap, context,
24011 resources, resource, plugin, fileContents,
24012 pluginProcessed = {},
24013 buildFileContents = "",
24014 pluginCollector = {};
24016 return prim().start(function () {
24019 //Can now run the patches to require.js to allow it to be used for
24020 //build generation. Do it here instead of at the top of the module
24021 //because we want normal require behavior to load the build tool
24022 //then want to switch to build mode.
24025 config = build.createConfig(cmdConfig);
24026 paths = config.paths;
24028 //Remove the previous build dir, in case it contains source transforms,
24029 //like the ones done with onBuildRead and onBuildWrite.
24030 if (config.dir && !config.keepBuildDir && file.exists(config.dir)) {
24031 file.deleteFile(config.dir);
24034 if (!config.out && !config.cssIn) {
24035 //This is not just a one-off file build but a full build profile, with
24036 //lots of files to process.
24038 //First copy all the baseUrl content
24039 file.copyDir((config.appDir || config.baseUrl), config.dir, /\w/, true);
24041 //Adjust baseUrl if config.appDir is in play, and set up build output paths.
24043 if (config.appDir) {
24044 //All the paths should be inside the appDir, so just adjust
24045 //the paths to use the dirBaseUrl
24046 for (prop in paths) {
24047 if (hasProp(paths, prop)) {
24048 buildPaths[prop] = paths[prop].replace(config.appDir, config.dir);
24052 //If no appDir, then make sure to copy the other paths to this directory.
24053 for (prop in paths) {
24054 if (hasProp(paths, prop)) {
24055 //Set up build path for each path prefix, but only do so
24056 //if the path falls out of the current baseUrl
24057 if (paths[prop].indexOf(config.baseUrl) === 0) {
24058 buildPaths[prop] = paths[prop].replace(config.baseUrl, config.dirBaseUrl);
24060 buildPaths[prop] = paths[prop] === 'empty:' ? 'empty:' : prop.replace(/\./g, "/");
24062 //Make sure source path is fully formed with baseUrl,
24063 //if it is a relative URL.
24064 srcPath = paths[prop];
24065 if (srcPath.indexOf('/') !== 0 && srcPath.indexOf(':') === -1) {
24066 srcPath = config.baseUrl + srcPath;
24069 destPath = config.dirBaseUrl + buildPaths[prop];
24071 //Skip empty: paths
24072 if (srcPath !== 'empty:') {
24073 //If the srcPath is a directory, copy the whole directory.
24074 if (file.exists(srcPath) && file.isDirectory(srcPath)) {
24075 //Copy files to build area. Copy all files (the /\w/ regexp)
24076 file.copyDir(srcPath, destPath, /\w/, true);
24078 //Try a .js extension
24081 file.copyFile(srcPath, destPath);
24090 //Figure out source file location for each module layer. Do this by seeding require
24091 //with source area configuration. This is needed so that later the module layers
24092 //can be manually copied over to the source area, since the build may be
24093 //require multiple times and the above copyDir call only copies newer files.
24095 baseUrl: config.baseUrl,
24097 packagePaths: config.packagePaths,
24098 packages: config.packages
24100 buildContext = require.s.contexts._;
24101 modules = config.modules;
24104 modules.forEach(function (module) {
24106 module._sourcePath = buildContext.nameToUrl(module.name);
24107 //If the module does not exist, and this is not a "new" module layer,
24108 //as indicated by a true "create" property on the module, and
24109 //it is not a plugin-loaded resource, and there is no
24110 //'rawText' containing the module's source then throw an error.
24111 if (!file.exists(module._sourcePath) && !module.create &&
24112 module.name.indexOf('!') === -1 &&
24113 (!config.rawText || !lang.hasProp(config.rawText, module.name))) {
24114 throw new Error("ERROR: module path does not exist: " +
24115 module._sourcePath + " for module named: " + module.name +
24116 ". Path is relative to: " + file.absPath('.'));
24123 //Just set up the _buildPath for the module layer.
24125 if (!config.cssIn) {
24126 config.modules[0]._buildPath = typeof config.out === 'function' ?
24127 'FUNCTION' : config.out;
24129 } else if (!config.cssIn) {
24130 //Now set up the config for require to use the build area, and calculate the
24131 //build file locations. Pass along any config info too.
24133 baseUrl: config.dirBaseUrl,
24137 lang.mixin(baseConfig, config);
24138 require(baseConfig);
24141 modules.forEach(function (module) {
24143 module._buildPath = buildContext.nameToUrl(module.name, null);
24144 if (!module.create) {
24145 file.copyFile(module._sourcePath, module._buildPath);
24152 //Run CSS optimizations before doing JS module tracing, to allow
24153 //things like text loader plugins loading CSS to get the optimized
24155 if (config.optimizeCss && config.optimizeCss !== "none" && config.dir) {
24156 buildFileContents += optimize.css(config.dir, config);
24158 }).then(function() {
24159 baseConfig = lang.deeplikeCopy(require.s.contexts._.config);
24160 }).then(function () {
24164 actions = modules.map(function (module, i) {
24165 return function () {
24166 //Save off buildPath to module index in a hash for quicker
24168 config._buildPathToModuleIndex[file.normalize(module._buildPath)] = i;
24170 //Call require to calculate dependencies.
24171 return build.traceDependencies(module, config, baseConfig)
24172 .then(function (layer) {
24173 module.layer = layer;
24178 return prim.serial(actions);
24180 }).then(function () {
24184 //Now build up shadow layers for anything that should be excluded.
24185 //Do this after tracing dependencies for each module, in case one
24186 //of those modules end up being one of the excluded values.
24187 actions = modules.map(function (module) {
24188 return function () {
24189 if (module.exclude) {
24190 module.excludeLayers = [];
24191 return prim.serial(module.exclude.map(function (exclude, i) {
24192 return function () {
24193 //See if it is already in the list of modules.
24194 //If not trace dependencies for it.
24195 var found = build.findBuildModule(exclude, modules);
24197 module.excludeLayers[i] = found;
24199 return build.traceDependencies({name: exclude}, config, baseConfig)
24200 .then(function (layer) {
24201 module.excludeLayers[i] = { layer: layer };
24210 return prim.serial(actions);
24212 }).then(function () {
24214 return prim.serial(modules.map(function (module) {
24215 return function () {
24216 if (module.exclude) {
24217 //module.exclude is an array of module names. For each one,
24218 //get the nested dependencies for it via a matching entry
24219 //in the module.excludeLayers array.
24220 module.exclude.forEach(function (excludeModule, i) {
24221 var excludeLayer = module.excludeLayers[i].layer,
24222 map = excludeLayer.buildFileToModule;
24223 excludeLayer.buildFilePaths.forEach(function(filePath){
24224 build.removeModulePath(map[filePath], filePath, module.layer);
24228 if (module.excludeShallow) {
24229 //module.excludeShallow is an array of module names.
24230 //shallow exclusions are just that module itself, and not
24231 //its nested dependencies.
24232 module.excludeShallow.forEach(function (excludeShallowModule) {
24233 var path = getOwn(module.layer.buildPathMap, excludeShallowModule);
24235 build.removeModulePath(excludeShallowModule, path, module.layer);
24240 //Flatten them and collect the build output for each module.
24241 return build.flattenModule(module, module.layer, config).then(function (builtModule) {
24242 var finalText, baseName;
24243 //Save it to a temp file for now, in case there are other layers that
24244 //contain optimized content that should not be included in later
24245 //layer optimizations. See issue #56.
24246 if (module._buildPath === 'FUNCTION') {
24247 module._buildText = builtModule.text;
24248 module._buildSourceMap = builtModule.sourceMap;
24250 finalText = builtModule.text;
24251 if (builtModule.sourceMap) {
24252 baseName = module._buildPath.split('/');
24253 baseName = baseName.pop();
24254 finalText += '\n//# sourceMappingURL=' + baseName + '.map';
24255 file.saveUtf8File(module._buildPath + '.map', builtModule.sourceMap);
24257 file.saveUtf8File(module._buildPath + '-temp', finalText);
24260 buildFileContents += builtModule.buildText;
24265 }).then(function () {
24268 //Now move the build layers to their final position.
24269 modules.forEach(function (module) {
24270 var finalPath = module._buildPath;
24271 if (finalPath !== 'FUNCTION') {
24272 if (file.exists(finalPath)) {
24273 file.deleteFile(finalPath);
24275 file.renameFile(finalPath + '-temp', finalPath);
24277 //And finally, if removeCombined is specified, remove
24278 //any of the files that were used in this layer.
24279 //Be sure not to remove other build layers.
24280 if (config.removeCombined) {
24281 module.layer.buildFilePaths.forEach(function (path) {
24282 var isLayer = modules.some(function (mod) {
24283 return mod._buildPath === path;
24285 relPath = build.makeRelativeFilePath(config.dir, path);
24287 if (file.exists(path) &&
24288 // not a build layer target
24290 // not outside the build directory
24291 relPath.indexOf('..') !== 0) {
24292 file.deleteFile(path);
24298 //Signal layer is done
24299 if (config.onModuleBundleComplete) {
24300 config.onModuleBundleComplete(module.onCompleteData);
24305 //If removeCombined in play, remove any empty directories that
24306 //may now exist because of its use
24307 if (config.removeCombined && !config.out && config.dir) {
24308 file.deleteEmptyDirs(config.dir);
24311 //Do other optimizations.
24312 if (config.out && !config.cssIn) {
24313 //Just need to worry about one JS file.
24314 fileName = config.modules[0]._buildPath;
24315 if (fileName === 'FUNCTION') {
24316 config.modules[0]._buildText = optimize.js(fileName,
24317 config.modules[0]._buildText,
24321 optimize.jsFile(fileName, null, fileName, config);
24323 } else if (!config.cssIn) {
24324 //Normal optimizations across modules.
24326 //JS optimizations.
24327 fileNames = file.getFilteredFileList(config.dir, /\.js$/, true);
24328 fileNames.forEach(function (fileName) {
24329 var cfg, override, moduleIndex;
24331 //Generate the module name from the config.dir root.
24332 moduleName = fileName.replace(config.dir, '');
24333 //Get rid of the extension
24334 moduleName = moduleName.substring(0, moduleName.length - 3);
24336 //If there is an override for a specific layer build module,
24337 //and this file is that module, mix in the override for use
24338 //by optimize.jsFile.
24339 moduleIndex = getOwn(config._buildPathToModuleIndex, fileName);
24340 //Normalize, since getOwn could have returned undefined
24341 moduleIndex = moduleIndex === 0 || moduleIndex > 0 ? moduleIndex : -1;
24343 //Try to avoid extra work if the other files do not need to
24344 //be read. Build layers should be processed at the very
24345 //least for optimization.
24346 if (moduleIndex > -1 || !config.skipDirOptimize ||
24347 config.normalizeDirDefines === "all" ||
24348 config.cjsTranslate) {
24349 //Convert the file to transport format, but without a name
24350 //inserted (by passing null for moduleName) since the files are
24351 //standalone, one module per file.
24352 fileContents = file.readFile(fileName);
24355 //For builds, if wanting cjs translation, do it now, so that
24356 //the individual modules can be loaded cross domain via
24357 //plain script tags.
24358 if (config.cjsTranslate &&
24359 (!config.shim || !lang.hasProp(config.shim, moduleName))) {
24360 fileContents = commonJs.convert(fileName, fileContents);
24363 if (moduleIndex === -1) {
24364 if (config.onBuildRead) {
24365 fileContents = config.onBuildRead(moduleName,
24370 //Only do transport normalization if this is not a build
24371 //layer (since it was already normalized) and if
24372 //normalizeDirDefines indicated all should be done.
24373 if (config.normalizeDirDefines === "all") {
24374 fileContents = build.toTransport(config.namespace,
24380 if (config.onBuildWrite) {
24381 fileContents = config.onBuildWrite(moduleName,
24387 override = moduleIndex > -1 ?
24388 config.modules[moduleIndex].override : null;
24390 cfg = build.createOverrideConfig(config, override);
24395 if (moduleIndex > -1 || !config.skipDirOptimize) {
24396 optimize.jsFile(fileName, fileContents, fileName, cfg, pluginCollector);
24401 //Normalize all the plugin resources.
24402 context = require.s.contexts._;
24404 for (moduleName in pluginCollector) {
24405 if (hasProp(pluginCollector, moduleName)) {
24406 parentModuleMap = context.makeModuleMap(moduleName);
24407 resources = pluginCollector[moduleName];
24408 for (i = 0; i < resources.length; i++) {
24409 resource = resources[i];
24410 moduleMap = context.makeModuleMap(resource, parentModuleMap);
24411 if (falseProp(context.plugins, moduleMap.prefix)) {
24412 //Set the value in context.plugins so it
24413 //will be evaluated as a full plugin.
24414 context.plugins[moduleMap.prefix] = true;
24416 //Do not bother if the plugin is not available.
24417 if (!file.exists(require.toUrl(moduleMap.prefix + '.js'))) {
24421 //Rely on the require in the build environment
24422 //to be synchronous
24423 context.require([moduleMap.prefix]);
24425 //Now that the plugin is loaded, redo the moduleMap
24426 //since the plugin will need to normalize part of the path.
24427 moduleMap = context.makeModuleMap(resource, parentModuleMap);
24430 //Only bother with plugin resources that can be handled
24431 //processed by the plugin, via support of the writeFile
24433 if (falseProp(pluginProcessed, moduleMap.id)) {
24434 //Only do the work if the plugin was really loaded.
24435 //Using an internal access because the file may
24436 //not really be loaded.
24437 plugin = getOwn(context.defined, moduleMap.prefix);
24438 if (plugin && plugin.writeFile) {
24450 pluginProcessed[moduleMap.id] = true;
24457 //console.log('PLUGIN COLLECTOR: ' + JSON.stringify(pluginCollector, null, " "));
24460 //All module layers are done, write out the build.txt file.
24461 file.saveUtf8File(config.dir + "build.txt", buildFileContents);
24464 //If just have one CSS file to optimize, do that here.
24465 if (config.cssIn) {
24466 buildFileContents += optimize.cssFile(config.cssIn, config.out, config).buildText;
24469 if (typeof config.out === 'function') {
24470 config.out(config.modules[0]._buildText);
24473 //Print out what was built into which layers.
24474 if (buildFileContents) {
24475 logger.info(buildFileContents);
24476 return buildFileContents;
24484 * Converts command line args like "paths.foo=../some/path"
24485 * result.paths = { foo: '../some/path' } where prop = paths,
24486 * name = paths.foo and value = ../some/path, so it assumes the
24487 * name=value splitting has already happened.
24489 function stringDotToObj(result, name, value) {
24490 var parts = name.split('.');
24492 parts.forEach(function (prop, i) {
24493 if (i === parts.length - 1) {
24494 result[prop] = value;
24496 if (falseProp(result, prop)) {
24499 result = result[prop];
24509 pragmasOnSave: true,
24519 build.hasDotPropMatch = function (prop) {
24521 index = prop.indexOf('.');
24523 if (index !== -1) {
24524 dotProp = prop.substring(0, index);
24525 return hasProp(build.objProps, dotProp);
24531 * Converts an array that has String members of "name=value"
24532 * into an object, where the properties on the object are the names in the array.
24533 * Also converts the strings "true" and "false" to booleans for the values.
24534 * member name/value pairs, and converts some comma-separated lists into
24536 * @param {Array} ary
24538 build.convertArrayToObject = function (ary) {
24539 var result = {}, i, separatorIndex, prop, value,
24543 "excludeShallow": true,
24544 "insertRequire": true,
24545 "stubModules": true,
24549 for (i = 0; i < ary.length; i++) {
24550 separatorIndex = ary[i].indexOf("=");
24551 if (separatorIndex === -1) {
24552 throw "Malformed name/value pair: [" + ary[i] + "]. Format should be name=value";
24555 value = ary[i].substring(separatorIndex + 1, ary[i].length);
24556 if (value === "true") {
24558 } else if (value === "false") {
24562 prop = ary[i].substring(0, separatorIndex);
24564 //Convert to array if necessary
24565 if (getOwn(needArray, prop)) {
24566 value = value.split(",");
24569 if (build.hasDotPropMatch(prop)) {
24570 stringDotToObj(result, prop, value);
24572 result[prop] = value;
24575 return result; //Object
24578 build.makeAbsPath = function (path, absFilePath) {
24579 if (!absFilePath) {
24583 //Add abspath if necessary. If path starts with a slash or has a colon,
24584 //then already is an abolute path.
24585 if (path.indexOf('/') !== 0 && path.indexOf(':') === -1) {
24586 path = absFilePath +
24587 (absFilePath.charAt(absFilePath.length - 1) === '/' ? '' : '/') +
24589 path = file.normalize(path);
24591 return path.replace(lang.backSlashRegExp, '/');
24594 build.makeAbsObject = function (props, obj, absFilePath) {
24597 for (i = 0; i < props.length; i++) {
24599 if (hasProp(obj, prop) && typeof obj[prop] === 'string') {
24600 obj[prop] = build.makeAbsPath(obj[prop], absFilePath);
24607 * For any path in a possible config, make it absolute relative
24608 * to the absFilePath passed in.
24610 build.makeAbsConfig = function (config, absFilePath) {
24611 var props, prop, i;
24613 props = ["appDir", "dir", "baseUrl"];
24614 for (i = 0; i < props.length; i++) {
24617 if (getOwn(config, prop)) {
24618 //Add abspath if necessary, make sure these paths end in
24620 if (prop === "baseUrl") {
24621 config.originalBaseUrl = config.baseUrl;
24622 if (config.appDir) {
24623 //If baseUrl with an appDir, the baseUrl is relative to
24624 //the appDir, *not* the absFilePath. appDir and dir are
24625 //made absolute before baseUrl, so this will work.
24626 config.baseUrl = build.makeAbsPath(config.originalBaseUrl, config.appDir);
24628 //The dir output baseUrl is same as regular baseUrl, both
24629 //relative to the absFilePath.
24630 config.baseUrl = build.makeAbsPath(config[prop], absFilePath);
24633 config[prop] = build.makeAbsPath(config[prop], absFilePath);
24636 config[prop] = endsWithSlash(config[prop]);
24640 build.makeAbsObject(["out", "cssIn"], config, absFilePath);
24641 build.makeAbsObject(["startFile", "endFile"], config.wrap, absFilePath);
24645 * Creates a relative path to targetPath from refPath.
24646 * Only deals with file paths, not folders. If folders,
24647 * make sure paths end in a trailing '/'.
24649 build.makeRelativeFilePath = function (refPath, targetPath) {
24650 var i, dotLength, finalParts, length,
24651 refParts = refPath.split('/'),
24652 targetParts = targetPath.split('/'),
24653 //Pull off file name
24654 targetName = targetParts.pop(),
24657 //Also pop off the ref file name to make the matches against
24658 //targetParts equivalent.
24661 length = refParts.length;
24663 for (i = 0; i < length; i += 1) {
24664 if (refParts[i] !== targetParts[i]) {
24669 //Now i is the index in which they diverge.
24670 finalParts = targetParts.slice(i);
24672 dotLength = length - i;
24673 for (i = 0; i > -1 && i < dotLength; i += 1) {
24674 dotParts.push('..');
24677 return dotParts.join('/') + (dotParts.length ? '/' : '') +
24678 finalParts.join('/') + (finalParts.length ? '/' : '') +
24682 build.nestedMix = {
24687 pragmasOnSave: true
24691 * Mixes additional source config into target config, and merges some
24692 * nested config, like paths, correctly.
24694 function mixConfig(target, source) {
24697 for (prop in source) {
24698 if (hasProp(source, prop)) {
24699 //If the value of the property is a plain object, then
24700 //allow a one-level-deep mixing of it.
24701 value = source[prop];
24702 if (typeof value === 'object' && value &&
24703 !lang.isArray(value) && !lang.isFunction(value) &&
24704 !lang.isRegExp(value)) {
24705 target[prop] = lang.mixin({}, target[prop], value, true);
24707 target[prop] = value;
24712 //Set up log level since it can affect if errors are thrown
24713 //or caught and passed to errbacks while doing config setup.
24714 if (lang.hasProp(target, 'logLevel')) {
24715 logger.logLevel(target.logLevel);
24720 * Converts a wrap.startFile or endFile to be start/end as a string.
24721 * the startFile/endFile values can be arrays.
24723 function flattenWrapFile(wrap, keyName, absFilePath) {
24724 var keyFileName = keyName + 'File';
24726 if (typeof wrap[keyName] !== 'string' && wrap[keyFileName]) {
24727 wrap[keyName] = '';
24728 if (typeof wrap[keyFileName] === 'string') {
24729 wrap[keyFileName] = [wrap[keyFileName]];
24731 wrap[keyFileName].forEach(function (fileName) {
24732 wrap[keyName] += (wrap[keyName] ? '\n' : '') +
24733 file.readFile(build.makeAbsPath(fileName, absFilePath));
24735 } else if (wrap[keyName] === null || wrap[keyName] === undefined) {
24736 //Allow missing one, just set to empty string.
24737 wrap[keyName] = '';
24738 } else if (typeof wrap[keyName] !== 'string') {
24739 throw new Error('wrap.' + keyName + ' or wrap.' + keyFileName + ' malformed');
24744 * Creates a config object for an optimization build.
24745 * It will also read the build profile if it is available, to create
24746 * the configuration.
24748 * @param {Object} cfg config options that take priority
24749 * over defaults and ones in the build file. These options could
24750 * be from a command line, for instance.
24752 * @param {Object} the created config object.
24754 build.createConfig = function (cfg) {
24755 /*jslint evil: true */
24756 var config = {}, buildFileContents, buildFileConfig, mainConfig,
24757 mainConfigFile, mainConfigPath, buildFile, absFilePath;
24759 //Make sure all paths are relative to current directory.
24760 absFilePath = file.absPath('.');
24761 build.makeAbsConfig(cfg, absFilePath);
24762 build.makeAbsConfig(buildBaseConfig, absFilePath);
24764 lang.mixin(config, buildBaseConfig);
24765 lang.mixin(config, cfg, true);
24767 //Set up log level early since it can affect if errors are thrown
24768 //or caught and passed to errbacks, even while constructing config.
24769 if (lang.hasProp(config, 'logLevel')) {
24770 logger.logLevel(config.logLevel);
24773 if (config.buildFile) {
24774 //A build file exists, load it to get more config.
24775 buildFile = file.absPath(config.buildFile);
24777 //Find the build file, and make sure it exists, if this is a build
24778 //that has a build profile, and not just command line args with an in=path
24779 if (!file.exists(buildFile)) {
24780 throw new Error("ERROR: build file does not exist: " + buildFile);
24783 absFilePath = config.baseUrl = file.absPath(file.parent(buildFile));
24785 //Load build file options.
24786 buildFileContents = file.readFile(buildFile);
24788 buildFileConfig = eval("(" + buildFileContents + ")");
24789 build.makeAbsConfig(buildFileConfig, absFilePath);
24791 //Mix in the config now so that items in mainConfigFile can
24792 //be resolved relative to them if necessary, like if appDir
24793 //is set here, but the baseUrl is in mainConfigFile. Will
24794 //re-mix in the same build config later after mainConfigFile
24795 //is processed, since build config should take priority.
24796 mixConfig(config, buildFileConfig);
24798 throw new Error("Build file " + buildFile + " is malformed: " + e);
24802 mainConfigFile = config.mainConfigFile || (buildFileConfig && buildFileConfig.mainConfigFile);
24803 if (mainConfigFile) {
24804 mainConfigFile = build.makeAbsPath(mainConfigFile, absFilePath);
24805 if (!file.exists(mainConfigFile)) {
24806 throw new Error(mainConfigFile + ' does not exist.');
24809 mainConfig = parse.findConfig(file.readFile(mainConfigFile)).config;
24810 } catch (configError) {
24811 throw new Error('The config in mainConfigFile ' +
24813 ' cannot be used because it cannot be evaluated' +
24814 ' correctly while running in the optimizer. Try only' +
24815 ' using a config that is also valid JSON, or do not use' +
24816 ' mainConfigFile and instead copy the config values needed' +
24817 ' into a build file or command line arguments given to the optimizer.\n' +
24818 'Source error from parsing: ' + mainConfigFile + ': ' + configError);
24821 mainConfigPath = mainConfigFile.substring(0, mainConfigFile.lastIndexOf('/'));
24823 //Add in some existing config, like appDir, since they can be
24824 //used inside the mainConfigFile -- paths and baseUrl are
24825 //relative to them.
24826 if (config.appDir && !mainConfig.appDir) {
24827 mainConfig.appDir = config.appDir;
24830 //If no baseUrl, then use the directory holding the main config.
24831 if (!mainConfig.baseUrl) {
24832 mainConfig.baseUrl = mainConfigPath;
24835 build.makeAbsConfig(mainConfig, mainConfigPath);
24836 mixConfig(config, mainConfig);
24840 //Mix in build file config, but only after mainConfig has been mixed in.
24841 if (buildFileConfig) {
24842 mixConfig(config, buildFileConfig);
24845 //Re-apply the override config values. Command line
24846 //args should take precedence over build file values.
24847 mixConfig(config, cfg);
24849 //Fix paths to full paths so that they can be adjusted consistently
24850 //lately to be in the output area.
24851 lang.eachProp(config.paths, function (value, prop) {
24852 if (lang.isArray(value)) {
24853 throw new Error('paths fallback not supported in optimizer. ' +
24854 'Please provide a build config path override ' +
24857 config.paths[prop] = build.makeAbsPath(value, config.baseUrl);
24860 //Set final output dir
24861 if (hasProp(config, "baseUrl")) {
24862 if (config.appDir) {
24863 config.dirBaseUrl = build.makeAbsPath(config.originalBaseUrl, config.dir);
24865 config.dirBaseUrl = config.dir || config.baseUrl;
24867 //Make sure dirBaseUrl ends in a slash, since it is
24868 //concatenated with other strings.
24869 config.dirBaseUrl = endsWithSlash(config.dirBaseUrl);
24872 //Check for errors in config
24874 throw new Error('"main" passed as an option, but the ' +
24875 'supported option is called "name".');
24877 if (config.out && !config.name && !config.modules && !config.include &&
24879 throw new Error('Missing either a "name", "include" or "modules" ' +
24882 if (config.cssIn) {
24883 if (config.dir || config.appDir) {
24884 throw new Error('cssIn is only for the output of single file ' +
24885 'CSS optimizations and is not compatible with "dir" or "appDir" configuration.');
24888 throw new Error('"out" option missing.');
24891 if (!config.cssIn && !config.baseUrl) {
24892 //Just use the current directory as the baseUrl
24893 config.baseUrl = './';
24895 if (!config.out && !config.dir) {
24896 throw new Error('Missing either an "out" or "dir" config value. ' +
24897 'If using "appDir" for a full project optimization, ' +
24898 'use "dir". If you want to optimize to one file, ' +
24901 if (config.appDir && config.out) {
24902 throw new Error('"appDir" is not compatible with "out". Use "dir" ' +
24903 'instead. appDir is used to copy whole projects, ' +
24904 'where "out" with "baseUrl" is used to just ' +
24905 'optimize to one file.');
24907 if (config.out && config.dir) {
24908 throw new Error('The "out" and "dir" options are incompatible.' +
24909 ' Use "out" if you are targeting a single file for' +
24910 ' for optimization, and "dir" if you want the appDir' +
24911 ' or baseUrl directories optimized.');
24915 // Make sure the output dir is not set to a parent of the
24916 // source dir or the same dir, as it will result in source
24918 if (config.dir === config.baseUrl ||
24919 config.dir === config.appDir ||
24920 (config.baseUrl && build.makeRelativeFilePath(config.dir,
24921 config.baseUrl).indexOf('..') !== 0) ||
24923 build.makeRelativeFilePath(config.dir, config.appDir).indexOf('..') !== 0)) {
24924 throw new Error('"dir" is set to a parent or same directory as' +
24925 ' "appDir" or "baseUrl". This can result in' +
24926 ' the deletion of source code. Stopping.');
24930 if (config.insertRequire && !lang.isArray(config.insertRequire)) {
24931 throw new Error('insertRequire should be a list of module IDs' +
24932 ' to insert in to a require([]) call.');
24935 if (config.generateSourceMaps) {
24936 if (config.preserveLicenseComments && config.optimize !== 'none') {
24937 throw new Error('Cannot use preserveLicenseComments and ' +
24938 'generateSourceMaps together. Either explcitly set ' +
24939 'preserveLicenseComments to false (default is true) or ' +
24940 'turn off generateSourceMaps. If you want source maps with ' +
24941 'license comments, see: ' +
24942 'http://requirejs.org/docs/errors.html#sourcemapcomments');
24943 } else if (config.optimize !== 'none' &&
24944 config.optimize !== 'closure' &&
24945 config.optimize !== 'uglify2') {
24946 //Allow optimize: none to pass, since it is useful when toggling
24947 //minification on and off to debug something, and it implicitly
24948 //works, since it does not need a source map.
24949 throw new Error('optimize: "' + config.optimize +
24950 '" does not support generateSourceMaps.');
24954 if ((config.name || config.include) && !config.modules) {
24955 //Just need to build one file, but may be part of a whole appDir/
24956 //baseUrl copy, but specified on the command line, so cannot do
24957 //the modules array setup. So create a modules section in that
24963 create: config.create,
24964 include: config.include,
24965 exclude: config.exclude,
24966 excludeShallow: config.excludeShallow,
24967 insertRequire: config.insertRequire,
24968 stubModules: config.stubModules
24971 delete config.stubModules;
24972 } else if (config.modules && config.out) {
24973 throw new Error('If the "modules" option is used, then there ' +
24974 'should be a "dir" option set and "out" should ' +
24975 'not be used since "out" is only for single file ' +
24976 'optimization output.');
24977 } else if (config.modules && config.name) {
24978 throw new Error('"name" and "modules" options are incompatible. ' +
24979 'Either use "name" if doing a single file ' +
24980 'optimization, or "modules" if you want to target ' +
24981 'more than one file for optimization.');
24984 if (config.out && !config.cssIn) {
24985 //Just one file to optimize.
24987 //Does not have a build file, so set up some defaults.
24988 //Optimizing CSS should not be allowed, unless explicitly
24989 //asked for on command line. In that case the only task is
24990 //to optimize a CSS file.
24991 if (!cfg.optimizeCss) {
24992 config.optimizeCss = "none";
24996 //Normalize cssPrefix
24997 if (config.cssPrefix) {
24998 //Make sure cssPrefix ends in a slash
24999 config.cssPrefix = endsWithSlash(config.cssPrefix);
25001 config.cssPrefix = '';
25004 //Cycle through modules and combine any local stubModules with
25006 if (config.modules && config.modules.length) {
25007 config.modules.forEach(function (mod) {
25008 if (config.stubModules) {
25009 mod.stubModules = config.stubModules.concat(mod.stubModules || []);
25012 //Create a hash lookup for the stubModules config to make lookup
25014 if (mod.stubModules) {
25015 mod.stubModules._byName = {};
25016 mod.stubModules.forEach(function (id) {
25017 mod.stubModules._byName[id] = true;
25023 //Get any wrap text.
25026 if (config.wrap === true) {
25027 //Use default values.
25029 start: '(function () {',
25033 flattenWrapFile(config.wrap, 'start', absFilePath);
25034 flattenWrapFile(config.wrap, 'end', absFilePath);
25037 } catch (wrapError) {
25038 throw new Error('Malformed wrap config: ' + wrapError.toString());
25041 //Do final input verification
25042 if (config.context) {
25043 throw new Error('The build argument "context" is not supported' +
25044 ' in a build. It should only be used in web' +
25048 //Set up normalizeDirDefines. If not explicitly set, if optimize "none",
25049 //set to "skip" otherwise set to "all".
25050 if (!hasProp(config, 'normalizeDirDefines')) {
25051 if (config.optimize === 'none' || config.skipDirOptimize) {
25052 config.normalizeDirDefines = 'skip';
25054 config.normalizeDirDefines = 'all';
25058 //Set file.fileExclusionRegExp if desired
25059 if (hasProp(config, 'fileExclusionRegExp')) {
25060 if (typeof config.fileExclusionRegExp === "string") {
25061 file.exclusionRegExp = new RegExp(config.fileExclusionRegExp);
25063 file.exclusionRegExp = config.fileExclusionRegExp;
25065 } else if (hasProp(config, 'dirExclusionRegExp')) {
25066 //Set file.dirExclusionRegExp if desired, this is the old
25067 //name for fileExclusionRegExp before 1.0.2. Support for backwards
25069 file.exclusionRegExp = config.dirExclusionRegExp;
25072 //Remove things that may cause problems in the build.
25073 delete config.jQuery;
25074 delete config.enforceDefine;
25075 delete config.urlArgs;
25081 * finds the module being built/optimized with the given moduleName,
25083 * @param {String} moduleName
25084 * @param {Array} modules
25085 * @returns {Object} the module object from the build profile, or null.
25087 build.findBuildModule = function (moduleName, modules) {
25089 for (i = 0; i < modules.length; i++) {
25090 module = modules[i];
25091 if (module.name === moduleName) {
25099 * Removes a module name and path from a layer, if it is supposed to be
25100 * excluded from the layer.
25101 * @param {String} moduleName the name of the module
25102 * @param {String} path the file path for the module
25103 * @param {Object} layer the layer to remove the module/path from
25105 build.removeModulePath = function (module, path, layer) {
25106 var index = layer.buildFilePaths.indexOf(path);
25107 if (index !== -1) {
25108 layer.buildFilePaths.splice(index, 1);
25113 * Uses the module build config object to trace the dependencies for the
25116 * @param {Object} module the module object from the build config info.
25117 * @param {Object} config the build config object.
25118 * @param {Object} [baseLoaderConfig] the base loader config to use for env resets.
25120 * @returns {Object} layer information about what paths and modules should
25121 * be in the flattened module.
25123 build.traceDependencies = function (module, config, baseLoaderConfig) {
25124 var include, override, layer, context, oldContext,
25133 //Reset some state set up in requirePatch.js, and clean up require's
25135 oldContext = require._buildReset();
25137 //Grab the reset layer and context after the reset, but keep the
25138 //old config to reuse in the new context.
25139 layer = require._layer;
25140 context = layer.context;
25142 //Put back basic config, use a fresh object for it.
25143 if (baseLoaderConfig) {
25144 require(lang.deeplikeCopy(baseLoaderConfig));
25147 logger.trace("\nTracing dependencies for: " + (module.name || module.out));
25148 include = module.name && !module.create ? [module.name] : [];
25149 if (module.include) {
25150 include = include.concat(module.include);
25153 //If there are overrides to basic config, set that up now.;
25154 if (module.override) {
25155 if (baseLoaderConfig) {
25156 override = build.createOverrideConfig(baseLoaderConfig, module.override);
25158 override = lang.deeplikeCopy(module.override);
25163 //Now, populate the rawText cache with any values explicitly passed in
25165 rawTextByIds = require.s.contexts._.config.rawText;
25166 if (rawTextByIds) {
25167 lang.eachProp(rawTextByIds, function (contents, id) {
25168 var url = require.toUrl(id) + '.js';
25169 require._cachedRawText[url] = contents;
25174 //Configure the callbacks to be called.
25175 deferred.reject.__requireJsBuild = true;
25177 //Use a wrapping function so can check for errors.
25178 function includeFinished(value) {
25179 //If a sync build environment, check for errors here, instead of
25180 //in the then callback below, since some errors, like two IDs pointed
25181 //to same URL but only one anon ID will leave the loader in an
25182 //unresolved state since a setTimeout cannot be used to check for
25184 var hasError = false;
25185 if (syncChecks[env.get()]) {
25187 build.checkForErrors(context);
25190 deferred.reject(e);
25195 deferred.resolve(value);
25198 includeFinished.__requireJsBuild = true;
25200 //Figure out module layer dependencies by calling require to do the work.
25201 require(include, includeFinished, deferred.reject);
25203 return deferred.promise.then(function () {
25205 if (module.override && baseLoaderConfig) {
25206 require(lang.deeplikeCopy(baseLoaderConfig));
25209 build.checkForErrors(context);
25215 build.checkForErrors = function (context) {
25216 //Check to see if it all loaded. If not, then throw, and give
25217 //a message on what is left.
25218 var id, prop, mod, errUrl, idParts, pluginId,
25220 failedPluginMap = {},
25221 failedPluginIds = [],
25224 errUrlConflicts = {},
25226 registry = context.registry;
25228 for (id in registry) {
25229 if (hasProp(registry, id) && id.indexOf('_@r') !== 0) {
25230 mod = getOwn(registry, id);
25231 if (id.indexOf('_unnormalized') === -1 && mod && mod.enabled) {
25233 errUrl = mod.map.url;
25235 if (errUrlMap[errUrl]) {
25237 //This error module has the same URL as another
25238 //error module, could be misconfiguration.
25239 if (!errUrlConflicts[errUrl]) {
25240 errUrlConflicts[errUrl] = [];
25241 //Store the original module that had the same URL.
25242 errUrlConflicts[errUrl].push(errUrlMap[errUrl]);
25244 errUrlConflicts[errUrl].push(id);
25246 errUrlMap[errUrl] = id;
25250 //Look for plugins that did not call load()
25251 idParts = id.split('!');
25252 pluginId = idParts[0];
25253 if (idParts.length > 1 && falseProp(failedPluginMap, pluginId)) {
25254 failedPluginIds.push(pluginId);
25255 failedPluginMap[pluginId] = true;
25260 if (errIds.length || failedPluginIds.length) {
25261 if (failedPluginIds.length) {
25262 errMessage += 'Loader plugin' +
25263 (failedPluginIds.length === 1 ? '' : 's') +
25265 'the load callback in the build: ' +
25266 failedPluginIds.join(', ') + '\n';
25268 errMessage += 'Module loading did not complete for: ' + errIds.join(', ');
25271 errMessage += '\nThe following modules share the same URL. This ' +
25272 'could be a misconfiguration if that URL only has ' +
25273 'one anonymous module in it:';
25274 for (prop in errUrlConflicts) {
25275 if (hasProp(errUrlConflicts, prop)) {
25276 errMessage += '\n' + prop + ': ' +
25277 errUrlConflicts[prop].join(', ');
25281 throw new Error(errMessage);
25286 build.createOverrideConfig = function (config, override) {
25287 var cfg = lang.deeplikeCopy(config),
25288 oride = lang.deeplikeCopy(override);
25290 lang.eachProp(oride, function (value, prop) {
25291 if (hasProp(build.objProps, prop)) {
25292 //An object property, merge keys. Start a new object
25293 //so that source object in config does not get modified.
25295 lang.mixin(cfg[prop], config[prop], true);
25296 lang.mixin(cfg[prop], override[prop], true);
25298 cfg[prop] = override[prop];
25306 * Uses the module build config object to create an flattened version
25307 * of the module, with deep dependencies included.
25309 * @param {Object} module the module object from the build config info.
25311 * @param {Object} layer the layer object returned from build.traceDependencies.
25313 * @param {Object} the build config object.
25315 * @returns {Object} with two properties: "text", the text of the flattened
25316 * module, and "buildText", a string of text representing which files were
25317 * included in the flattened module text.
25319 build.flattenModule = function (module, layer, config) {
25320 var fileContents, sourceMapGenerator,
25322 buildFileContents = '';
25324 return prim().start(function () {
25325 var reqIndex, currContents,
25326 moduleName, shim, packageConfig, nonPackageName,
25327 parts, builder, writeApi,
25328 namespace, namespaceWithDot, stubModulesByName,
25329 context = layer.context,
25331 onLayerEndAdded = {};
25333 //Use override settings, particularly for pragmas
25334 //Do this before the var readings since it reads config values.
25335 if (module.override) {
25336 config = build.createOverrideConfig(config, module.override);
25339 namespace = config.namespace || '';
25340 namespaceWithDot = namespace ? namespace + '.' : '';
25341 stubModulesByName = (module.stubModules && module.stubModules._byName) || {};
25343 //Start build output for the module.
25344 module.onCompleteData = {
25346 path: (config.dir ? module._buildPath.replace(config.dir, "") : module._buildPath),
25350 buildFileContents += "\n" +
25351 module.onCompleteData.path +
25352 "\n----------------\n";
25354 //If there was an existing file with require in it, hoist to the top.
25355 if (layer.existingRequireUrl) {
25356 reqIndex = layer.buildFilePaths.indexOf(layer.existingRequireUrl);
25357 if (reqIndex !== -1) {
25358 layer.buildFilePaths.splice(reqIndex, 1);
25359 layer.buildFilePaths.unshift(layer.existingRequireUrl);
25363 if (config.generateSourceMaps) {
25364 sourceMapBase = config.dir || config.baseUrl;
25365 sourceMapGenerator = new SourceMapGenerator.SourceMapGenerator({
25366 file: module._buildPath.replace(sourceMapBase, '')
25370 //Write the built module to disk, and build up the build output.
25372 return prim.serial(layer.buildFilePaths.map(function (path) {
25373 return function () {
25375 singleContents = '';
25377 moduleName = layer.buildFileToModule[path];
25378 //If the moduleName is for a package main, then update it to the
25380 packageConfig = layer.context.config.pkgs &&
25381 getOwn(layer.context.config.pkgs, moduleName);
25382 if (packageConfig) {
25383 nonPackageName = moduleName;
25384 moduleName += '/' + packageConfig.main;
25387 return prim().start(function () {
25388 //Figure out if the module is a result of a build plugin, and if so,
25389 //then delegate to that plugin.
25390 parts = context.makeModuleMap(moduleName);
25391 builder = parts.prefix && getOwn(context.defined, parts.prefix);
25393 if (builder.onLayerEnd && falseProp(onLayerEndAdded, parts.prefix)) {
25394 onLayerEnds.push(builder);
25395 onLayerEndAdded[parts.prefix] = true;
25398 if (builder.write) {
25399 writeApi = function (input) {
25400 singleContents += "\n" + addSemiColon(input, config);
25401 if (config.onBuildWrite) {
25402 singleContents = config.onBuildWrite(moduleName, path, singleContents);
25405 writeApi.asModule = function (moduleName, input) {
25406 singleContents += "\n" +
25407 addSemiColon(build.toTransport(namespace, moduleName, path, input, layer, {
25408 useSourceUrl: layer.context.config.useSourceUrl
25410 if (config.onBuildWrite) {
25411 singleContents = config.onBuildWrite(moduleName, path, singleContents);
25414 builder.write(parts.prefix, parts.name, writeApi);
25418 return prim().start(function () {
25419 if (hasProp(stubModulesByName, moduleName)) {
25420 //Just want to insert a simple module definition instead
25421 //of the source module. Useful for plugins that inline
25422 //all their resources.
25423 if (hasProp(layer.context.plugins, moduleName)) {
25424 //Slightly different content for plugins, to indicate
25425 //that dynamic loading will not work.
25426 return 'define({load: function(id){throw new Error("Dynamic load not allowed: " + id);}});';
25428 return 'define({});';
25431 return require._cacheReadAsync(path);
25433 }).then(function (text) {
25434 var hasPackageName;
25436 currContents = text;
25438 if (config.cjsTranslate &&
25439 (!config.shim || !lang.hasProp(config.shim, moduleName))) {
25440 currContents = commonJs.convert(path, currContents);
25443 if (config.onBuildRead) {
25444 currContents = config.onBuildRead(moduleName, path, currContents);
25447 if (packageConfig) {
25448 hasPackageName = (nonPackageName === parse.getNamedDefine(currContents));
25452 currContents = pragma.namespace(currContents, namespace);
25455 currContents = build.toTransport(namespace, moduleName, path, currContents, layer, {
25456 useSourceUrl: config.useSourceUrl
25459 if (packageConfig && !hasPackageName) {
25460 currContents = addSemiColon(currContents, config) + '\n';
25461 currContents += namespaceWithDot + "define('" +
25462 packageConfig.name + "', ['" + moduleName +
25463 "'], function (main) { return main; });\n";
25466 if (config.onBuildWrite) {
25467 currContents = config.onBuildWrite(moduleName, path, currContents);
25470 //Semicolon is for files that are not well formed when
25471 //concatenated with other content.
25472 singleContents += "\n" + addSemiColon(currContents, config);
25475 }).then(function () {
25476 var sourceMapPath, sourceMapLineNumber,
25477 shortPath = path.replace(config.dir, "");
25479 module.onCompleteData.included.push(shortPath);
25480 buildFileContents += shortPath + "\n";
25482 //Some files may not have declared a require module, and if so,
25483 //put in a placeholder call so the require does not try to load them
25484 //after the module is processed.
25485 //If we have a name, but no defined module, then add in the placeholder.
25486 if (moduleName && falseProp(layer.modulesWithNames, moduleName) && !config.skipModuleInsertion) {
25487 shim = config.shim && (getOwn(config.shim, moduleName) || (packageConfig && getOwn(config.shim, nonPackageName)));
25489 singleContents += '\n' + namespaceWithDot + 'define("' + moduleName + '", ' +
25490 (shim.deps && shim.deps.length ?
25491 build.makeJsArrayString(shim.deps) + ', ' : '') +
25492 (shim.exportsFn ? shim.exportsFn() : 'function(){}') +
25495 singleContents += '\n' + namespaceWithDot + 'define("' + moduleName + '", function(){});\n';
25499 //Add to the source map
25500 if (sourceMapGenerator) {
25501 sourceMapPath = build.makeRelativeFilePath(module._buildPath, path);
25502 sourceMapLineNumber = fileContents.split('\n').length - 1;
25503 lineCount = singleContents.split('\n').length;
25504 for (var i = 1; i <= lineCount; i += 1) {
25505 sourceMapGenerator.addMapping({
25507 line: sourceMapLineNumber + i,
25514 source: sourceMapPath
25518 //Store the content of the original in the source
25519 //map since other transforms later like minification
25520 //can mess up translating back to the original
25522 sourceMapGenerator.setSourceContent(sourceMapPath, singleContents);
25525 //Add the file to the final contents
25526 fileContents += singleContents;
25529 })).then(function () {
25530 if (onLayerEnds.length) {
25531 onLayerEnds.forEach(function (builder) {
25533 if (typeof module.out === 'string') {
25535 } else if (typeof module._buildPath === 'string') {
25536 path = module._buildPath;
25538 builder.onLayerEnd(function (input) {
25539 fileContents += "\n" + addSemiColon(input, config);
25547 if (module.create) {
25548 //The ID is for a created layer. Write out
25549 //a module definition for it in case the
25550 //built file is used with enforceDefine
25552 fileContents += '\n' + namespaceWithDot + 'define("' + module.name + '", function(){});\n';
25555 //Add a require at the end to kick start module execution, if that
25556 //was desired. Usually this is only specified when using small shim
25557 //loaders like almond.
25558 if (module.insertRequire) {
25559 fileContents += '\n' + namespaceWithDot + 'require(["' + module.insertRequire.join('", "') + '"]);\n';
25562 }).then(function () {
25564 text: config.wrap ?
25565 config.wrap.start + fileContents + config.wrap.end :
25567 buildText: buildFileContents,
25568 sourceMap: sourceMapGenerator ?
25569 JSON.stringify(sourceMapGenerator.toJSON(), null, ' ') :
25575 //Converts an JS array of strings to a string representation.
25576 //Not using JSON.stringify() for Rhino's sake.
25577 build.makeJsArrayString = function (ary) {
25578 return '["' + ary.map(function (item) {
25579 //Escape any double quotes, backslashes
25580 return lang.jsEscape(item);
25581 }).join('","') + '"]';
25584 build.toTransport = function (namespace, moduleName, path, contents, layer, options) {
25585 var baseUrl = layer && layer.context.config.baseUrl;
25587 function onFound(info) {
25588 //Only mark this module as having a name if not a named module,
25589 //or if a named module and the name matches expectations.
25590 if (layer && (info.needsId || info.foundId === moduleName)) {
25591 layer.modulesWithNames[moduleName] = true;
25595 //Convert path to be a local one to the baseUrl, useful for
25598 path = path.replace(baseUrl, '');
25601 return transform.toTransport(namespace, moduleName, path, contents, onFound, options);
25611 * Sets the default baseUrl for requirejs to be directory of top level
25614 function setBaseUrl(fileName) {
25615 //Use the file name's directory as the baseUrl if available.
25616 dir = fileName.replace(/\\/g, '/');
25617 if (dir.indexOf('/') !== -1) {
25618 dir = dir.split('/');
25620 dir = dir.join('/');
25621 //Make sure dir is JS-escaped, since it will be part of a JS string.
25622 exec("require({baseUrl: '" + dir.replace(/[\\"']/g, '\\$&') + "'});");
25626 function createRjsApi() {
25627 //Create a method that will run the optimzer given an object
25629 requirejs.optimize = function (config, callback, errback) {
25630 if (!loadedOptimizedLib) {
25632 loadedOptimizedLib = true;
25635 //Create the function that will be called once build modules
25636 //have been loaded.
25637 var runBuild = function (build, logger, quit) {
25638 //Make sure config has a log level, and if not,
25639 //make it "silent" by default.
25640 config.logLevel = config.hasOwnProperty('logLevel') ?
25641 config.logLevel : logger.SILENT;
25643 //Reset build internals first in case this is part
25644 //of a long-running server process that could have
25645 //exceptioned out in a bad state. It is only defined
25646 //after the first call though.
25647 if (requirejs._buildReset) {
25648 requirejs._buildReset();
25649 requirejs._cacheReset();
25652 function done(result) {
25653 //And clean up, in case something else triggers
25654 //a build in another pathway.
25655 if (requirejs._buildReset) {
25656 requirejs._buildReset();
25657 requirejs._cacheReset();
25660 // Ensure errors get propagated to the errback
25661 if (result instanceof Error) {
25668 errback = errback || function (err) {
25669 // Using console here since logger may have
25670 // turned off error logging. Since quit is
25671 // called want to be sure a message is printed.
25676 build(config).then(done, done).then(callback, errback);
25681 }, ['build', 'logger', 'env!env/quit'], runBuild);
25684 requirejs.tools = {
25685 useLib: function (contextName, callback) {
25687 callback = contextName;
25688 contextName = 'uselib';
25691 if (!useLibLoaded[contextName]) {
25693 useLibLoaded[contextName] = true;
25696 var req = requirejs({
25697 context: contextName
25700 req(['build'], function () {
25706 requirejs.define = define;
25709 //If in Node, and included via a require('requirejs'), just export and
25710 //THROW IT ON THE GROUND!
25711 if (env === 'node' && reqMain !== module) {
25712 setBaseUrl(path.resolve(reqMain ? reqMain.filename : '.'));
25716 module.exports = requirejs;
25718 } else if (env === 'browser') {
25719 //Only option is to use the API.
25720 setBaseUrl(location.href);
25723 } else if ((env === 'rhino' || env === 'xpconnect') &&
25724 //User sets up requirejsAsLib variable to indicate it is loaded
25725 //via load() to be used as a library.
25726 typeof requirejsAsLib !== 'undefined' && requirejsAsLib) {
25727 //This script is loaded via rhino's load() method, expose the
25729 setBaseUrl(fileName);
25734 if (commandOption === 'o') {
25735 //Do the optimizer work.
25739 * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
25740 * Available via the MIT or new BSD license.
25741 * see: http://github.com/jrburke/requirejs for details
25745 * Create a build.js file that has the build options you want and pass that
25746 * build file to this file to do the build. See example.build.js for more information.
25749 /*jslint strict: false, nomen: false */
25750 /*global require: false */
25753 baseUrl: require.s.contexts._.config.baseUrl,
25754 //Use a separate context than the default context so that the
25755 //build can use the default context.
25760 }, ['env!env/args', 'env!env/quit', 'logger', 'build'],
25761 function (args, quit, logger, build) {
25762 build(args).then(function () {}, function (err) {
25769 } else if (commandOption === 'v') {
25770 console.log('r.js: ' + version +
25771 ', RequireJS: ' + this.requirejsVars.require.version +
25772 ', UglifyJS2: 2.3.6, UglifyJS: 1.3.4');
25773 } else if (commandOption === 'convert') {
25776 this.requirejsVars.require(['env!env/args', 'commonJs', 'env!env/print'],
25777 function (args, commonJs, print) {
25779 var srcDir, outDir;
25783 if (!srcDir || !outDir) {
25784 print('Usage: path/to/commonjs/modules output/dir');
25788 commonJs.convertDir(args[0], args[1]);
25793 //Load the bundled libraries for use in the app.
25794 if (commandOption === 'lib') {
25798 setBaseUrl(fileName);
25800 if (exists(fileName)) {
25801 exec(readFile(fileName), fileName);
25807 }((typeof console !== 'undefined' ? console : undefined),
25808 (typeof Packages !== 'undefined' || (typeof window === 'undefined' &&
25809 typeof Components !== 'undefined' && Components.interfaces) ?
25810 Array.prototype.slice.call(arguments, 0) : []),
25811 (typeof readFile !== 'undefined' ? readFile : undefined)));