22894 lines
		
	
	
		
			756 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			22894 lines
		
	
	
		
			756 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| !function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.jade=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var nodes = require('./nodes');
 | |
| var filters = require('./filters');
 | |
| var doctypes = require('./doctypes');
 | |
| var selfClosing = require('./self-closing');
 | |
| var runtime = require('./runtime');
 | |
| var utils = require('./utils');
 | |
| var parseJSExpression = require('character-parser').parseMax;
 | |
| var isConstant = require('constantinople');
 | |
| var toConstant = require('constantinople').toConstant;
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Initialize `Compiler` with the given `node`.
 | |
|  *
 | |
|  * @param {Node} node
 | |
|  * @param {Object} options
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Compiler = module.exports = function Compiler(node, options) {
 | |
|   this.options = options = options || {};
 | |
|   this.node = node;
 | |
|   this.hasCompiledDoctype = false;
 | |
|   this.hasCompiledTag = false;
 | |
|   this.pp = options.pretty || false;
 | |
|   this.debug = false !== options.compileDebug;
 | |
|   this.indents = 0;
 | |
|   this.parentIndents = 0;
 | |
|   this.terse = false;
 | |
|   if (options.doctype) this.setDoctype(options.doctype);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Compiler prototype.
 | |
|  */
 | |
| 
 | |
| Compiler.prototype = {
 | |
| 
 | |
|   /**
 | |
|    * Compile parse tree to JavaScript.
 | |
|    *
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   compile: function(){
 | |
|     this.buf = [];
 | |
|     if (this.pp) this.buf.push("jade.indent = [];");
 | |
|     this.lastBufferedIdx = -1;
 | |
|     this.visit(this.node);
 | |
|     return this.buf.join('\n');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Sets the default doctype `name`. Sets terse mode to `true` when
 | |
|    * html 5 is used, causing self-closing tags to end with ">" vs "/>",
 | |
|    * and boolean attributes are not mirrored.
 | |
|    *
 | |
|    * @param {string} name
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   setDoctype: function(name){
 | |
|     name = name || 'default';
 | |
|     this.doctype = doctypes[name.toLowerCase()] || '<!DOCTYPE ' + name + '>';
 | |
|     this.terse = this.doctype.toLowerCase() == '<!doctype html>';
 | |
|     this.xml = 0 == this.doctype.indexOf('<?xml');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Buffer the given `str` exactly as is or with interpolation
 | |
|    *
 | |
|    * @param {String} str
 | |
|    * @param {Boolean} interpolate
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   buffer: function (str, interpolate) {
 | |
|     var self = this;
 | |
|     if (interpolate) {
 | |
|       var match = /(\\)?([#!]){((?:.|\n)*)$/.exec(str);
 | |
|       if (match) {
 | |
|         this.buffer(str.substr(0, match.index), false);
 | |
|         if (match[1]) { // escape
 | |
|           this.buffer(match[2] + '{', false);
 | |
|           this.buffer(match[3], true);
 | |
|           return;
 | |
|         } else {
 | |
|           try {
 | |
|             var rest = match[3];
 | |
|             var range = parseJSExpression(rest);
 | |
|             var code = ('!' == match[2] ? '' : 'jade.escape') + "((jade.interp = " + range.src + ") == null ? '' : jade.interp)";
 | |
|           } catch (ex) {
 | |
|             throw ex;
 | |
|             //didn't match, just as if escaped
 | |
|             this.buffer(match[2] + '{', false);
 | |
|             this.buffer(match[3], true);
 | |
|             return;
 | |
|           }
 | |
|           this.bufferExpression(code);
 | |
|           this.buffer(rest.substr(range.end + 1), true);
 | |
|           return;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     str = JSON.stringify(str);
 | |
|     str = str.substr(1, str.length - 2);
 | |
| 
 | |
|     if (this.lastBufferedIdx == this.buf.length) {
 | |
|       if (this.lastBufferedType === 'code') this.lastBuffered += ' + "';
 | |
|       this.lastBufferedType = 'text';
 | |
|       this.lastBuffered += str;
 | |
|       this.buf[this.lastBufferedIdx - 1] = 'buf.push(' + this.bufferStartChar + this.lastBuffered + '");'
 | |
|     } else {
 | |
|       this.buf.push('buf.push("' + str + '");');
 | |
|       this.lastBufferedType = 'text';
 | |
|       this.bufferStartChar = '"';
 | |
|       this.lastBuffered = str;
 | |
|       this.lastBufferedIdx = this.buf.length;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Buffer the given `src` so it is evaluated at run time
 | |
|    *
 | |
|    * @param {String} src
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   bufferExpression: function (src) {
 | |
|     var fn = Function('', 'return (' + src + ');');
 | |
|     if (isConstant(src)) {
 | |
|       return this.buffer(fn(), false)
 | |
|     }
 | |
|     if (this.lastBufferedIdx == this.buf.length) {
 | |
|       if (this.lastBufferedType === 'text') this.lastBuffered += '"';
 | |
|       this.lastBufferedType = 'code';
 | |
|       this.lastBuffered += ' + (' + src + ')';
 | |
|       this.buf[this.lastBufferedIdx - 1] = 'buf.push(' + this.bufferStartChar + this.lastBuffered + ');'
 | |
|     } else {
 | |
|       this.buf.push('buf.push(' + src + ');');
 | |
|       this.lastBufferedType = 'code';
 | |
|       this.bufferStartChar = '';
 | |
|       this.lastBuffered = '(' + src + ')';
 | |
|       this.lastBufferedIdx = this.buf.length;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Buffer an indent based on the current `indent`
 | |
|    * property and an additional `offset`.
 | |
|    *
 | |
|    * @param {Number} offset
 | |
|    * @param {Boolean} newline
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   prettyIndent: function(offset, newline){
 | |
|     offset = offset || 0;
 | |
|     newline = newline ? '\n' : '';
 | |
|     this.buffer(newline + Array(this.indents + offset).join('  '));
 | |
|     if (this.parentIndents)
 | |
|       this.buf.push("buf.push.apply(buf, jade.indent);");
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit `node`.
 | |
|    *
 | |
|    * @param {Node} node
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visit: function(node){
 | |
|     var debug = this.debug;
 | |
| 
 | |
|     if (debug) {
 | |
|       this.buf.push('jade_debug.unshift({ lineno: ' + node.line
 | |
|         + ', filename: ' + (node.filename
 | |
|           ? JSON.stringify(node.filename)
 | |
|           : 'jade_debug[0].filename')
 | |
|         + ' });');
 | |
|     }
 | |
| 
 | |
|     // Massive hack to fix our context
 | |
|     // stack for - else[ if] etc
 | |
|     if (false === node.debug && this.debug) {
 | |
|       this.buf.pop();
 | |
|       this.buf.pop();
 | |
|     }
 | |
| 
 | |
|     this.visitNode(node);
 | |
| 
 | |
|     if (debug) this.buf.push('jade_debug.shift();');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit `node`.
 | |
|    *
 | |
|    * @param {Node} node
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitNode: function(node){
 | |
|     return this['visit' + node.type](node);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit case `node`.
 | |
|    *
 | |
|    * @param {Literal} node
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitCase: function(node){
 | |
|     var _ = this.withinCase;
 | |
|     this.withinCase = true;
 | |
|     this.buf.push('switch (' + node.expr + '){');
 | |
|     this.visit(node.block);
 | |
|     this.buf.push('}');
 | |
|     this.withinCase = _;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit when `node`.
 | |
|    *
 | |
|    * @param {Literal} node
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitWhen: function(node){
 | |
|     if ('default' == node.expr) {
 | |
|       this.buf.push('default:');
 | |
|     } else {
 | |
|       this.buf.push('case ' + node.expr + ':');
 | |
|     }
 | |
|     this.visit(node.block);
 | |
|     this.buf.push('  break;');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit literal `node`.
 | |
|    *
 | |
|    * @param {Literal} node
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitLiteral: function(node){
 | |
|     this.buffer(node.str);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit all nodes in `block`.
 | |
|    *
 | |
|    * @param {Block} block
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitBlock: function(block){
 | |
|     var len = block.nodes.length
 | |
|       , escape = this.escape
 | |
|       , pp = this.pp
 | |
| 
 | |
|     // Pretty print multi-line text
 | |
|     if (pp && len > 1 && !escape && block.nodes[0].isText && block.nodes[1].isText)
 | |
|       this.prettyIndent(1, true);
 | |
| 
 | |
|     for (var i = 0; i < len; ++i) {
 | |
|       // Pretty print text
 | |
|       if (pp && i > 0 && !escape && block.nodes[i].isText && block.nodes[i-1].isText)
 | |
|         this.prettyIndent(1, false);
 | |
| 
 | |
|       this.visit(block.nodes[i]);
 | |
|       // Multiple text nodes are separated by newlines
 | |
|       if (block.nodes[i+1] && block.nodes[i].isText && block.nodes[i+1].isText)
 | |
|         this.buffer('\n');
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit a mixin's `block` keyword.
 | |
|    *
 | |
|    * @param {MixinBlock} block
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitMixinBlock: function(block){
 | |
|     if (this.pp) this.buf.push("jade.indent.push('" + Array(this.indents + 1).join('  ') + "');");
 | |
|     this.buf.push('block && block();');
 | |
|     if (this.pp) this.buf.push("jade.indent.pop();");
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit `doctype`. Sets terse mode to `true` when html 5
 | |
|    * is used, causing self-closing tags to end with ">" vs "/>",
 | |
|    * and boolean attributes are not mirrored.
 | |
|    *
 | |
|    * @param {Doctype} doctype
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitDoctype: function(doctype){
 | |
|     if (doctype && (doctype.val || !this.doctype)) {
 | |
|       this.setDoctype(doctype.val || 'default');
 | |
|     }
 | |
| 
 | |
|     if (this.doctype) this.buffer(this.doctype);
 | |
|     this.hasCompiledDoctype = true;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit `mixin`, generating a function that
 | |
|    * may be called within the template.
 | |
|    *
 | |
|    * @param {Mixin} mixin
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitMixin: function(mixin){
 | |
|     var name = 'jade_mixins[';
 | |
|     var args = mixin.args || '';
 | |
|     var block = mixin.block;
 | |
|     var attrs = mixin.attrs;
 | |
|     var attrsBlocks = mixin.attributeBlocks;
 | |
|     var pp = this.pp;
 | |
| 
 | |
|     name += (mixin.name[0]=='#' ? mixin.name.substr(2,mixin.name.length-3):'"'+mixin.name+'"')+']';
 | |
| 
 | |
|     if (mixin.call) {
 | |
|       if (pp) this.buf.push("jade.indent.push('" + Array(this.indents + 1).join('  ') + "');")
 | |
|       if (block || attrs.length || attrsBlocks.length) {
 | |
| 
 | |
|         this.buf.push(name + '.call({');
 | |
| 
 | |
|         if (block) {
 | |
|           this.buf.push('block: function(){');
 | |
| 
 | |
|           // Render block with no indents, dynamically added when rendered
 | |
|           this.parentIndents++;
 | |
|           var _indents = this.indents;
 | |
|           this.indents = 0;
 | |
|           this.visit(mixin.block);
 | |
|           this.indents = _indents;
 | |
|           this.parentIndents--;
 | |
| 
 | |
|           if (attrs.length || attrsBlocks.length) {
 | |
|             this.buf.push('},');
 | |
|           } else {
 | |
|             this.buf.push('}');
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         if (attrsBlocks.length) {
 | |
|           if (attrs.length) {
 | |
|             var val = this.attrs(attrs);
 | |
|             attrsBlocks.unshift(val);
 | |
|           }
 | |
|           this.buf.push('attributes: jade.merge([' + attrsBlocks.join(',') + '])');
 | |
|         } else if (attrs.length) {
 | |
|           var val = this.attrs(attrs);
 | |
|           this.buf.push('attributes: ' + val);
 | |
|         }
 | |
| 
 | |
|         if (args) {
 | |
|           this.buf.push('}, ' + args + ');');
 | |
|         } else {
 | |
|           this.buf.push('});');
 | |
|         }
 | |
| 
 | |
|       } else {
 | |
|         this.buf.push(name + '(' + args + ');');
 | |
|       }
 | |
|       if (pp) this.buf.push("jade.indent.pop();")
 | |
|     } else {
 | |
|       this.buf.push(name + ' = function(' + args + '){');
 | |
|       this.buf.push('var block = (this && this.block), attributes = (this && this.attributes) || {};');
 | |
|       this.parentIndents++;
 | |
|       this.visit(block);
 | |
|       this.parentIndents--;
 | |
|       this.buf.push('};');
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit `tag` buffering tag markup, generating
 | |
|    * attributes, visiting the `tag`'s code and block.
 | |
|    *
 | |
|    * @param {Tag} tag
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitTag: function(tag){
 | |
|     this.indents++;
 | |
|     var name = tag.name
 | |
|       , pp = this.pp
 | |
|       , self = this;
 | |
| 
 | |
|     function bufferName() {
 | |
|       if (tag.buffer) self.bufferExpression(name);
 | |
|       else self.buffer(name);
 | |
|     }
 | |
| 
 | |
|     if ('pre' == tag.name) this.escape = true;
 | |
| 
 | |
|     if (!this.hasCompiledTag) {
 | |
|       if (!this.hasCompiledDoctype && 'html' == name) {
 | |
|         this.visitDoctype();
 | |
|       }
 | |
|       this.hasCompiledTag = true;
 | |
|     }
 | |
| 
 | |
|     // pretty print
 | |
|     if (pp && !tag.isInline())
 | |
|       this.prettyIndent(0, true);
 | |
| 
 | |
|     if ((~selfClosing.indexOf(name) || tag.selfClosing) && !this.xml) {
 | |
|       this.buffer('<');
 | |
|       bufferName();
 | |
|       this.visitAttributes(tag.attrs, tag.attributeBlocks);
 | |
|       this.terse
 | |
|         ? this.buffer('>')
 | |
|         : this.buffer('/>');
 | |
|       // if it is non-empty throw an error
 | |
|       if (tag.block && !(tag.block.type === 'Block' && tag.block.nodes.length === 0)
 | |
|       &&  tag.block.nodes.some(function (tag) { return tag.type !== 'Text' || !/^\s*$/.test(tag.val)})) {
 | |
|         throw new Error(name + ' is self closing and should not have content.');
 | |
|       }
 | |
|     } else {
 | |
|       // Optimize attributes buffering
 | |
|       this.buffer('<');
 | |
|       bufferName();
 | |
|       this.visitAttributes(tag.attrs, tag.attributeBlocks);
 | |
|       this.buffer('>');
 | |
|       if (tag.code) this.visitCode(tag.code);
 | |
|       this.visit(tag.block);
 | |
| 
 | |
|       // pretty print
 | |
|       if (pp && !tag.isInline() && 'pre' != tag.name && !tag.canInline())
 | |
|         this.prettyIndent(0, true);
 | |
| 
 | |
|       this.buffer('</');
 | |
|       bufferName();
 | |
|       this.buffer('>');
 | |
|     }
 | |
| 
 | |
|     if ('pre' == tag.name) this.escape = false;
 | |
| 
 | |
|     this.indents--;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit `filter`, throwing when the filter does not exist.
 | |
|    *
 | |
|    * @param {Filter} filter
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitFilter: function(filter){
 | |
|     var text = filter.block.nodes.map(
 | |
|       function(node){ return node.val; }
 | |
|     ).join('\n');
 | |
|     filter.attrs = filter.attrs || {};
 | |
|     filter.attrs.filename = this.options.filename;
 | |
|     this.buffer(filters(filter.name, text, filter.attrs), true);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit `text` node.
 | |
|    *
 | |
|    * @param {Text} text
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitText: function(text){
 | |
|     this.buffer(text.val, true);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit a `comment`, only buffering when the buffer flag is set.
 | |
|    *
 | |
|    * @param {Comment} comment
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitComment: function(comment){
 | |
|     if (!comment.buffer) return;
 | |
|     if (this.pp) this.prettyIndent(1, true);
 | |
|     this.buffer('<!--' + comment.val + '-->');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit a `BlockComment`.
 | |
|    *
 | |
|    * @param {Comment} comment
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitBlockComment: function(comment){
 | |
|     if (!comment.buffer) return;
 | |
|     if (this.pp) this.prettyIndent(1, true);
 | |
|     this.buffer('<!--' + comment.val);
 | |
|     this.visit(comment.block);
 | |
|     if (this.pp) this.prettyIndent(1, true);
 | |
|     this.buffer('-->');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit `code`, respecting buffer / escape flags.
 | |
|    * If the code is followed by a block, wrap it in
 | |
|    * a self-calling function.
 | |
|    *
 | |
|    * @param {Code} code
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitCode: function(code){
 | |
|     // Wrap code blocks with {}.
 | |
|     // we only wrap unbuffered code blocks ATM
 | |
|     // since they are usually flow control
 | |
| 
 | |
|     // Buffer code
 | |
|     if (code.buffer) {
 | |
|       var val = code.val.trimLeft();
 | |
|       val = 'null == (jade.interp = '+val+') ? "" : jade.interp';
 | |
|       if (code.escape) val = 'jade.escape(' + val + ')';
 | |
|       this.bufferExpression(val);
 | |
|     } else {
 | |
|       this.buf.push(code.val);
 | |
|     }
 | |
| 
 | |
|     // Block support
 | |
|     if (code.block) {
 | |
|       if (!code.buffer) this.buf.push('{');
 | |
|       this.visit(code.block);
 | |
|       if (!code.buffer) this.buf.push('}');
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit `each` block.
 | |
|    *
 | |
|    * @param {Each} each
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitEach: function(each){
 | |
|     this.buf.push(''
 | |
|       + '// iterate ' + each.obj + '\n'
 | |
|       + ';(function(){\n'
 | |
|       + '  var $$obj = ' + each.obj + ';\n'
 | |
|       + '  if (\'number\' == typeof $$obj.length) {\n');
 | |
| 
 | |
|     if (each.alternative) {
 | |
|       this.buf.push('  if ($$obj.length) {');
 | |
|     }
 | |
| 
 | |
|     this.buf.push(''
 | |
|       + '    for (var ' + each.key + ' = 0, $$l = $$obj.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n'
 | |
|       + '      var ' + each.val + ' = $$obj[' + each.key + '];\n');
 | |
| 
 | |
|     this.visit(each.block);
 | |
| 
 | |
|     this.buf.push('    }\n');
 | |
| 
 | |
|     if (each.alternative) {
 | |
|       this.buf.push('  } else {');
 | |
|       this.visit(each.alternative);
 | |
|       this.buf.push('  }');
 | |
|     }
 | |
| 
 | |
|     this.buf.push(''
 | |
|       + '  } else {\n'
 | |
|       + '    var $$l = 0;\n'
 | |
|       + '    for (var ' + each.key + ' in $$obj) {\n'
 | |
|       + '      $$l++;'
 | |
|       + '      var ' + each.val + ' = $$obj[' + each.key + '];\n');
 | |
| 
 | |
|     this.visit(each.block);
 | |
| 
 | |
|     this.buf.push('    }\n');
 | |
|     if (each.alternative) {
 | |
|       this.buf.push('    if ($$l === 0) {');
 | |
|       this.visit(each.alternative);
 | |
|       this.buf.push('    }');
 | |
|     }
 | |
|     this.buf.push('  }\n}).call(this);\n');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Visit `attrs`.
 | |
|    *
 | |
|    * @param {Array} attrs
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   visitAttributes: function(attrs, attributeBlocks){
 | |
|     if (attributeBlocks.length) {
 | |
|       if (attrs.length) {
 | |
|         var val = this.attrs(attrs);
 | |
|         attributeBlocks.unshift(val);
 | |
|       }
 | |
|       this.bufferExpression('jade.attrs(jade.merge([' + attributeBlocks.join(',') + ']), ' + JSON.stringify(this.terse) + ')');
 | |
|     } else if (attrs.length) {
 | |
|       this.attrs(attrs, true);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Compile attributes.
 | |
|    */
 | |
| 
 | |
|   attrs: function(attrs, buffer){
 | |
|     var buf = [];
 | |
|     var classes = [];
 | |
|     var classEscaping = [];
 | |
| 
 | |
|     attrs.forEach(function(attr){
 | |
|       var key = attr.name;
 | |
|       var escaped = attr.escaped;
 | |
| 
 | |
|       if (key === 'class') {
 | |
|         classes.push(attr.val);
 | |
|         classEscaping.push(attr.escaped);
 | |
|       } else if (isConstant(attr.val)) {
 | |
|         if (buffer) {
 | |
|           this.buffer(runtime.attr(key, toConstant(attr.val), escaped, this.terse));
 | |
|         } else {
 | |
|           var val = toConstant(attr.val);
 | |
|           if (escaped && !(key.indexOf('data') === 0 && typeof val !== 'string')) {
 | |
|             val = runtime.escape(val);
 | |
|           }
 | |
|           buf.push(JSON.stringify(key) + ': ' + JSON.stringify(val));
 | |
|         }
 | |
|       } else {
 | |
|         if (buffer) {
 | |
|           this.bufferExpression('jade.attr("' + key + '", ' + attr.val + ', ' + JSON.stringify(escaped) + ', ' + JSON.stringify(this.terse) + ')');
 | |
|         } else {
 | |
|           var val = attr.val;
 | |
|           if (escaped && !(key.indexOf('data') === 0)) {
 | |
|             val = 'jade.escape(' + val + ')';
 | |
|           } else if (escaped) {
 | |
|             val = '(typeof (jade.interp = ' + val + ') == "string" ? jade.escape(jade.interp) : jade.interp)"';
 | |
|           }
 | |
|           buf.push(JSON.stringify(key) + ': ' + val);
 | |
|         }
 | |
|       }
 | |
|     }.bind(this));
 | |
|     if (buffer) {
 | |
|       if (classes.every(isConstant)) {
 | |
|         this.buffer(runtime.cls(classes.map(toConstant), classEscaping));
 | |
|       } else {
 | |
|         this.bufferExpression('jade.cls([' + classes.join(',') + '], ' + JSON.stringify(classEscaping) + ')');
 | |
|       }
 | |
|     } else {
 | |
|       if (classes.every(isConstant)) {
 | |
|         classes = JSON.stringify(runtime.joinClasses(classes.map(toConstant).map(runtime.joinClasses).map(function (cls, i) {
 | |
|           return classEscaping[i] ? runtime.escape(cls) : cls;
 | |
|         })));
 | |
|       } else if (classes.length) {
 | |
|         classes = '(jade.interp = ' + JSON.stringify(classEscaping) + ',' +
 | |
|           ' jade.joinClasses([' + classes.join(',') + '].map(jade.joinClasses).map(function (cls, i) {' +
 | |
|           '   return jade.interp[i] ? jade.escape(cls) : cls' +
 | |
|           ' }))' +
 | |
|           ')';
 | |
|       }
 | |
|       if (classes.length)
 | |
|         buf.push('"class": ' + classes);
 | |
|     }
 | |
|     return '{' + buf.join(',') + '}';
 | |
|   }
 | |
| };
 | |
| 
 | |
| },{"./doctypes":2,"./filters":3,"./nodes":16,"./runtime":24,"./self-closing":25,"./utils":26,"character-parser":33,"constantinople":34}],2:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| module.exports = {
 | |
|     'default': '<!DOCTYPE html>'
 | |
|   , 'xml': '<?xml version="1.0" encoding="utf-8" ?>'
 | |
|   , 'transitional': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
 | |
|   , 'strict': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
 | |
|   , 'frameset': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
 | |
|   , '1.1': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
 | |
|   , 'basic': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
 | |
|   , 'mobile': '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
 | |
| };
 | |
| },{}],3:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| module.exports = filter;
 | |
| function filter(name, str, options) {
 | |
|   if (typeof filter[name] === 'function') {
 | |
|     var res = filter[name](str, options);
 | |
|   } else {
 | |
|     throw new Error('unknown filter ":' + name + '"');
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| filter.exists = function (name, str, options) {
 | |
|   return typeof filter[name] === 'function';
 | |
| };
 | |
| 
 | |
| },{}],4:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| module.exports = [
 | |
|     'a'
 | |
|   , 'abbr'
 | |
|   , 'acronym'
 | |
|   , 'b'
 | |
|   , 'br'
 | |
|   , 'code'
 | |
|   , 'em'
 | |
|   , 'font'
 | |
|   , 'i'
 | |
|   , 'img'
 | |
|   , 'ins'
 | |
|   , 'kbd'
 | |
|   , 'map'
 | |
|   , 'samp'
 | |
|   , 'small'
 | |
|   , 'span'
 | |
|   , 'strong'
 | |
|   , 'sub'
 | |
|   , 'sup'
 | |
| ];
 | |
| },{}],5:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| /*!
 | |
|  * Jade
 | |
|  * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 | |
|  * MIT Licensed
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * Module dependencies.
 | |
|  */
 | |
| 
 | |
| var Parser = require('./parser')
 | |
|   , Lexer = require('./lexer')
 | |
|   , Compiler = require('./compiler')
 | |
|   , runtime = require('./runtime')
 | |
|   , addWith = require('with')
 | |
|   , fs = require('fs');
 | |
| 
 | |
| /**
 | |
|  * Expose self closing tags.
 | |
|  */
 | |
| 
 | |
| exports.selfClosing = require('./self-closing');
 | |
| 
 | |
| /**
 | |
|  * Default supported doctypes.
 | |
|  */
 | |
| 
 | |
| exports.doctypes = require('./doctypes');
 | |
| 
 | |
| /**
 | |
|  * Text filters.
 | |
|  */
 | |
| 
 | |
| exports.filters = require('./filters');
 | |
| 
 | |
| /**
 | |
|  * Utilities.
 | |
|  */
 | |
| 
 | |
| exports.utils = require('./utils');
 | |
| 
 | |
| /**
 | |
|  * Expose `Compiler`.
 | |
|  */
 | |
| 
 | |
| exports.Compiler = Compiler;
 | |
| 
 | |
| /**
 | |
|  * Expose `Parser`.
 | |
|  */
 | |
| 
 | |
| exports.Parser = Parser;
 | |
| 
 | |
| /**
 | |
|  * Expose `Lexer`.
 | |
|  */
 | |
| 
 | |
| exports.Lexer = Lexer;
 | |
| 
 | |
| /**
 | |
|  * Nodes.
 | |
|  */
 | |
| 
 | |
| exports.nodes = require('./nodes');
 | |
| 
 | |
| /**
 | |
|  * Jade runtime helpers.
 | |
|  */
 | |
| 
 | |
| exports.runtime = runtime;
 | |
| 
 | |
| /**
 | |
|  * Template function cache.
 | |
|  */
 | |
| 
 | |
| exports.cache = {};
 | |
| 
 | |
| /**
 | |
|  * Parse the given `str` of jade and return a function body.
 | |
|  *
 | |
|  * @param {String} str
 | |
|  * @param {Object} options
 | |
|  * @return {String}
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| function parse(str, options){
 | |
|   try {
 | |
|     // Parse
 | |
|     var parser = new (options.parser || Parser)(str, options.filename, options);
 | |
| 
 | |
|     // Compile
 | |
|     var compiler = new (options.compiler || Compiler)(parser.parse(), options)
 | |
|       , js = compiler.compile();
 | |
| 
 | |
|     // Debug compiler
 | |
|     if (options.debug) {
 | |
|       console.error('\nCompiled Function:\n\n\u001b[90m%s\u001b[0m', js.replace(/^/gm, '  '));
 | |
|     }
 | |
| 
 | |
|     var globals = options.globals && Array.isArray(options.globals) ? options.globals : [];
 | |
| 
 | |
|     globals.push('jade');
 | |
|     globals.push('jade_mixins');
 | |
|     globals.push('jade_debug');
 | |
|     globals.push('buf');
 | |
| 
 | |
|     return ''
 | |
|       + 'var buf = [];\n'
 | |
|       + 'var jade_mixins = {};\n'
 | |
|       + (options.self
 | |
|         ? 'var self = locals || {};\n' + js
 | |
|         : addWith('locals || {}', '\n' + js, globals)) + ';'
 | |
|       + 'return buf.join("");';
 | |
|   } catch (err) {
 | |
|     parser = parser.context();
 | |
|     runtime.rethrow(err, parser.filename, parser.lexer.lineno, parser.input);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Compile a `Function` representation of the given jade `str`.
 | |
|  *
 | |
|  * Options:
 | |
|  *
 | |
|  *   - `compileDebug` when `false` debugging code is stripped from the compiled
 | |
|        template, when it is explicitly `true`, the source code is included in
 | |
|        the compiled template for better accuracy.
 | |
|  *   - `filename` used to improve errors when `compileDebug` is not `false` and to resolve imports/extends
 | |
|  *
 | |
|  * @param {String} str
 | |
|  * @param {Options} options
 | |
|  * @return {Function}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| exports.compile = function(str, options){
 | |
|   var options = options || {}
 | |
|     , filename = options.filename
 | |
|       ? JSON.stringify(options.filename)
 | |
|       : 'undefined'
 | |
|     , fn;
 | |
| 
 | |
|   str = String(str);
 | |
| 
 | |
|   if (options.compileDebug !== false) {
 | |
|     fn = [
 | |
|         'var jade_debug = [{ lineno: 1, filename: ' + filename + ' }];'
 | |
|       , 'try {'
 | |
|       , parse(str, options)
 | |
|       , '} catch (err) {'
 | |
|       , '  jade.rethrow(err, jade_debug[0].filename, jade_debug[0].lineno' + (options.compileDebug === true ? ',' + JSON.stringify(str) : '') + ');'
 | |
|       , '}'
 | |
|     ].join('\n');
 | |
|   } else {
 | |
|     fn = parse(str, options);
 | |
|   }
 | |
|   fn = new Function('locals, jade', fn)
 | |
|   var res = function(locals){ return fn(locals, Object.create(runtime)) };
 | |
|   if (options.client) {
 | |
|     res.toString = function () {
 | |
|       var err = new Error('The `client` option is deprecated, use `jade.compileClient`');
 | |
|       console.error(err.stack || err.message);
 | |
|       return exports.compileClient(str, options);
 | |
|     };
 | |
|   }
 | |
|   return res;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Compile a JavaScript source representation of the given jade `str`.
 | |
|  *
 | |
|  * Options:
 | |
|  *
 | |
|  *   - `compileDebug` When it is `true`, the source code is included in
 | |
|        the compiled template for better error messages.
 | |
|  *   - `filename` used to improve errors when `compileDebug` is not `true` and to resolve imports/extends
 | |
|  *
 | |
|  * @param {String} str
 | |
|  * @param {Options} options
 | |
|  * @return {String}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| exports.compileClient = function(str, options){
 | |
|   var options = options || {}
 | |
|     , filename = options.filename
 | |
|       ? JSON.stringify(options.filename)
 | |
|       : 'undefined'
 | |
|     , fn;
 | |
| 
 | |
|   str = String(str);
 | |
| 
 | |
|   if (options.compileDebug) {
 | |
|     options.compileDebug = true;
 | |
|     fn = [
 | |
|         'var jade_debug = [{ lineno: 1, filename: ' + filename + ' }];'
 | |
|       , 'try {'
 | |
|       , parse(str, options)
 | |
|       , '} catch (err) {'
 | |
|       , '  jade.rethrow(err, jade_debug[0].filename, jade_debug[0].lineno, ' + JSON.stringify(str) + ');'
 | |
|       , '}'
 | |
|     ].join('\n');
 | |
|   } else {
 | |
|     options.compileDebug = false;
 | |
|     fn = parse(str, options);
 | |
|   }
 | |
| 
 | |
|   return 'function template(locals) {\n' + fn + '\n}';
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Render the given `str` of jade.
 | |
|  *
 | |
|  * Options:
 | |
|  *
 | |
|  *   - `cache` enable template caching
 | |
|  *   - `filename` filename required for `include` / `extends` and caching
 | |
|  *
 | |
|  * @param {String} str
 | |
|  * @param {Object|Function} options or fn
 | |
|  * @param {Function|undefined} fn
 | |
|  * @returns {String}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| exports.render = function(str, options, fn){
 | |
|   // support callback API
 | |
|   if ('function' == typeof options) {
 | |
|     fn = options, options = undefined;
 | |
|   }
 | |
|   if (typeof fn === 'function') {
 | |
|     var res
 | |
|     try {
 | |
|       res = exports.render(str, options);
 | |
|     } catch (ex) {
 | |
|       return fn(ex);
 | |
|     }
 | |
|     return fn(null, res);
 | |
|   }
 | |
| 
 | |
|   options = options || {};
 | |
| 
 | |
|   // cache requires .filename
 | |
|   if (options.cache && !options.filename) {
 | |
|     throw new Error('the "filename" option is required for caching');
 | |
|   }
 | |
| 
 | |
|   var path = options.filename;
 | |
|   var tmpl = options.cache
 | |
|     ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options))
 | |
|     : exports.compile(str, options);
 | |
|   return tmpl(options);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Render a Jade file at the given `path`.
 | |
|  *
 | |
|  * @param {String} path
 | |
|  * @param {Object|Function} options or callback
 | |
|  * @param {Function|undefined} fn
 | |
|  * @returns {String}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| exports.renderFile = function(path, options, fn){
 | |
|   // support callback API
 | |
|   if ('function' == typeof options) {
 | |
|     fn = options, options = undefined;
 | |
|   }
 | |
|   if (typeof fn === 'function') {
 | |
|     var res
 | |
|     try {
 | |
|       res = exports.renderFile(path, options);
 | |
|     } catch (ex) {
 | |
|       return fn(ex);
 | |
|     }
 | |
|     return fn(null, res);
 | |
|   }
 | |
| 
 | |
|   options = options || {};
 | |
| 
 | |
|   var key = path + ':string';
 | |
| 
 | |
|   options.filename = path;
 | |
|   var str = options.cache
 | |
|     ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8'))
 | |
|     : fs.readFileSync(path, 'utf8');
 | |
|   return exports.render(str, options);
 | |
| };
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Compile a Jade file at the given `path` for use on the client.
 | |
|  *
 | |
|  * @param {String} path
 | |
|  * @param {Object} options
 | |
|  * @returns {String}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| exports.compileFileClient = function(path, options){
 | |
|   options = options || {};
 | |
| 
 | |
|   var key = path + ':string';
 | |
| 
 | |
|   options.filename = path;
 | |
|   var str = options.cache
 | |
|     ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8'))
 | |
|     : fs.readFileSync(path, 'utf8');
 | |
| 
 | |
|   return exports.compileClient(str, options);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Express support.
 | |
|  */
 | |
| 
 | |
| exports.__express = exports.renderFile;
 | |
| 
 | |
| },{"./compiler":1,"./doctypes":2,"./filters":3,"./lexer":6,"./nodes":16,"./parser":23,"./runtime":24,"./self-closing":25,"./utils":26,"fs":27,"with":46}],6:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var utils = require('./utils');
 | |
| var characterParser = require('character-parser');
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Initialize `Lexer` with the given `str`.
 | |
|  *
 | |
|  * @param {String} str
 | |
|  * @param {String} filename
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| var Lexer = module.exports = function Lexer(str, filename) {
 | |
|   this.input = str.replace(/\r\n|\r/g, '\n');
 | |
|   this.filename = filename;
 | |
|   this.deferredTokens = [];
 | |
|   this.lastIndents = 0;
 | |
|   this.lineno = 1;
 | |
|   this.stash = [];
 | |
|   this.indentStack = [];
 | |
|   this.indentRe = null;
 | |
|   this.pipeless = false;
 | |
| };
 | |
| 
 | |
| 
 | |
| function assertExpression(exp) {
 | |
|   //this verifies that a JavaScript expression is valid
 | |
|   Function('', 'return (' + exp + ')');
 | |
| }
 | |
| function assertNestingCorrect(exp) {
 | |
|   //this verifies that code is properly nested, but allows
 | |
|   //invalid JavaScript such as the contents of `attributes`
 | |
|   var res = characterParser(exp)
 | |
|   if (res.isNesting()) {
 | |
|     throw new Error('Nesting must match on expression `' + exp + '`')
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Lexer prototype.
 | |
|  */
 | |
| 
 | |
| Lexer.prototype = {
 | |
| 
 | |
|   /**
 | |
|    * Construct a token with the given `type` and `val`.
 | |
|    *
 | |
|    * @param {String} type
 | |
|    * @param {String} val
 | |
|    * @return {Object}
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   tok: function(type, val){
 | |
|     return {
 | |
|         type: type
 | |
|       , line: this.lineno
 | |
|       , val: val
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Consume the given `len` of input.
 | |
|    *
 | |
|    * @param {Number} len
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   consume: function(len){
 | |
|     this.input = this.input.substr(len);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Scan for `type` with the given `regexp`.
 | |
|    *
 | |
|    * @param {String} type
 | |
|    * @param {RegExp} regexp
 | |
|    * @return {Object}
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   scan: function(regexp, type){
 | |
|     var captures;
 | |
|     if (captures = regexp.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       return this.tok(type, captures[1]);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Defer the given `tok`.
 | |
|    *
 | |
|    * @param {Object} tok
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   defer: function(tok){
 | |
|     this.deferredTokens.push(tok);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Lookahead `n` tokens.
 | |
|    *
 | |
|    * @param {Number} n
 | |
|    * @return {Object}
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   lookahead: function(n){
 | |
|     var fetch = n - this.stash.length;
 | |
|     while (fetch-- > 0) this.stash.push(this.next());
 | |
|     return this.stash[--n];
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Return the indexOf `(` or `{` or `[` / `)` or `}` or `]` delimiters.
 | |
|    *
 | |
|    * @return {Number}
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   bracketExpression: function(skip){
 | |
|     skip = skip || 0;
 | |
|     var start = this.input[skip];
 | |
|     if (start != '(' && start != '{' && start != '[') throw new Error('unrecognized start character');
 | |
|     var end = ({'(': ')', '{': '}', '[': ']'})[start];
 | |
|     var range = characterParser.parseMax(this.input, {start: skip + 1});
 | |
|     if (this.input[range.end] !== end) throw new Error('start character ' + start + ' does not match end character ' + this.input[range.end]);
 | |
|     return range;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Stashed token.
 | |
|    */
 | |
| 
 | |
|   stashed: function() {
 | |
|     return this.stash.length
 | |
|       && this.stash.shift();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Deferred token.
 | |
|    */
 | |
| 
 | |
|   deferred: function() {
 | |
|     return this.deferredTokens.length
 | |
|       && this.deferredTokens.shift();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * end-of-source.
 | |
|    */
 | |
| 
 | |
|   eos: function() {
 | |
|     if (this.input.length) return;
 | |
|     if (this.indentStack.length) {
 | |
|       this.indentStack.shift();
 | |
|       return this.tok('outdent');
 | |
|     } else {
 | |
|       return this.tok('eos');
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Blank line.
 | |
|    */
 | |
| 
 | |
|   blank: function() {
 | |
|     var captures;
 | |
|     if (captures = /^\n *\n/.exec(this.input)) {
 | |
|       this.consume(captures[0].length - 1);
 | |
|       ++this.lineno;
 | |
|       if (this.pipeless) return this.tok('text', '');
 | |
|       return this.next();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Comment.
 | |
|    */
 | |
| 
 | |
|   comment: function() {
 | |
|     var captures;
 | |
|     if (captures = /^\/\/(-)?([^\n]*)/.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       var tok = this.tok('comment', captures[2]);
 | |
|       tok.buffer = '-' != captures[1];
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Interpolated tag.
 | |
|    */
 | |
| 
 | |
|   interpolation: function() {
 | |
|     if (/^#\{/.test(this.input)) {
 | |
|       var match;
 | |
|       try {
 | |
|         match = this.bracketExpression(1);
 | |
|       } catch (ex) {
 | |
|         return;//not an interpolation expression, just an unmatched open interpolation
 | |
|       }
 | |
| 
 | |
|       this.consume(match.end + 1);
 | |
|       return this.tok('interpolation', match.src);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Tag.
 | |
|    */
 | |
| 
 | |
|   tag: function() {
 | |
|     var captures;
 | |
|     if (captures = /^(\w[-:\w]*)(\/?)/.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       var tok, name = captures[1];
 | |
|       if (':' == name[name.length - 1]) {
 | |
|         name = name.slice(0, -1);
 | |
|         tok = this.tok('tag', name);
 | |
|         this.defer(this.tok(':'));
 | |
|         while (' ' == this.input[0]) this.input = this.input.substr(1);
 | |
|       } else {
 | |
|         tok = this.tok('tag', name);
 | |
|       }
 | |
|       tok.selfClosing = !! captures[2];
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Filter.
 | |
|    */
 | |
| 
 | |
|   filter: function() {
 | |
|     return this.scan(/^:([\w\-]+)/, 'filter');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Doctype.
 | |
|    */
 | |
| 
 | |
|   doctype: function() {
 | |
|     if (this.scan(/^!!! *([^\n]+)?/, 'doctype')) {
 | |
|       throw new Error('`!!!` is deprecated, you must now use `doctype`');
 | |
|     }
 | |
|     var node = this.scan(/^(?:doctype) *([^\n]+)?/, 'doctype');
 | |
|     if (node && node.val && node.val.trim() === '5') {
 | |
|       throw new Error('`doctype 5` is deprecated, you must now use `doctype html`');
 | |
|     }
 | |
|     return node;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Id.
 | |
|    */
 | |
| 
 | |
|   id: function() {
 | |
|     return this.scan(/^#([\w-]+)/, 'id');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Class.
 | |
|    */
 | |
| 
 | |
|   className: function() {
 | |
|     return this.scan(/^\.([\w-]+)/, 'class');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Text.
 | |
|    */
 | |
| 
 | |
|   text: function() {
 | |
|     return this.scan(/^(?:\| ?| )([^\n]+)/, 'text') || this.scan(/^(<[^\n]*)/, 'text');
 | |
|   },
 | |
| 
 | |
|   textFail: function () {
 | |
|     var tok;
 | |
|     if (tok = this.scan(/^([^\.\n][^\n]+)/, 'text')) {
 | |
|       console.warn('Warning: missing space before text for line ' + this.lineno + 
 | |
|           ' of jade file "' + this.filename + '"');
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Dot.
 | |
|    */
 | |
| 
 | |
|   dot: function() {
 | |
|     return this.scan(/^\./, 'dot');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Extends.
 | |
|    */
 | |
| 
 | |
|   "extends": function() {
 | |
|     return this.scan(/^extends? +([^\n]+)/, 'extends');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Block prepend.
 | |
|    */
 | |
| 
 | |
|   prepend: function() {
 | |
|     var captures;
 | |
|     if (captures = /^prepend +([^\n]+)/.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       var mode = 'prepend'
 | |
|         , name = captures[1]
 | |
|         , tok = this.tok('block', name);
 | |
|       tok.mode = mode;
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Block append.
 | |
|    */
 | |
| 
 | |
|   append: function() {
 | |
|     var captures;
 | |
|     if (captures = /^append +([^\n]+)/.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       var mode = 'append'
 | |
|         , name = captures[1]
 | |
|         , tok = this.tok('block', name);
 | |
|       tok.mode = mode;
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Block.
 | |
|    */
 | |
| 
 | |
|   block: function() {
 | |
|     var captures;
 | |
|     if (captures = /^block\b *(?:(prepend|append) +)?([^\n]+)/.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       var mode = captures[1] || 'replace'
 | |
|         , name = captures[2]
 | |
|         , tok = this.tok('block', name);
 | |
| 
 | |
|       tok.mode = mode;
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Mixin Block.
 | |
|    */
 | |
| 
 | |
|   mixinBlock: function() {
 | |
|     var captures;
 | |
|     if (captures = /^block\s*(\n|$)/.exec(this.input)) {
 | |
|       this.consume(captures[0].length - 1);
 | |
|       return this.tok('mixin-block');
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Yield.
 | |
|    */
 | |
| 
 | |
|   yield: function() {
 | |
|     return this.scan(/^yield */, 'yield');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Include.
 | |
|    */
 | |
| 
 | |
|   include: function() {
 | |
|     return this.scan(/^include +([^\n]+)/, 'include');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Include with filter
 | |
|    */
 | |
| 
 | |
|   includeFiltered: function() {
 | |
|     var captures;
 | |
|     if (captures = /^include:([\w\-]+) +([^\n]+)/.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       var filter = captures[1];
 | |
|       var path = captures[2];
 | |
|       var tok = this.tok('include', path);
 | |
|       tok.filter = filter;
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Case.
 | |
|    */
 | |
| 
 | |
|   "case": function() {
 | |
|     return this.scan(/^case +([^\n]+)/, 'case');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * When.
 | |
|    */
 | |
| 
 | |
|   when: function() {
 | |
|     return this.scan(/^when +([^:\n]+)/, 'when');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Default.
 | |
|    */
 | |
| 
 | |
|   "default": function() {
 | |
|     return this.scan(/^default */, 'default');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Call mixin.
 | |
|    */
 | |
| 
 | |
|   call: function(){
 | |
| 
 | |
|     var tok, captures;
 | |
|     if (captures = /^\+(([-\w]+)|(#\{))/.exec(this.input)) {
 | |
|       // try to consume simple or interpolated call
 | |
|       if (captures[2]) {
 | |
|         // simple call
 | |
|         this.consume(captures[0].length);
 | |
|         tok = this.tok('call', captures[2]);
 | |
|       } else {
 | |
|         // interpolated call
 | |
|         var match;
 | |
|         try {
 | |
|           match = this.bracketExpression(2);
 | |
|         } catch (ex) {
 | |
|           return;//not an interpolation expression, just an unmatched open interpolation
 | |
|         }
 | |
|         this.consume(match.end + 1);
 | |
|         assertExpression(match.src);
 | |
|         tok = this.tok('call', '#{'+match.src+'}');
 | |
|       }
 | |
| 
 | |
|       // Check for args (not attributes)
 | |
|       if (captures = /^ *\(/.exec(this.input)) {
 | |
|         try {
 | |
|           var range = this.bracketExpression(captures[0].length - 1);
 | |
|           if (!/^ *[-\w]+ *=/.test(range.src)) { // not attributes
 | |
|             this.consume(range.end + 1);
 | |
|             tok.args = range.src;
 | |
|           }
 | |
|         } catch (ex) {
 | |
|           //not a bracket expcetion, just unmatched open parens
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Mixin.
 | |
|    */
 | |
| 
 | |
|   mixin: function(){
 | |
|     var captures;
 | |
|     if (captures = /^mixin +([-\w]+)(?: *\((.*)\))? */.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       var tok = this.tok('mixin', captures[1]);
 | |
|       tok.args = captures[2];
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Conditional.
 | |
|    */
 | |
| 
 | |
|   conditional: function() {
 | |
|     var captures;
 | |
|     if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       var type = captures[1]
 | |
|         , js = captures[2];
 | |
| 
 | |
|       switch (type) {
 | |
|         case 'if':
 | |
|           assertExpression(js)
 | |
|           js = 'if (' + js + ')';
 | |
|           break;
 | |
|         case 'unless':
 | |
|           assertExpression(js)
 | |
|           js = 'if (!(' + js + '))';
 | |
|           break;
 | |
|         case 'else if':
 | |
|           assertExpression(js)
 | |
|           js = 'else if (' + js + ')';
 | |
|           break;
 | |
|         case 'else':
 | |
|           if (js && js.trim()) {
 | |
|             throw new Error('`else` cannot have a condition, perhaps you meant `else if`');
 | |
|           }
 | |
|           js = 'else';
 | |
|           break;
 | |
|       }
 | |
| 
 | |
|       return this.tok('code', js);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * While.
 | |
|    */
 | |
| 
 | |
|   "while": function() {
 | |
|     var captures;
 | |
|     if (captures = /^while +([^\n]+)/.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       assertExpression(captures[1])
 | |
|       return this.tok('code', 'while (' + captures[1] + ')');
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Each.
 | |
|    */
 | |
| 
 | |
|   each: function() {
 | |
|     var captures;
 | |
|     if (captures = /^(?:- *)?(?:each|for) +([a-zA-Z_$][\w$]*)(?: *, *([a-zA-Z_$][\w$]*))? * in *([^\n]+)/.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       var tok = this.tok('each', captures[1]);
 | |
|       tok.key = captures[2] || '$index';
 | |
|       assertExpression(captures[3])
 | |
|       tok.code = captures[3];
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Code.
 | |
|    */
 | |
| 
 | |
|   code: function() {
 | |
|     var captures;
 | |
|     if (captures = /^(!?=|-)[ \t]*([^\n]+)/.exec(this.input)) {
 | |
|       this.consume(captures[0].length);
 | |
|       var flags = captures[1];
 | |
|       captures[1] = captures[2];
 | |
|       var tok = this.tok('code', captures[1]);
 | |
|       tok.escape = flags.charAt(0) === '=';
 | |
|       tok.buffer = flags.charAt(0) === '=' || flags.charAt(1) === '=';
 | |
|       if (tok.buffer) assertExpression(captures[1])
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Attributes.
 | |
|    */
 | |
| 
 | |
|   attrs: function() {
 | |
|     if ('(' == this.input.charAt(0)) {
 | |
|       var index = this.bracketExpression().end
 | |
|         , str = this.input.substr(1, index-1)
 | |
|         , tok = this.tok('attrs');
 | |
| 
 | |
|       assertNestingCorrect(str);
 | |
| 
 | |
|       var quote = '';
 | |
|       var interpolate = function (attr) {
 | |
|         return attr.replace(/(\\)?#\{(.+)/g, function(_, escape, expr){
 | |
|           if (escape) return _;
 | |
|           try {
 | |
|             var range = characterParser.parseMax(expr);
 | |
|             if (expr[range.end] !== '}') return _.substr(0, 2) + interpolate(_.substr(2));
 | |
|             assertExpression(range.src)
 | |
|             return quote + " + (" + range.src + ") + " + quote + interpolate(expr.substr(range.end + 1));
 | |
|           } catch (ex) {
 | |
|             return _.substr(0, 2) + interpolate(_.substr(2));
 | |
|           }
 | |
|         });
 | |
|       }
 | |
| 
 | |
|       this.consume(index + 1);
 | |
|       tok.attrs = [];
 | |
| 
 | |
|       var escapedAttr = true
 | |
|       var key = '';
 | |
|       var val = '';
 | |
|       var interpolatable = '';
 | |
|       var state = characterParser.defaultState();
 | |
|       var loc = 'key';
 | |
|       var isEndOfAttribute = function (i) {
 | |
|         if (key.trim() === '') return false;
 | |
|         if (i === str.length) return true;
 | |
|         if (loc === 'key') {
 | |
|           if (str[i] === ' ' || str[i] === '\n') {
 | |
|             for (var x = i; x < str.length; x++) {
 | |
|               if (str[x] != ' ' && str[x] != '\n') {
 | |
|                 if (str[x] === '=' || str[x] === '!' || str[x] === ',') return false;
 | |
|                 else return true;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|           return str[i] === ','
 | |
|         } else if (loc === 'value' && !state.isNesting()) {
 | |
|           try {
 | |
|             Function('', 'return (' + val + ');');
 | |
|             if (str[i] === ' ' || str[i] === '\n') {
 | |
|               for (var x = i; x < str.length; x++) {
 | |
|                 if (str[x] != ' ' && str[x] != '\n') {
 | |
|                   if (characterParser.isPunctuator(str[x]) && str[x] != '"' && str[x] != "'") return false;
 | |
|                   else return true;
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|             return str[i] === ',';
 | |
|           } catch (ex) {
 | |
|             return false;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       this.lineno += str.split("\n").length - 1;
 | |
| 
 | |
|       for (var i = 0; i <= str.length; i++) {
 | |
|         if (isEndOfAttribute(i)) {
 | |
|           val = val.trim();
 | |
|           if (val) assertExpression(val)
 | |
|           key = key.trim();
 | |
|           key = key.replace(/^['"]|['"]$/g, '');
 | |
|           tok.attrs.push({
 | |
|             name: key,
 | |
|             val: '' == val ? true : val,
 | |
|             escaped: escapedAttr
 | |
|           });
 | |
|           key = val = '';
 | |
|           loc = 'key';
 | |
|           escapedAttr = false;
 | |
|         } else {
 | |
|           switch (loc) {
 | |
|             case 'key-char':
 | |
|               if (str[i] === quote) {
 | |
|                 loc = 'key';
 | |
|                 if (i + 1 < str.length && [' ', ',', '!', '=', '\n'].indexOf(str[i + 1]) === -1)
 | |
|                   throw new Error('Unexpected character ' + str[i + 1] + ' expected ` `, `\\n`, `,`, `!` or `=`');
 | |
|               } else if (loc === 'key-char') {
 | |
|                 key += str[i];
 | |
|               }
 | |
|               break;
 | |
|             case 'key':
 | |
|               if (key === '' && (str[i] === '"' || str[i] === "'")) {
 | |
|                 loc = 'key-char';
 | |
|                 quote = str[i];
 | |
|               } else if (str[i] === '!' || str[i] === '=') {
 | |
|                 escapedAttr = str[i] !== '!';
 | |
|                 if (str[i] === '!') i++;
 | |
|                 if (str[i] !== '=') throw new Error('Unexpected character ' + str[i] + ' expected `=`');
 | |
|                 loc = 'value';
 | |
|                 state = characterParser.defaultState();
 | |
|               } else {
 | |
|                 key += str[i]
 | |
|               }
 | |
|               break;
 | |
|             case 'value':
 | |
|               state = characterParser.parseChar(str[i], state);
 | |
|               if (state.isString()) {
 | |
|                 loc = 'string';
 | |
|                 quote = str[i];
 | |
|                 interpolatable = str[i];
 | |
|               } else {
 | |
|                 val += str[i];
 | |
|               }
 | |
|               break;
 | |
|             case 'string':
 | |
|               state = characterParser.parseChar(str[i], state);
 | |
|               interpolatable += str[i];
 | |
|               if (!state.isString()) {
 | |
|                 loc = 'value';
 | |
|                 val += interpolate(interpolatable);
 | |
|               }
 | |
|               break;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if ('/' == this.input.charAt(0)) {
 | |
|         this.consume(1);
 | |
|         tok.selfClosing = true;
 | |
|       }
 | |
| 
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * &attributes block
 | |
|    */
 | |
|   attributesBlock: function () {
 | |
|     var captures;
 | |
|     if (/^&attributes\b/.test(this.input)) {
 | |
|       this.consume(11);
 | |
|       var args = this.bracketExpression();
 | |
|       this.consume(args.end + 1);
 | |
|       return this.tok('&attributes', args.src);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Indent | Outdent | Newline.
 | |
|    */
 | |
| 
 | |
|   indent: function() {
 | |
|     var captures, re;
 | |
| 
 | |
|     // established regexp
 | |
|     if (this.indentRe) {
 | |
|       captures = this.indentRe.exec(this.input);
 | |
|     // determine regexp
 | |
|     } else {
 | |
|       // tabs
 | |
|       re = /^\n(\t*) */;
 | |
|       captures = re.exec(this.input);
 | |
| 
 | |
|       // spaces
 | |
|       if (captures && !captures[1].length) {
 | |
|         re = /^\n( *)/;
 | |
|         captures = re.exec(this.input);
 | |
|       }
 | |
| 
 | |
|       // established
 | |
|       if (captures && captures[1].length) this.indentRe = re;
 | |
|     }
 | |
| 
 | |
|     if (captures) {
 | |
|       var tok
 | |
|         , indents = captures[1].length;
 | |
| 
 | |
|       ++this.lineno;
 | |
|       this.consume(indents + 1);
 | |
| 
 | |
|       if (' ' == this.input[0] || '\t' == this.input[0]) {
 | |
|         throw new Error('Invalid indentation, you can use tabs or spaces but not both');
 | |
|       }
 | |
| 
 | |
|       // blank line
 | |
|       if ('\n' == this.input[0]) return this.tok('newline');
 | |
| 
 | |
|       // outdent
 | |
|       if (this.indentStack.length && indents < this.indentStack[0]) {
 | |
|         while (this.indentStack.length && this.indentStack[0] > indents) {
 | |
|           this.stash.push(this.tok('outdent'));
 | |
|           this.indentStack.shift();
 | |
|         }
 | |
|         tok = this.stash.pop();
 | |
|       // indent
 | |
|       } else if (indents && indents != this.indentStack[0]) {
 | |
|         this.indentStack.unshift(indents);
 | |
|         tok = this.tok('indent', indents);
 | |
|       // newline
 | |
|       } else {
 | |
|         tok = this.tok('newline');
 | |
|       }
 | |
| 
 | |
|       return tok;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Pipe-less text consumed only when
 | |
|    * pipeless is true;
 | |
|    */
 | |
| 
 | |
|   pipelessText: function() {
 | |
|     if (this.pipeless) {
 | |
|       if ('\n' == this.input[0]) return;
 | |
|       var i = this.input.indexOf('\n');
 | |
|       if (-1 == i) i = this.input.length;
 | |
|       var str = this.input.substr(0, i);
 | |
|       this.consume(str.length);
 | |
|       return this.tok('text', str);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * ':'
 | |
|    */
 | |
| 
 | |
|   colon: function() {
 | |
|     return this.scan(/^: */, ':');
 | |
|   },
 | |
| 
 | |
|   fail: function () {
 | |
|     if (/^ ($|\n)/.test(this.input)) {
 | |
|       this.consume(1);
 | |
|       return this.next();
 | |
|     }
 | |
|     throw new Error('unexpected text ' + this.input.substr(0, 5));
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Return the next token object, or those
 | |
|    * previously stashed by lookahead.
 | |
|    *
 | |
|    * @return {Object}
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   advance: function(){
 | |
|     return this.stashed()
 | |
|       || this.next();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Return the next token object.
 | |
|    *
 | |
|    * @return {Object}
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   next: function() {
 | |
|     return this.deferred()
 | |
|       || this.blank()
 | |
|       || this.eos()
 | |
|       || this.pipelessText()
 | |
|       || this.yield()
 | |
|       || this.doctype()
 | |
|       || this.interpolation()
 | |
|       || this["case"]()
 | |
|       || this.when()
 | |
|       || this["default"]()
 | |
|       || this["extends"]()
 | |
|       || this.append()
 | |
|       || this.prepend()
 | |
|       || this.block()
 | |
|       || this.mixinBlock()
 | |
|       || this.include()
 | |
|       || this.includeFiltered()
 | |
|       || this.mixin()
 | |
|       || this.call()
 | |
|       || this.conditional()
 | |
|       || this.each()
 | |
|       || this["while"]()
 | |
|       || this.tag()
 | |
|       || this.filter()
 | |
|       || this.code()
 | |
|       || this.id()
 | |
|       || this.className()
 | |
|       || this.attrs()
 | |
|       || this.attributesBlock()
 | |
|       || this.indent()
 | |
|       || this.text()
 | |
|       || this.comment()
 | |
|       || this.colon()
 | |
|       || this.dot()
 | |
|       || this.textFail()
 | |
|       || this.fail();
 | |
|   }
 | |
| };
 | |
| 
 | |
| },{"./utils":26,"character-parser":33}],7:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node');
 | |
| var Block = require('./block');
 | |
| 
 | |
| /**
 | |
|  * Initialize a `Attrs` node.
 | |
|  *
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Attrs = module.exports = function Attrs() {
 | |
|   this.attributeNames = [];
 | |
|   this.attrs = [];
 | |
|   this.attributeBlocks = [];
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| Attrs.prototype = Object.create(Node.prototype);
 | |
| Attrs.prototype.constructor = Attrs;
 | |
| 
 | |
| Attrs.prototype.type = 'Attrs';
 | |
| 
 | |
| /**
 | |
|  * Set attribute `name` to `val`, keep in mind these become
 | |
|  * part of a raw js object literal, so to quote a value you must
 | |
|  * '"quote me"', otherwise or example 'user.name' is literal JavaScript.
 | |
|  *
 | |
|  * @param {String} name
 | |
|  * @param {String} val
 | |
|  * @param {Boolean} escaped
 | |
|  * @return {Tag} for chaining
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| Attrs.prototype.setAttribute = function(name, val, escaped){
 | |
|   this.attributeNames = this.attributeNames || [];
 | |
|   if (name !== 'class' && this.attributeNames.indexOf(name) !== -1) {
 | |
|     throw new Error('Duplicate attribute "' + name + '" is not allowed.');
 | |
|   }
 | |
|   this.attributeNames.push(name);
 | |
|   this.attrs.push({ name: name, val: val, escaped: escaped });
 | |
|   return this;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Remove attribute `name` when present.
 | |
|  *
 | |
|  * @param {String} name
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| Attrs.prototype.removeAttribute = function(name){
 | |
|   for (var i = 0, len = this.attrs.length; i < len; ++i) {
 | |
|     if (this.attrs[i] && this.attrs[i].name == name) {
 | |
|       delete this.attrs[i];
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Get attribute value by `name`.
 | |
|  *
 | |
|  * @param {String} name
 | |
|  * @return {String}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| Attrs.prototype.getAttribute = function(name){
 | |
|   for (var i = 0, len = this.attrs.length; i < len; ++i) {
 | |
|     if (this.attrs[i] && this.attrs[i].name == name) {
 | |
|       return this.attrs[i].val;
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| Attrs.prototype.addAttributes = function (src) {
 | |
|   this.attributeBlocks.push(src);
 | |
| };
 | |
| 
 | |
| },{"./block":9,"./node":20}],8:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node');
 | |
| 
 | |
| /**
 | |
|  * Initialize a `BlockComment` with the given `block`.
 | |
|  *
 | |
|  * @param {String} val
 | |
|  * @param {Block} block
 | |
|  * @param {Boolean} buffer
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var BlockComment = module.exports = function BlockComment(val, block, buffer) {
 | |
|   this.block = block;
 | |
|   this.val = val;
 | |
|   this.buffer = buffer;
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| BlockComment.prototype = Object.create(Node.prototype);
 | |
| BlockComment.prototype.constructor = BlockComment;
 | |
| 
 | |
| BlockComment.prototype.type = 'BlockComment';
 | |
| 
 | |
| },{"./node":20}],9:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node');
 | |
| 
 | |
| /**
 | |
|  * Initialize a new `Block` with an optional `node`.
 | |
|  *
 | |
|  * @param {Node} node
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Block = module.exports = function Block(node){
 | |
|   this.nodes = [];
 | |
|   if (node) this.push(node);
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| Block.prototype = Object.create(Node.prototype);
 | |
| Block.prototype.constructor = Block;
 | |
| 
 | |
| Block.prototype.type = 'Block';
 | |
| 
 | |
| /**
 | |
|  * Block flag.
 | |
|  */
 | |
| 
 | |
| Block.prototype.isBlock = true;
 | |
| 
 | |
| /**
 | |
|  * Replace the nodes in `other` with the nodes
 | |
|  * in `this` block.
 | |
|  *
 | |
|  * @param {Block} other
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| Block.prototype.replace = function(other){
 | |
|   other.nodes = this.nodes;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Pust the given `node`.
 | |
|  *
 | |
|  * @param {Node} node
 | |
|  * @return {Number}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| Block.prototype.push = function(node){
 | |
|   return this.nodes.push(node);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Check if this block is empty.
 | |
|  *
 | |
|  * @return {Boolean}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| Block.prototype.isEmpty = function(){
 | |
|   return 0 == this.nodes.length;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Unshift the given `node`.
 | |
|  *
 | |
|  * @param {Node} node
 | |
|  * @return {Number}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| Block.prototype.unshift = function(node){
 | |
|   return this.nodes.unshift(node);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Return the "last" block, or the first `yield` node.
 | |
|  *
 | |
|  * @return {Block}
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| Block.prototype.includeBlock = function(){
 | |
|   var ret = this
 | |
|     , node;
 | |
| 
 | |
|   for (var i = 0, len = this.nodes.length; i < len; ++i) {
 | |
|     node = this.nodes[i];
 | |
|     if (node.yield) return node;
 | |
|     else if (node.textOnly) continue;
 | |
|     else if (node.includeBlock) ret = node.includeBlock();
 | |
|     else if (node.block && !node.block.isEmpty()) ret = node.block.includeBlock();
 | |
|     if (ret.yield) return ret;
 | |
|   }
 | |
| 
 | |
|   return ret;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Return a clone of this block.
 | |
|  *
 | |
|  * @return {Block}
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| Block.prototype.clone = function(){
 | |
|   var clone = new Block;
 | |
|   for (var i = 0, len = this.nodes.length; i < len; ++i) {
 | |
|     clone.push(this.nodes[i].clone());
 | |
|   }
 | |
|   return clone;
 | |
| };
 | |
| 
 | |
| },{"./node":20}],10:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node');
 | |
| 
 | |
| /**
 | |
|  * Initialize a new `Case` with `expr`.
 | |
|  *
 | |
|  * @param {String} expr
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Case = exports = module.exports = function Case(expr, block){
 | |
|   this.expr = expr;
 | |
|   this.block = block;
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| Case.prototype = Object.create(Node.prototype);
 | |
| Case.prototype.constructor = Case;
 | |
| 
 | |
| Case.prototype.type = 'Case';
 | |
| 
 | |
| var When = exports.When = function When(expr, block){
 | |
|   this.expr = expr;
 | |
|   this.block = block;
 | |
|   this.debug = false;
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| When.prototype = Object.create(Node.prototype);
 | |
| When.prototype.constructor = When;
 | |
| 
 | |
| When.prototype.type = 'When';
 | |
| 
 | |
| },{"./node":20}],11:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node');
 | |
| 
 | |
| /**
 | |
|  * Initialize a `Code` node with the given code `val`.
 | |
|  * Code may also be optionally buffered and escaped.
 | |
|  *
 | |
|  * @param {String} val
 | |
|  * @param {Boolean} buffer
 | |
|  * @param {Boolean} escape
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Code = module.exports = function Code(val, buffer, escape) {
 | |
|   this.val = val;
 | |
|   this.buffer = buffer;
 | |
|   this.escape = escape;
 | |
|   if (val.match(/^ *else/)) this.debug = false;
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| Code.prototype = Object.create(Node.prototype);
 | |
| Code.prototype.constructor = Code;
 | |
| 
 | |
| Code.prototype.type = 'Code'; // prevent the minifiers removing this
 | |
| },{"./node":20}],12:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node');
 | |
| 
 | |
| /**
 | |
|  * Initialize a `Comment` with the given `val`, optionally `buffer`,
 | |
|  * otherwise the comment may render in the output.
 | |
|  *
 | |
|  * @param {String} val
 | |
|  * @param {Boolean} buffer
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Comment = module.exports = function Comment(val, buffer) {
 | |
|   this.val = val;
 | |
|   this.buffer = buffer;
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| Comment.prototype = Object.create(Node.prototype);
 | |
| Comment.prototype.constructor = Comment;
 | |
| 
 | |
| Comment.prototype.type = 'Comment';
 | |
| 
 | |
| },{"./node":20}],13:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node');
 | |
| 
 | |
| /**
 | |
|  * Initialize a `Doctype` with the given `val`. 
 | |
|  *
 | |
|  * @param {String} val
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Doctype = module.exports = function Doctype(val) {
 | |
|   this.val = val;
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| Doctype.prototype = Object.create(Node.prototype);
 | |
| Doctype.prototype.constructor = Doctype;
 | |
| 
 | |
| Doctype.prototype.type = 'Doctype';
 | |
| 
 | |
| },{"./node":20}],14:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node');
 | |
| 
 | |
| /**
 | |
|  * Initialize an `Each` node, representing iteration
 | |
|  *
 | |
|  * @param {String} obj
 | |
|  * @param {String} val
 | |
|  * @param {String} key
 | |
|  * @param {Block} block
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Each = module.exports = function Each(obj, val, key, block) {
 | |
|   this.obj = obj;
 | |
|   this.val = val;
 | |
|   this.key = key;
 | |
|   this.block = block;
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| Each.prototype = Object.create(Node.prototype);
 | |
| Each.prototype.constructor = Each;
 | |
| 
 | |
| Each.prototype.type = 'Each';
 | |
| 
 | |
| },{"./node":20}],15:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node')
 | |
| var Block = require('./block');
 | |
| 
 | |
| /**
 | |
|  * Initialize a `Filter` node with the given
 | |
|  * filter `name` and `block`.
 | |
|  *
 | |
|  * @param {String} name
 | |
|  * @param {Block|Node} block
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Filter = module.exports = function Filter(name, block, attrs) {
 | |
|   this.name = name;
 | |
|   this.block = block;
 | |
|   this.attrs = attrs;
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| Filter.prototype = Object.create(Node.prototype);
 | |
| Filter.prototype.constructor = Filter;
 | |
| 
 | |
| Filter.prototype.type = 'Filter';
 | |
| 
 | |
| },{"./block":9,"./node":20}],16:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| exports.Node = require('./node');
 | |
| exports.Tag = require('./tag');
 | |
| exports.Code = require('./code');
 | |
| exports.Each = require('./each');
 | |
| exports.Case = require('./case');
 | |
| exports.Text = require('./text');
 | |
| exports.Block = require('./block');
 | |
| exports.MixinBlock = require('./mixin-block');
 | |
| exports.Mixin = require('./mixin');
 | |
| exports.Filter = require('./filter');
 | |
| exports.Comment = require('./comment');
 | |
| exports.Literal = require('./literal');
 | |
| exports.BlockComment = require('./block-comment');
 | |
| exports.Doctype = require('./doctype');
 | |
| 
 | |
| },{"./block":9,"./block-comment":8,"./case":10,"./code":11,"./comment":12,"./doctype":13,"./each":14,"./filter":15,"./literal":17,"./mixin":19,"./mixin-block":18,"./node":20,"./tag":21,"./text":22}],17:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node');
 | |
| 
 | |
| /**
 | |
|  * Initialize a `Literal` node with the given `str.
 | |
|  *
 | |
|  * @param {String} str
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Literal = module.exports = function Literal(str) {
 | |
|   this.str = str;
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| Literal.prototype = Object.create(Node.prototype);
 | |
| Literal.prototype.constructor = Literal;
 | |
| 
 | |
| Literal.prototype.type = 'Literal';
 | |
| 
 | |
| },{"./node":20}],18:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node');
 | |
| 
 | |
| /**
 | |
|  * Initialize a new `Block` with an optional `node`.
 | |
|  *
 | |
|  * @param {Node} node
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var MixinBlock = module.exports = function MixinBlock(){};
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| MixinBlock.prototype = Object.create(Node.prototype);
 | |
| MixinBlock.prototype.constructor = MixinBlock;
 | |
| 
 | |
| MixinBlock.prototype.type = 'MixinBlock';
 | |
| 
 | |
| },{"./node":20}],19:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Attrs = require('./attrs');
 | |
| 
 | |
| /**
 | |
|  * Initialize a new `Mixin` with `name` and `block`.
 | |
|  *
 | |
|  * @param {String} name
 | |
|  * @param {String} args
 | |
|  * @param {Block} block
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Mixin = module.exports = function Mixin(name, args, block, call){
 | |
|   this.name = name;
 | |
|   this.args = args;
 | |
|   this.block = block;
 | |
|   this.attrs = [];
 | |
|   this.attributeBlocks = [];
 | |
|   this.call = call;
 | |
| };
 | |
| 
 | |
| // Inherit from `Attrs`.
 | |
| Mixin.prototype = Object.create(Attrs.prototype);
 | |
| Mixin.prototype.constructor = Mixin;
 | |
| 
 | |
| Mixin.prototype.type = 'Mixin';
 | |
| 
 | |
| },{"./attrs":7}],20:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = module.exports = function Node(){};
 | |
| 
 | |
| /**
 | |
|  * Clone this node (return itself)
 | |
|  *
 | |
|  * @return {Node}
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| Node.prototype.clone = function(){
 | |
|   return this;
 | |
| };
 | |
| 
 | |
| Node.prototype.type = '';
 | |
| 
 | |
| },{}],21:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Attrs = require('./attrs');
 | |
| var Block = require('./block');
 | |
| var inlineTags = require('../inline-tags');
 | |
| 
 | |
| /**
 | |
|  * Initialize a `Tag` node with the given tag `name` and optional `block`.
 | |
|  *
 | |
|  * @param {String} name
 | |
|  * @param {Block} block
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Tag = module.exports = function Tag(name, block) {
 | |
|   this.name = name;
 | |
|   this.attrs = [];
 | |
|   this.attributeBlocks = [];
 | |
|   this.block = block || new Block;
 | |
| };
 | |
| 
 | |
| // Inherit from `Attrs`.
 | |
| Tag.prototype = Object.create(Attrs.prototype);
 | |
| Tag.prototype.constructor = Tag;
 | |
| 
 | |
| Tag.prototype.type = 'Tag';
 | |
| 
 | |
| /**
 | |
|  * Clone this tag.
 | |
|  *
 | |
|  * @return {Tag}
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| Tag.prototype.clone = function(){
 | |
|   var clone = new Tag(this.name, this.block.clone());
 | |
|   clone.line = this.line;
 | |
|   clone.attrs = this.attrs;
 | |
|   clone.textOnly = this.textOnly;
 | |
|   return clone;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Check if this tag is an inline tag.
 | |
|  *
 | |
|  * @return {Boolean}
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| Tag.prototype.isInline = function(){
 | |
|   return ~inlineTags.indexOf(this.name);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Check if this tag's contents can be inlined.  Used for pretty printing.
 | |
|  *
 | |
|  * @return {Boolean}
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| Tag.prototype.canInline = function(){
 | |
|   var nodes = this.block.nodes;
 | |
| 
 | |
|   function isInline(node){
 | |
|     // Recurse if the node is a block
 | |
|     if (node.isBlock) return node.nodes.every(isInline);
 | |
|     return node.isText || (node.isInline && node.isInline());
 | |
|   }
 | |
| 
 | |
|   // Empty tag
 | |
|   if (!nodes.length) return true;
 | |
| 
 | |
|   // Text-only or inline-only tag
 | |
|   if (1 == nodes.length) return isInline(nodes[0]);
 | |
| 
 | |
|   // Multi-line inline-only tag
 | |
|   if (this.block.nodes.every(isInline)) {
 | |
|     for (var i = 1, len = nodes.length; i < len; ++i) {
 | |
|       if (nodes[i-1].isText && nodes[i].isText)
 | |
|         return false;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Mixed tag
 | |
|   return false;
 | |
| };
 | |
| 
 | |
| },{"../inline-tags":4,"./attrs":7,"./block":9}],22:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Node = require('./node');
 | |
| 
 | |
| /**
 | |
|  * Initialize a `Text` node with optional `line`.
 | |
|  *
 | |
|  * @param {String} line
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Text = module.exports = function Text(line) {
 | |
|   this.val = '';
 | |
|   if ('string' == typeof line) this.val = line;
 | |
| };
 | |
| 
 | |
| // Inherit from `Node`.
 | |
| Text.prototype = Object.create(Node.prototype);
 | |
| Text.prototype.constructor = Text;
 | |
| 
 | |
| Text.prototype.type = 'Text';
 | |
| 
 | |
| /**
 | |
|  * Flag as text.
 | |
|  */
 | |
| 
 | |
| Text.prototype.isText = true;
 | |
| },{"./node":20}],23:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| var Lexer = require('./lexer');
 | |
| var nodes = require('./nodes');
 | |
| var utils = require('./utils');
 | |
| var filters = require('./filters');
 | |
| var path = require('path');
 | |
| var constantinople = require('constantinople');
 | |
| var parseJSExpression = require('character-parser').parseMax;
 | |
| var extname = path.extname;
 | |
| 
 | |
| /**
 | |
|  * Initialize `Parser` with the given input `str` and `filename`.
 | |
|  *
 | |
|  * @param {String} str
 | |
|  * @param {String} filename
 | |
|  * @param {Object} options
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| var Parser = exports = module.exports = function Parser(str, filename, options){
 | |
|   //Strip any UTF-8 BOM off of the start of `str`, if it exists.
 | |
|   this.input = str.replace(/^\uFEFF/, '');
 | |
|   this.lexer = new Lexer(this.input, filename);
 | |
|   this.filename = filename;
 | |
|   this.blocks = {};
 | |
|   this.mixins = {};
 | |
|   this.options = options;
 | |
|   this.contexts = [this];
 | |
|   this.inMixin = false;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Parser prototype.
 | |
|  */
 | |
| 
 | |
| Parser.prototype = {
 | |
| 
 | |
|   /**
 | |
|    * Save original constructor
 | |
|    */
 | |
| 
 | |
|   constructor: Parser,
 | |
| 
 | |
|   /**
 | |
|    * Push `parser` onto the context stack,
 | |
|    * or pop and return a `Parser`.
 | |
|    */
 | |
| 
 | |
|   context: function(parser){
 | |
|     if (parser) {
 | |
|       this.contexts.push(parser);
 | |
|     } else {
 | |
|       return this.contexts.pop();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Return the next token object.
 | |
|    *
 | |
|    * @return {Object}
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   advance: function(){
 | |
|     return this.lexer.advance();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Skip `n` tokens.
 | |
|    *
 | |
|    * @param {Number} n
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   skip: function(n){
 | |
|     while (n--) this.advance();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Single token lookahead.
 | |
|    *
 | |
|    * @return {Object}
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   peek: function() {
 | |
|     return this.lookahead(1);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Return lexer lineno.
 | |
|    *
 | |
|    * @return {Number}
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   line: function() {
 | |
|     return this.lexer.lineno;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * `n` token lookahead.
 | |
|    *
 | |
|    * @param {Number} n
 | |
|    * @return {Object}
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   lookahead: function(n){
 | |
|     return this.lexer.lookahead(n);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Parse input returning a string of js for evaluation.
 | |
|    *
 | |
|    * @return {String}
 | |
|    * @api public
 | |
|    */
 | |
| 
 | |
|   parse: function(){
 | |
|     var block = new nodes.Block, parser;
 | |
|     block.line = 0;
 | |
|     block.filename = this.filename;
 | |
| 
 | |
|     while ('eos' != this.peek().type) {
 | |
|       if ('newline' == this.peek().type) {
 | |
|         this.advance();
 | |
|       } else {
 | |
|         var next = this.peek();
 | |
|         var expr = this.parseExpr();
 | |
|         expr.filename = expr.filename || this.filename;
 | |
|         expr.line = next.line;
 | |
|         block.push(expr);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (parser = this.extending) {
 | |
|       this.context(parser);
 | |
|       var ast = parser.parse();
 | |
|       this.context();
 | |
| 
 | |
|       // hoist mixins
 | |
|       for (var name in this.mixins)
 | |
|         ast.unshift(this.mixins[name]);
 | |
|       return ast;
 | |
|     }
 | |
| 
 | |
|     return block;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Expect the given type, or throw an exception.
 | |
|    *
 | |
|    * @param {String} type
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   expect: function(type){
 | |
|     if (this.peek().type === type) {
 | |
|       return this.advance();
 | |
|     } else {
 | |
|       throw new Error('expected "' + type + '", but got "' + this.peek().type + '"');
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Accept the given `type`.
 | |
|    *
 | |
|    * @param {String} type
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   accept: function(type){
 | |
|     if (this.peek().type === type) {
 | |
|       return this.advance();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    *   tag
 | |
|    * | doctype
 | |
|    * | mixin
 | |
|    * | include
 | |
|    * | filter
 | |
|    * | comment
 | |
|    * | text
 | |
|    * | each
 | |
|    * | code
 | |
|    * | yield
 | |
|    * | id
 | |
|    * | class
 | |
|    * | interpolation
 | |
|    */
 | |
| 
 | |
|   parseExpr: function(){
 | |
|     switch (this.peek().type) {
 | |
|       case 'tag':
 | |
|         return this.parseTag();
 | |
|       case 'mixin':
 | |
|         return this.parseMixin();
 | |
|       case 'block':
 | |
|         return this.parseBlock();
 | |
|       case 'mixin-block':
 | |
|         return this.parseMixinBlock();
 | |
|       case 'case':
 | |
|         return this.parseCase();
 | |
|       case 'when':
 | |
|         return this.parseWhen();
 | |
|       case 'default':
 | |
|         return this.parseDefault();
 | |
|       case 'extends':
 | |
|         return this.parseExtends();
 | |
|       case 'include':
 | |
|         return this.parseInclude();
 | |
|       case 'doctype':
 | |
|         return this.parseDoctype();
 | |
|       case 'filter':
 | |
|         return this.parseFilter();
 | |
|       case 'comment':
 | |
|         return this.parseComment();
 | |
|       case 'text':
 | |
|         return this.parseText();
 | |
|       case 'each':
 | |
|         return this.parseEach();
 | |
|       case 'code':
 | |
|         return this.parseCode();
 | |
|       case 'call':
 | |
|         return this.parseCall();
 | |
|       case 'interpolation':
 | |
|         return this.parseInterpolation();
 | |
|       case 'yield':
 | |
|         this.advance();
 | |
|         var block = new nodes.Block;
 | |
|         block.yield = true;
 | |
|         return block;
 | |
|       case 'id':
 | |
|       case 'class':
 | |
|         var tok = this.advance();
 | |
|         this.lexer.defer(this.lexer.tok('tag', 'div'));
 | |
|         this.lexer.defer(tok);
 | |
|         return this.parseExpr();
 | |
|       default:
 | |
|         throw new Error('unexpected token "' + this.peek().type + '"');
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Text
 | |
|    */
 | |
| 
 | |
|   parseText: function(){
 | |
|     var tok = this.expect('text');
 | |
|     var tokens = this.parseTextWithInlineTags(tok.val);
 | |
|     if (tokens.length === 1) return tokens[0];
 | |
|     var node = new nodes.Block;
 | |
|     for (var i = 0; i < tokens.length; i++) {
 | |
|       node.push(tokens[i]);
 | |
|     };
 | |
|     return node;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    *   ':' expr
 | |
|    * | block
 | |
|    */
 | |
| 
 | |
|   parseBlockExpansion: function(){
 | |
|     if (':' == this.peek().type) {
 | |
|       this.advance();
 | |
|       return new nodes.Block(this.parseExpr());
 | |
|     } else {
 | |
|       return this.block();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * case
 | |
|    */
 | |
| 
 | |
|   parseCase: function(){
 | |
|     var val = this.expect('case').val;
 | |
|     var node = new nodes.Case(val);
 | |
|     node.line = this.line();
 | |
|     node.block = this.block();
 | |
|     return node;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * when
 | |
|    */
 | |
| 
 | |
|   parseWhen: function(){
 | |
|     var val = this.expect('when').val
 | |
|     return new nodes.Case.When(val, this.parseBlockExpansion());
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * default
 | |
|    */
 | |
| 
 | |
|   parseDefault: function(){
 | |
|     this.expect('default');
 | |
|     return new nodes.Case.When('default', this.parseBlockExpansion());
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * code
 | |
|    */
 | |
| 
 | |
|   parseCode: function(){
 | |
|     var tok = this.expect('code');
 | |
|     var node = new nodes.Code(tok.val, tok.buffer, tok.escape);
 | |
|     var block;
 | |
|     var i = 1;
 | |
|     node.line = this.line();
 | |
|     while (this.lookahead(i) && 'newline' == this.lookahead(i).type) ++i;
 | |
|     block = 'indent' == this.lookahead(i).type;
 | |
|     if (block) {
 | |
|       this.skip(i-1);
 | |
|       node.block = this.block();
 | |
|     }
 | |
|     return node;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * comment
 | |
|    */
 | |
| 
 | |
|   parseComment: function(){
 | |
|     var tok = this.expect('comment');
 | |
|     var node;
 | |
| 
 | |
|     if ('indent' == this.peek().type) {
 | |
|       this.lexer.pipeless = true;
 | |
|       node = new nodes.BlockComment(tok.val, this.parseTextBlock(), tok.buffer);
 | |
|       this.lexer.pipeless = false;
 | |
|     } else {
 | |
|       node = new nodes.Comment(tok.val, tok.buffer);
 | |
|     }
 | |
| 
 | |
|     node.line = this.line();
 | |
|     return node;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * doctype
 | |
|    */
 | |
| 
 | |
|   parseDoctype: function(){
 | |
|     var tok = this.expect('doctype');
 | |
|     var node = new nodes.Doctype(tok.val);
 | |
|     node.line = this.line();
 | |
|     return node;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * filter attrs? text-block
 | |
|    */
 | |
| 
 | |
|   parseFilter: function(){
 | |
|     var tok = this.expect('filter');
 | |
|     var attrs = this.accept('attrs');
 | |
|     var block;
 | |
| 
 | |
|     if ('indent' == this.peek().type) {
 | |
|       this.lexer.pipeless = true;
 | |
|       block = this.parseTextBlock();
 | |
|       this.lexer.pipeless = false;
 | |
|     } else {
 | |
|       block = new nodes.Block;
 | |
|     }
 | |
| 
 | |
|     var options = {};
 | |
|     if (attrs) {
 | |
|       attrs.attrs.forEach(function (attribute) {
 | |
|         options[attribute.name] = constantinople.toConstant(attribute.val);
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     var node = new nodes.Filter(tok.val, block, options);
 | |
|     node.line = this.line();
 | |
|     return node;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * each block
 | |
|    */
 | |
| 
 | |
|   parseEach: function(){
 | |
|     var tok = this.expect('each');
 | |
|     var node = new nodes.Each(tok.code, tok.val, tok.key);
 | |
|     node.line = this.line();
 | |
|     node.block = this.block();
 | |
|     if (this.peek().type == 'code' && this.peek().val == 'else') {
 | |
|       this.advance();
 | |
|       node.alternative = this.block();
 | |
|     }
 | |
|     return node;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Resolves a path relative to the template for use in
 | |
|    * includes and extends
 | |
|    *
 | |
|    * @param {String}  path
 | |
|    * @param {String}  purpose  Used in error messages.
 | |
|    * @return {String}
 | |
|    * @api private
 | |
|    */
 | |
| 
 | |
|   resolvePath: function (path, purpose) {
 | |
|     var p = require('path');
 | |
|     var dirname = p.dirname;
 | |
|     var basename = p.basename;
 | |
|     var join = p.join;
 | |
| 
 | |
|     if (path[0] !== '/' && !this.filename)
 | |
|       throw new Error('the "filename" option is required to use "' + purpose + '" with "relative" paths');
 | |
| 
 | |
|     if (path[0] === '/' && !this.options.basedir)
 | |
|       throw new Error('the "basedir" option is required to use "' + purpose + '" with "absolute" paths');
 | |
| 
 | |
|     path = join(path[0] === '/' ? this.options.basedir : dirname(this.filename), path);
 | |
| 
 | |
|     if (basename(path).indexOf('.') === -1) path += '.jade';
 | |
| 
 | |
|     return path;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * 'extends' name
 | |
|    */
 | |
| 
 | |
|   parseExtends: function(){
 | |
|     var fs = require('fs');
 | |
| 
 | |
|     var path = this.resolvePath(this.expect('extends').val.trim(), 'extends');
 | |
|     if ('.jade' != path.substr(-5)) path += '.jade';
 | |
| 
 | |
|     var str = fs.readFileSync(path, 'utf8');
 | |
|     var parser = new this.constructor(str, path, this.options);
 | |
| 
 | |
|     parser.blocks = this.blocks;
 | |
|     parser.contexts = this.contexts;
 | |
|     this.extending = parser;
 | |
| 
 | |
|     // TODO: null node
 | |
|     return new nodes.Literal('');
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * 'block' name block
 | |
|    */
 | |
| 
 | |
|   parseBlock: function(){
 | |
|     var block = this.expect('block');
 | |
|     var mode = block.mode;
 | |
|     var name = block.val.trim();
 | |
| 
 | |
|     block = 'indent' == this.peek().type
 | |
|       ? this.block()
 | |
|       : new nodes.Block(new nodes.Literal(''));
 | |
| 
 | |
|     var prev = this.blocks[name] || {prepended: [], appended: []}
 | |
|     if (prev.mode === 'replace') return this.blocks[name] = prev;
 | |
| 
 | |
|     var allNodes = prev.prepended.concat(block.nodes).concat(prev.appended);
 | |
| 
 | |
|     switch (mode) {
 | |
|       case 'append':
 | |
|         prev.appended = prev.parser === this ?
 | |
|                         prev.appended.concat(block.nodes) :
 | |
|                         block.nodes.concat(prev.appended);
 | |
|         break;
 | |
|       case 'prepend':
 | |
|         prev.prepended = prev.parser === this ?
 | |
|                          block.nodes.concat(prev.prepended) :
 | |
|                          prev.prepended.concat(block.nodes);
 | |
|         break;
 | |
|     }
 | |
|     block.nodes = allNodes;
 | |
|     block.appended = prev.appended;
 | |
|     block.prepended = prev.prepended;
 | |
|     block.mode = mode;
 | |
|     block.parser = this;
 | |
| 
 | |
|     return this.blocks[name] = block;
 | |
|   },
 | |
| 
 | |
|   parseMixinBlock: function () {
 | |
|     var block = this.expect('mixin-block');
 | |
|     if (!this.inMixin) {
 | |
|       throw new Error('Anonymous blocks are not allowed unless they are part of a mixin.');
 | |
|     }
 | |
|     return new nodes.MixinBlock();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * include block?
 | |
|    */
 | |
| 
 | |
|   parseInclude: function(){
 | |
|     var fs = require('fs');
 | |
|     var tok = this.expect('include');
 | |
| 
 | |
|     var path = this.resolvePath(tok.val.trim(), 'include');
 | |
| 
 | |
|     // has-filter
 | |
|     if (tok.filter) {
 | |
|       var str = fs.readFileSync(path, 'utf8').replace(/\r/g, '');
 | |
|       str = filters(tok.filter, str, { filename: path });
 | |
|       return new nodes.Literal(str);
 | |
|     }
 | |
| 
 | |
|     // non-jade
 | |
|     if ('.jade' != path.substr(-5)) {
 | |
|       var str = fs.readFileSync(path, 'utf8').replace(/\r/g, '');
 | |
|       return new nodes.Literal(str);
 | |
|     }
 | |
| 
 | |
|     var str = fs.readFileSync(path, 'utf8');
 | |
|     var parser = new this.constructor(str, path, this.options);
 | |
|     parser.blocks = utils.merge({}, this.blocks);
 | |
| 
 | |
|     parser.mixins = this.mixins;
 | |
| 
 | |
|     this.context(parser);
 | |
|     var ast = parser.parse();
 | |
|     this.context();
 | |
|     ast.filename = path;
 | |
| 
 | |
|     if ('indent' == this.peek().type) {
 | |
|       ast.includeBlock().push(this.block());
 | |
|     }
 | |
| 
 | |
|     return ast;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * call ident block
 | |
|    */
 | |
| 
 | |
|   parseCall: function(){
 | |
|     var tok = this.expect('call');
 | |
|     var name = tok.val;
 | |
|     var args = tok.args;
 | |
|     var mixin = new nodes.Mixin(name, args, new nodes.Block, true);
 | |
| 
 | |
|     this.tag(mixin);
 | |
|     if (mixin.code) {
 | |
|       mixin.block.push(mixin.code);
 | |
|       mixin.code = null;
 | |
|     }
 | |
|     if (mixin.block.isEmpty()) mixin.block = null;
 | |
|     return mixin;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * mixin block
 | |
|    */
 | |
| 
 | |
|   parseMixin: function(){
 | |
|     var tok = this.expect('mixin');
 | |
|     var name = tok.val;
 | |
|     var args = tok.args;
 | |
|     var mixin;
 | |
| 
 | |
|     // definition
 | |
|     if ('indent' == this.peek().type) {
 | |
|       this.inMixin = true;
 | |
|       mixin = new nodes.Mixin(name, args, this.block(), false);
 | |
|       this.mixins[name] = mixin;
 | |
|       this.inMixin = false;
 | |
|       return mixin;
 | |
|     // call
 | |
|     } else {
 | |
|       return new nodes.Mixin(name, args, null, true);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   parseTextWithInlineTags: function (str) {
 | |
|     var line = this.line();
 | |
| 
 | |
|     var match = /(\\)?#\[((?:.|\n)*)$/.exec(str);
 | |
|     if (match) {
 | |
|       if (match[1]) { // escape
 | |
|         var text = new nodes.Text(str.substr(0, match.index) + '#[');
 | |
|         text.line = line;
 | |
|         var rest = this.parseTextWithInlineTags(match[2]);
 | |
|         if (rest[0].type === 'Text') {
 | |
|           text.val += rest[0].val;
 | |
|           rest.shift();
 | |
|         }
 | |
|         return [text].concat(rest);
 | |
|       } else {
 | |
|         var text = new nodes.Text(str.substr(0, match.index));
 | |
|         text.line = line;
 | |
|         var buffer = [text];
 | |
|         var rest = match[2];
 | |
|         var range = parseJSExpression(rest);
 | |
|         var inner = new Parser(range.src, this.filename, this.options);
 | |
|         buffer.push(inner.parse());
 | |
|         return buffer.concat(this.parseTextWithInlineTags(rest.substr(range.end + 1)));
 | |
|       }
 | |
|     } else {
 | |
|       var text = new nodes.Text(str);
 | |
|       text.line = line;
 | |
|       return [text];
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * indent (text | newline)* outdent
 | |
|    */
 | |
| 
 | |
|   parseTextBlock: function(){
 | |
|     var block = new nodes.Block;
 | |
|     block.line = this.line();
 | |
|     var spaces = this.expect('indent').val;
 | |
|     if (null == this._spaces) this._spaces = spaces;
 | |
|     var indent = Array(spaces - this._spaces + 1).join(' ');
 | |
|     while ('outdent' != this.peek().type) {
 | |
|       switch (this.peek().type) {
 | |
|         case 'newline':
 | |
|           this.advance();
 | |
|           break;
 | |
|         case 'indent':
 | |
|           this.parseTextBlock(true).nodes.forEach(function(node){
 | |
|             block.push(node);
 | |
|           });
 | |
|           break;
 | |
|         default:
 | |
|           var texts = this.parseTextWithInlineTags(indent + this.advance().val);
 | |
|           texts.forEach(function (text) {
 | |
|             block.push(text);
 | |
|           });
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (spaces == this._spaces) this._spaces = null;
 | |
|     this.expect('outdent');
 | |
| 
 | |
|     return block;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * indent expr* outdent
 | |
|    */
 | |
| 
 | |
|   block: function(){
 | |
|     var block = new nodes.Block;
 | |
|     block.line = this.line();
 | |
|     block.filename = this.filename;
 | |
|     this.expect('indent');
 | |
|     while ('outdent' != this.peek().type) {
 | |
|       if ('newline' == this.peek().type) {
 | |
|         this.advance();
 | |
|       } else {
 | |
|         var expr = this.parseExpr();
 | |
|         expr.filename = this.filename;
 | |
|         block.push(expr);
 | |
|       }
 | |
|     }
 | |
|     this.expect('outdent');
 | |
|     return block;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * interpolation (attrs | class | id)* (text | code | ':')? newline* block?
 | |
|    */
 | |
| 
 | |
|   parseInterpolation: function(){
 | |
|     var tok = this.advance();
 | |
|     var tag = new nodes.Tag(tok.val);
 | |
|     tag.buffer = true;
 | |
|     return this.tag(tag);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * tag (attrs | class | id)* (text | code | ':')? newline* block?
 | |
|    */
 | |
| 
 | |
|   parseTag: function(){
 | |
|     var tok = this.advance();
 | |
|     var tag = new nodes.Tag(tok.val);
 | |
| 
 | |
|     tag.selfClosing = tok.selfClosing;
 | |
| 
 | |
|     return this.tag(tag);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Parse tag.
 | |
|    */
 | |
| 
 | |
|   tag: function(tag){
 | |
|     tag.line = this.line();
 | |
| 
 | |
|     var seenAttrs = false;
 | |
|     // (attrs | class | id)*
 | |
|     out:
 | |
|       while (true) {
 | |
|         switch (this.peek().type) {
 | |
|           case 'id':
 | |
|           case 'class':
 | |
|             var tok = this.advance();
 | |
|             tag.setAttribute(tok.type, "'" + tok.val + "'");
 | |
|             continue;
 | |
|           case 'attrs':
 | |
|             if (seenAttrs) {
 | |
|               console.warn('You should not have jade tags with multiple attributes.');
 | |
|             }
 | |
|             seenAttrs = true;
 | |
|             var tok = this.advance();
 | |
|             var attrs = tok.attrs;
 | |
| 
 | |
|             if (tok.selfClosing) tag.selfClosing = true;
 | |
| 
 | |
|             for (var i = 0; i < attrs.length; i++) {
 | |
|               tag.setAttribute(attrs[i].name, attrs[i].val, attrs[i].escaped);
 | |
|             }
 | |
|             continue;
 | |
|           case '&attributes':
 | |
|             var tok = this.advance();
 | |
|             tag.addAttributes(tok.val);
 | |
|             break;
 | |
|           default:
 | |
|             break out;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|     // check immediate '.'
 | |
|     if ('dot' == this.peek().type) {
 | |
|       tag.textOnly = true;
 | |
|       this.advance();
 | |
|     }
 | |
|     
 | |
|     if (tag.selfClosing
 | |
|         && ['newline', 'outdent', 'eos'].indexOf(this.peek().type) === -1
 | |
|         && (this.peek().type !== 'text' || /^\s*$/.text(this.peek().val))) {
 | |
|       throw new Error(name + ' is self closing and should not have content.');
 | |
|     }
 | |
| 
 | |
|     // (text | code | ':')?
 | |
|     switch (this.peek().type) {
 | |
|       case 'text':
 | |
|         tag.block.push(this.parseText());
 | |
|         break;
 | |
|       case 'code':
 | |
|         tag.code = this.parseCode();
 | |
|         break;
 | |
|       case ':':
 | |
|         this.advance();
 | |
|         tag.block = new nodes.Block;
 | |
|         tag.block.push(this.parseExpr());
 | |
|         break;
 | |
|       case 'newline':
 | |
|       case 'indent':
 | |
|       case 'outdent':
 | |
|       case 'eos':
 | |
|         break;
 | |
|       default:
 | |
|         throw new Error('Unexpected token `' + this.peek().type + '` expected `text`, `code`, `:`, `newline` or `eos`')
 | |
|     }
 | |
| 
 | |
|     // newline*
 | |
|     while ('newline' == this.peek().type) this.advance();
 | |
| 
 | |
|     // block?
 | |
|     if ('indent' == this.peek().type) {
 | |
|       if (tag.textOnly) {
 | |
|         this.lexer.pipeless = true;
 | |
|         tag.block = this.parseTextBlock();
 | |
|         this.lexer.pipeless = false;
 | |
|       } else {
 | |
|         var block = this.block();
 | |
|         if (tag.block) {
 | |
|           for (var i = 0, len = block.nodes.length; i < len; ++i) {
 | |
|             tag.block.push(block.nodes[i]);
 | |
|           }
 | |
|         } else {
 | |
|           tag.block = block;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return tag;
 | |
|   }
 | |
| };
 | |
| 
 | |
| },{"./filters":3,"./lexer":6,"./nodes":16,"./utils":26,"character-parser":33,"constantinople":34,"fs":27,"path":30}],24:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| /**
 | |
|  * Merge two attribute objects giving precedence
 | |
|  * to values in object `b`. Classes are special-cased
 | |
|  * allowing for arrays and merging/joining appropriately
 | |
|  * resulting in a string.
 | |
|  *
 | |
|  * @param {Object} a
 | |
|  * @param {Object} b
 | |
|  * @return {Object} a
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| exports.merge = function merge(a, b) {
 | |
|   if (arguments.length === 1) {
 | |
|     var attrs = a[0];
 | |
|     for (var i = 1; i < a.length; i++) {
 | |
|       attrs = merge(attrs, a[i]);
 | |
|     }
 | |
|     return attrs;
 | |
|   }
 | |
|   var ac = a['class'];
 | |
|   var bc = b['class'];
 | |
| 
 | |
|   if (ac || bc) {
 | |
|     ac = ac || [];
 | |
|     bc = bc || [];
 | |
|     if (!Array.isArray(ac)) ac = [ac];
 | |
|     if (!Array.isArray(bc)) bc = [bc];
 | |
|     a['class'] = ac.concat(bc).filter(nulls);
 | |
|   }
 | |
| 
 | |
|   for (var key in b) {
 | |
|     if (key != 'class') {
 | |
|       a[key] = b[key];
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return a;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Filter null `val`s.
 | |
|  *
 | |
|  * @param {*} val
 | |
|  * @return {Boolean}
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| function nulls(val) {
 | |
|   return val != null && val !== '';
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * join array as classes.
 | |
|  *
 | |
|  * @param {*} val
 | |
|  * @return {String}
 | |
|  */
 | |
| exports.joinClasses = joinClasses;
 | |
| function joinClasses(val) {
 | |
|   return Array.isArray(val) ? val.map(joinClasses).filter(nulls).join(' ') : val;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Render the given classes.
 | |
|  *
 | |
|  * @param {Array} classes
 | |
|  * @param {Array.<Boolean>} escaped
 | |
|  * @return {String}
 | |
|  */
 | |
| exports.cls = function cls(classes, escaped) {
 | |
|   var buf = [];
 | |
|   for (var i = 0; i < classes.length; i++) {
 | |
|     if (escaped && escaped[i]) {
 | |
|       buf.push(exports.escape(joinClasses([classes[i]])));
 | |
|     } else {
 | |
|       buf.push(joinClasses(classes[i]));
 | |
|     }
 | |
|   }
 | |
|   var text = joinClasses(buf);
 | |
|   if (text.length) {
 | |
|     return ' class="' + text + '"';
 | |
|   } else {
 | |
|     return '';
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Render the given attribute.
 | |
|  *
 | |
|  * @param {String} key
 | |
|  * @param {String} val
 | |
|  * @param {Boolean} escaped
 | |
|  * @param {Boolean} terse
 | |
|  * @return {String}
 | |
|  */
 | |
| exports.attr = function attr(key, val, escaped, terse) {
 | |
|   if ('boolean' == typeof val || null == val) {
 | |
|     if (val) {
 | |
|       return ' ' + (terse ? key : key + '="' + key + '"');
 | |
|     } else {
 | |
|       return '';
 | |
|     }
 | |
|   } else if (0 == key.indexOf('data') && 'string' != typeof val) {
 | |
|     return ' ' + key + "='" + JSON.stringify(val).replace(/'/g, ''') + "'";
 | |
|   } else if (escaped) {
 | |
|     return ' ' + key + '="' + exports.escape(val) + '"';
 | |
|   } else {
 | |
|     return ' ' + key + '="' + val + '"';
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Render the given attributes object.
 | |
|  *
 | |
|  * @param {Object} obj
 | |
|  * @param {Object} escaped
 | |
|  * @return {String}
 | |
|  */
 | |
| exports.attrs = function attrs(obj, terse){
 | |
|   var buf = [];
 | |
| 
 | |
|   var keys = Object.keys(obj);
 | |
| 
 | |
|   if (keys.length) {
 | |
|     for (var i = 0; i < keys.length; ++i) {
 | |
|       var key = keys[i]
 | |
|         , val = obj[key];
 | |
| 
 | |
|       if ('class' == key) {
 | |
|         if (val = joinClasses(val)) {
 | |
|           buf.push(' ' + key + '="' + val + '"');
 | |
|         }
 | |
|       } else {
 | |
|         buf.push(exports.attr(key, val, false, terse));
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return buf.join('');
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Escape the given string of `html`.
 | |
|  *
 | |
|  * @param {String} html
 | |
|  * @return {String}
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| exports.escape = function escape(html){
 | |
|   var result = String(html)
 | |
|     .replace(/&/g, '&')
 | |
|     .replace(/</g, '<')
 | |
|     .replace(/>/g, '>')
 | |
|     .replace(/"/g, '"');
 | |
|   if (result === '' + html) return html;
 | |
|   else return result;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Re-throw the given `err` in context to the
 | |
|  * the jade in `filename` at the given `lineno`.
 | |
|  *
 | |
|  * @param {Error} err
 | |
|  * @param {String} filename
 | |
|  * @param {String} lineno
 | |
|  * @api private
 | |
|  */
 | |
| 
 | |
| exports.rethrow = function rethrow(err, filename, lineno, str){
 | |
|   if (!(err instanceof Error)) throw err;
 | |
|   if ((typeof window != 'undefined' || !filename) && !str) {
 | |
|     err.message += ' on line ' + lineno;
 | |
|     throw err;
 | |
|   }
 | |
|   try {
 | |
|     str =  str || require('fs').readFileSync(filename, 'utf8')
 | |
|   } catch (ex) {
 | |
|     rethrow(err, null, lineno)
 | |
|   }
 | |
|   var context = 3
 | |
|     , lines = str.split('\n')
 | |
|     , start = Math.max(lineno - context, 0)
 | |
|     , end = Math.min(lines.length, lineno + context);
 | |
| 
 | |
|   // Error context
 | |
|   var context = lines.slice(start, end).map(function(line, i){
 | |
|     var curr = i + start + 1;
 | |
|     return (curr == lineno ? '  > ' : '    ')
 | |
|       + curr
 | |
|       + '| '
 | |
|       + line;
 | |
|   }).join('\n');
 | |
| 
 | |
|   // Alter exception message
 | |
|   err.path = filename;
 | |
|   err.message = (filename || 'Jade') + ':' + lineno
 | |
|     + '\n' + context + '\n\n' + err.message;
 | |
|   throw err;
 | |
| };
 | |
| 
 | |
| },{"fs":27}],25:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| // source: http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
 | |
| 
 | |
| module.exports = [
 | |
|     'area'
 | |
|   , 'base'
 | |
|   , 'br'
 | |
|   , 'col'
 | |
|   , 'embed'
 | |
|   , 'hr'
 | |
|   , 'img'
 | |
|   , 'input'
 | |
|   , 'keygen'
 | |
|   , 'link'
 | |
|   , 'menuitem'
 | |
|   , 'meta'
 | |
|   , 'param'
 | |
|   , 'source'
 | |
|   , 'track'
 | |
|   , 'wbr'
 | |
| ];
 | |
| 
 | |
| },{}],26:[function(require,module,exports){
 | |
| 'use strict';
 | |
| 
 | |
| /**
 | |
|  * Merge `b` into `a`.
 | |
|  *
 | |
|  * @param {Object} a
 | |
|  * @param {Object} b
 | |
|  * @return {Object}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| exports.merge = function(a, b) {
 | |
|   for (var key in b) a[key] = b[key];
 | |
|   return a;
 | |
| };
 | |
| 
 | |
| 
 | |
| },{}],27:[function(require,module,exports){
 | |
| 
 | |
| },{}],28:[function(require,module,exports){
 | |
| if (typeof Object.create === 'function') {
 | |
|   // implementation from standard node.js 'util' module
 | |
|   module.exports = function inherits(ctor, superCtor) {
 | |
|     ctor.super_ = superCtor
 | |
|     ctor.prototype = Object.create(superCtor.prototype, {
 | |
|       constructor: {
 | |
|         value: ctor,
 | |
|         enumerable: false,
 | |
|         writable: true,
 | |
|         configurable: true
 | |
|       }
 | |
|     });
 | |
|   };
 | |
| } else {
 | |
|   // old school shim for old browsers
 | |
|   module.exports = function inherits(ctor, superCtor) {
 | |
|     ctor.super_ = superCtor
 | |
|     var TempCtor = function () {}
 | |
|     TempCtor.prototype = superCtor.prototype
 | |
|     ctor.prototype = new TempCtor()
 | |
|     ctor.prototype.constructor = ctor
 | |
|   }
 | |
| }
 | |
| 
 | |
| },{}],29:[function(require,module,exports){
 | |
| // shim for using process in browser
 | |
| 
 | |
| var process = module.exports = {};
 | |
| 
 | |
| process.nextTick = (function () {
 | |
|     var canSetImmediate = typeof window !== 'undefined'
 | |
|     && window.setImmediate;
 | |
|     var canPost = typeof window !== 'undefined'
 | |
|     && window.postMessage && window.addEventListener
 | |
|     ;
 | |
| 
 | |
|     if (canSetImmediate) {
 | |
|         return function (f) { return window.setImmediate(f) };
 | |
|     }
 | |
| 
 | |
|     if (canPost) {
 | |
|         var queue = [];
 | |
|         window.addEventListener('message', function (ev) {
 | |
|             var source = ev.source;
 | |
|             if ((source === window || source === null) && ev.data === 'process-tick') {
 | |
|                 ev.stopPropagation();
 | |
|                 if (queue.length > 0) {
 | |
|                     var fn = queue.shift();
 | |
|                     fn();
 | |
|                 }
 | |
|             }
 | |
|         }, true);
 | |
| 
 | |
|         return function nextTick(fn) {
 | |
|             queue.push(fn);
 | |
|             window.postMessage('process-tick', '*');
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     return function nextTick(fn) {
 | |
|         setTimeout(fn, 0);
 | |
|     };
 | |
| })();
 | |
| 
 | |
| process.title = 'browser';
 | |
| process.browser = true;
 | |
| process.env = {};
 | |
| process.argv = [];
 | |
| 
 | |
| process.binding = function (name) {
 | |
|     throw new Error('process.binding is not supported');
 | |
| }
 | |
| 
 | |
| // TODO(shtylman)
 | |
| process.cwd = function () { return '/' };
 | |
| process.chdir = function (dir) {
 | |
|     throw new Error('process.chdir is not supported');
 | |
| };
 | |
| 
 | |
| },{}],30:[function(require,module,exports){
 | |
| var process=require("__browserify_process");// Copyright Joyent, Inc. and other Node contributors.
 | |
| //
 | |
| // Permission is hereby granted, free of charge, to any person obtaining a
 | |
| // copy of this software and associated documentation files (the
 | |
| // "Software"), to deal in the Software without restriction, including
 | |
| // without limitation the rights to use, copy, modify, merge, publish,
 | |
| // distribute, sublicense, and/or sell copies of the Software, and to permit
 | |
| // persons to whom the Software is furnished to do so, subject to the
 | |
| // following conditions:
 | |
| //
 | |
| // The above copyright notice and this permission notice shall be included
 | |
| // in all copies or substantial portions of the Software.
 | |
| //
 | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 | |
| // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | |
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 | |
| // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 | |
| // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 | |
| // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 | |
| // USE OR OTHER DEALINGS IN THE SOFTWARE.
 | |
| 
 | |
| // resolves . and .. elements in a path array with directory names there
 | |
| // must be no slashes, empty elements, or device names (c:\) in the array
 | |
| // (so also no leading and trailing slashes - it does not distinguish
 | |
| // relative and absolute paths)
 | |
| function normalizeArray(parts, allowAboveRoot) {
 | |
|   // if the path tries to go above the root, `up` ends up > 0
 | |
|   var up = 0;
 | |
|   for (var i = parts.length - 1; i >= 0; i--) {
 | |
|     var last = parts[i];
 | |
|     if (last === '.') {
 | |
|       parts.splice(i, 1);
 | |
|     } else if (last === '..') {
 | |
|       parts.splice(i, 1);
 | |
|       up++;
 | |
|     } else if (up) {
 | |
|       parts.splice(i, 1);
 | |
|       up--;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // if the path is allowed to go above the root, restore leading ..s
 | |
|   if (allowAboveRoot) {
 | |
|     for (; up--; up) {
 | |
|       parts.unshift('..');
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return parts;
 | |
| }
 | |
| 
 | |
| // Split a filename into [root, dir, basename, ext], unix version
 | |
| // 'root' is just a slash, or nothing.
 | |
| var splitPathRe =
 | |
|     /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
 | |
| var splitPath = function(filename) {
 | |
|   return splitPathRe.exec(filename).slice(1);
 | |
| };
 | |
| 
 | |
| // path.resolve([from ...], to)
 | |
| // posix version
 | |
| exports.resolve = function() {
 | |
|   var resolvedPath = '',
 | |
|       resolvedAbsolute = false;
 | |
| 
 | |
|   for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
 | |
|     var path = (i >= 0) ? arguments[i] : process.cwd();
 | |
| 
 | |
|     // Skip empty and invalid entries
 | |
|     if (typeof path !== 'string') {
 | |
|       throw new TypeError('Arguments to path.resolve must be strings');
 | |
|     } else if (!path) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     resolvedPath = path + '/' + resolvedPath;
 | |
|     resolvedAbsolute = path.charAt(0) === '/';
 | |
|   }
 | |
| 
 | |
|   // At this point the path should be resolved to a full absolute path, but
 | |
|   // handle relative paths to be safe (might happen when process.cwd() fails)
 | |
| 
 | |
|   // Normalize the path
 | |
|   resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
 | |
|     return !!p;
 | |
|   }), !resolvedAbsolute).join('/');
 | |
| 
 | |
|   return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
 | |
| };
 | |
| 
 | |
| // path.normalize(path)
 | |
| // posix version
 | |
| exports.normalize = function(path) {
 | |
|   var isAbsolute = exports.isAbsolute(path),
 | |
|       trailingSlash = substr(path, -1) === '/';
 | |
| 
 | |
|   // Normalize the path
 | |
|   path = normalizeArray(filter(path.split('/'), function(p) {
 | |
|     return !!p;
 | |
|   }), !isAbsolute).join('/');
 | |
| 
 | |
|   if (!path && !isAbsolute) {
 | |
|     path = '.';
 | |
|   }
 | |
|   if (path && trailingSlash) {
 | |
|     path += '/';
 | |
|   }
 | |
| 
 | |
|   return (isAbsolute ? '/' : '') + path;
 | |
| };
 | |
| 
 | |
| // posix version
 | |
| exports.isAbsolute = function(path) {
 | |
|   return path.charAt(0) === '/';
 | |
| };
 | |
| 
 | |
| // posix version
 | |
| exports.join = function() {
 | |
|   var paths = Array.prototype.slice.call(arguments, 0);
 | |
|   return exports.normalize(filter(paths, function(p, index) {
 | |
|     if (typeof p !== 'string') {
 | |
|       throw new TypeError('Arguments to path.join must be strings');
 | |
|     }
 | |
|     return p;
 | |
|   }).join('/'));
 | |
| };
 | |
| 
 | |
| 
 | |
| // path.relative(from, to)
 | |
| // posix version
 | |
| exports.relative = function(from, to) {
 | |
|   from = exports.resolve(from).substr(1);
 | |
|   to = exports.resolve(to).substr(1);
 | |
| 
 | |
|   function trim(arr) {
 | |
|     var start = 0;
 | |
|     for (; start < arr.length; start++) {
 | |
|       if (arr[start] !== '') break;
 | |
|     }
 | |
| 
 | |
|     var end = arr.length - 1;
 | |
|     for (; end >= 0; end--) {
 | |
|       if (arr[end] !== '') break;
 | |
|     }
 | |
| 
 | |
|     if (start > end) return [];
 | |
|     return arr.slice(start, end - start + 1);
 | |
|   }
 | |
| 
 | |
|   var fromParts = trim(from.split('/'));
 | |
|   var toParts = trim(to.split('/'));
 | |
| 
 | |
|   var length = Math.min(fromParts.length, toParts.length);
 | |
|   var samePartsLength = length;
 | |
|   for (var i = 0; i < length; i++) {
 | |
|     if (fromParts[i] !== toParts[i]) {
 | |
|       samePartsLength = i;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   var outputParts = [];
 | |
|   for (var i = samePartsLength; i < fromParts.length; i++) {
 | |
|     outputParts.push('..');
 | |
|   }
 | |
| 
 | |
|   outputParts = outputParts.concat(toParts.slice(samePartsLength));
 | |
| 
 | |
|   return outputParts.join('/');
 | |
| };
 | |
| 
 | |
| exports.sep = '/';
 | |
| exports.delimiter = ':';
 | |
| 
 | |
| exports.dirname = function(path) {
 | |
|   var result = splitPath(path),
 | |
|       root = result[0],
 | |
|       dir = result[1];
 | |
| 
 | |
|   if (!root && !dir) {
 | |
|     // No dirname whatsoever
 | |
|     return '.';
 | |
|   }
 | |
| 
 | |
|   if (dir) {
 | |
|     // It has a dirname, strip trailing slash
 | |
|     dir = dir.substr(0, dir.length - 1);
 | |
|   }
 | |
| 
 | |
|   return root + dir;
 | |
| };
 | |
| 
 | |
| 
 | |
| exports.basename = function(path, ext) {
 | |
|   var f = splitPath(path)[2];
 | |
|   // TODO: make this comparison case-insensitive on windows?
 | |
|   if (ext && f.substr(-1 * ext.length) === ext) {
 | |
|     f = f.substr(0, f.length - ext.length);
 | |
|   }
 | |
|   return f;
 | |
| };
 | |
| 
 | |
| 
 | |
| exports.extname = function(path) {
 | |
|   return splitPath(path)[3];
 | |
| };
 | |
| 
 | |
| function filter (xs, f) {
 | |
|     if (xs.filter) return xs.filter(f);
 | |
|     var res = [];
 | |
|     for (var i = 0; i < xs.length; i++) {
 | |
|         if (f(xs[i], i, xs)) res.push(xs[i]);
 | |
|     }
 | |
|     return res;
 | |
| }
 | |
| 
 | |
| // String.prototype.substr - negative index don't work in IE8
 | |
| var substr = 'ab'.substr(-1) === 'b'
 | |
|     ? function (str, start, len) { return str.substr(start, len) }
 | |
|     : function (str, start, len) {
 | |
|         if (start < 0) start = str.length + start;
 | |
|         return str.substr(start, len);
 | |
|     }
 | |
| ;
 | |
| 
 | |
| },{"__browserify_process":29}],31:[function(require,module,exports){
 | |
| module.exports = function isBuffer(arg) {
 | |
|   return arg && typeof arg === 'object'
 | |
|     && typeof arg.copy === 'function'
 | |
|     && typeof arg.fill === 'function'
 | |
|     && typeof arg.readUInt8 === 'function';
 | |
| }
 | |
| },{}],32:[function(require,module,exports){
 | |
| var process=require("__browserify_process"),global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};// Copyright Joyent, Inc. and other Node contributors.
 | |
| //
 | |
| // Permission is hereby granted, free of charge, to any person obtaining a
 | |
| // copy of this software and associated documentation files (the
 | |
| // "Software"), to deal in the Software without restriction, including
 | |
| // without limitation the rights to use, copy, modify, merge, publish,
 | |
| // distribute, sublicense, and/or sell copies of the Software, and to permit
 | |
| // persons to whom the Software is furnished to do so, subject to the
 | |
| // following conditions:
 | |
| //
 | |
| // The above copyright notice and this permission notice shall be included
 | |
| // in all copies or substantial portions of the Software.
 | |
| //
 | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 | |
| // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | |
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 | |
| // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 | |
| // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 | |
| // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 | |
| // USE OR OTHER DEALINGS IN THE SOFTWARE.
 | |
| 
 | |
| var formatRegExp = /%[sdj%]/g;
 | |
| exports.format = function(f) {
 | |
|   if (!isString(f)) {
 | |
|     var objects = [];
 | |
|     for (var i = 0; i < arguments.length; i++) {
 | |
|       objects.push(inspect(arguments[i]));
 | |
|     }
 | |
|     return objects.join(' ');
 | |
|   }
 | |
| 
 | |
|   var i = 1;
 | |
|   var args = arguments;
 | |
|   var len = args.length;
 | |
|   var str = String(f).replace(formatRegExp, function(x) {
 | |
|     if (x === '%%') return '%';
 | |
|     if (i >= len) return x;
 | |
|     switch (x) {
 | |
|       case '%s': return String(args[i++]);
 | |
|       case '%d': return Number(args[i++]);
 | |
|       case '%j':
 | |
|         try {
 | |
|           return JSON.stringify(args[i++]);
 | |
|         } catch (_) {
 | |
|           return '[Circular]';
 | |
|         }
 | |
|       default:
 | |
|         return x;
 | |
|     }
 | |
|   });
 | |
|   for (var x = args[i]; i < len; x = args[++i]) {
 | |
|     if (isNull(x) || !isObject(x)) {
 | |
|       str += ' ' + x;
 | |
|     } else {
 | |
|       str += ' ' + inspect(x);
 | |
|     }
 | |
|   }
 | |
|   return str;
 | |
| };
 | |
| 
 | |
| 
 | |
| // Mark that a method should not be used.
 | |
| // Returns a modified function which warns once by default.
 | |
| // If --no-deprecation is set, then it is a no-op.
 | |
| exports.deprecate = function(fn, msg) {
 | |
|   // Allow for deprecating things in the process of starting up.
 | |
|   if (isUndefined(global.process)) {
 | |
|     return function() {
 | |
|       return exports.deprecate(fn, msg).apply(this, arguments);
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   if (process.noDeprecation === true) {
 | |
|     return fn;
 | |
|   }
 | |
| 
 | |
|   var warned = false;
 | |
|   function deprecated() {
 | |
|     if (!warned) {
 | |
|       if (process.throwDeprecation) {
 | |
|         throw new Error(msg);
 | |
|       } else if (process.traceDeprecation) {
 | |
|         console.trace(msg);
 | |
|       } else {
 | |
|         console.error(msg);
 | |
|       }
 | |
|       warned = true;
 | |
|     }
 | |
|     return fn.apply(this, arguments);
 | |
|   }
 | |
| 
 | |
|   return deprecated;
 | |
| };
 | |
| 
 | |
| 
 | |
| var debugs = {};
 | |
| var debugEnviron;
 | |
| exports.debuglog = function(set) {
 | |
|   if (isUndefined(debugEnviron))
 | |
|     debugEnviron = process.env.NODE_DEBUG || '';
 | |
|   set = set.toUpperCase();
 | |
|   if (!debugs[set]) {
 | |
|     if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
 | |
|       var pid = process.pid;
 | |
|       debugs[set] = function() {
 | |
|         var msg = exports.format.apply(exports, arguments);
 | |
|         console.error('%s %d: %s', set, pid, msg);
 | |
|       };
 | |
|     } else {
 | |
|       debugs[set] = function() {};
 | |
|     }
 | |
|   }
 | |
|   return debugs[set];
 | |
| };
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Echos the value of a value. Trys to print the value out
 | |
|  * in the best way possible given the different types.
 | |
|  *
 | |
|  * @param {Object} obj The object to print out.
 | |
|  * @param {Object} opts Optional options object that alters the output.
 | |
|  */
 | |
| /* legacy: obj, showHidden, depth, colors*/
 | |
| function inspect(obj, opts) {
 | |
|   // default options
 | |
|   var ctx = {
 | |
|     seen: [],
 | |
|     stylize: stylizeNoColor
 | |
|   };
 | |
|   // legacy...
 | |
|   if (arguments.length >= 3) ctx.depth = arguments[2];
 | |
|   if (arguments.length >= 4) ctx.colors = arguments[3];
 | |
|   if (isBoolean(opts)) {
 | |
|     // legacy...
 | |
|     ctx.showHidden = opts;
 | |
|   } else if (opts) {
 | |
|     // got an "options" object
 | |
|     exports._extend(ctx, opts);
 | |
|   }
 | |
|   // set default options
 | |
|   if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
 | |
|   if (isUndefined(ctx.depth)) ctx.depth = 2;
 | |
|   if (isUndefined(ctx.colors)) ctx.colors = false;
 | |
|   if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
 | |
|   if (ctx.colors) ctx.stylize = stylizeWithColor;
 | |
|   return formatValue(ctx, obj, ctx.depth);
 | |
| }
 | |
| exports.inspect = inspect;
 | |
| 
 | |
| 
 | |
| // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
 | |
| inspect.colors = {
 | |
|   'bold' : [1, 22],
 | |
|   'italic' : [3, 23],
 | |
|   'underline' : [4, 24],
 | |
|   'inverse' : [7, 27],
 | |
|   'white' : [37, 39],
 | |
|   'grey' : [90, 39],
 | |
|   'black' : [30, 39],
 | |
|   'blue' : [34, 39],
 | |
|   'cyan' : [36, 39],
 | |
|   'green' : [32, 39],
 | |
|   'magenta' : [35, 39],
 | |
|   'red' : [31, 39],
 | |
|   'yellow' : [33, 39]
 | |
| };
 | |
| 
 | |
| // Don't use 'blue' not visible on cmd.exe
 | |
| inspect.styles = {
 | |
|   'special': 'cyan',
 | |
|   'number': 'yellow',
 | |
|   'boolean': 'yellow',
 | |
|   'undefined': 'grey',
 | |
|   'null': 'bold',
 | |
|   'string': 'green',
 | |
|   'date': 'magenta',
 | |
|   // "name": intentionally not styling
 | |
|   'regexp': 'red'
 | |
| };
 | |
| 
 | |
| 
 | |
| function stylizeWithColor(str, styleType) {
 | |
|   var style = inspect.styles[styleType];
 | |
| 
 | |
|   if (style) {
 | |
|     return '\u001b[' + inspect.colors[style][0] + 'm' + str +
 | |
|            '\u001b[' + inspect.colors[style][1] + 'm';
 | |
|   } else {
 | |
|     return str;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| function stylizeNoColor(str, styleType) {
 | |
|   return str;
 | |
| }
 | |
| 
 | |
| 
 | |
| function arrayToHash(array) {
 | |
|   var hash = {};
 | |
| 
 | |
|   array.forEach(function(val, idx) {
 | |
|     hash[val] = true;
 | |
|   });
 | |
| 
 | |
|   return hash;
 | |
| }
 | |
| 
 | |
| 
 | |
| function formatValue(ctx, value, recurseTimes) {
 | |
|   // Provide a hook for user-specified inspect functions.
 | |
|   // Check that value is an object with an inspect function on it
 | |
|   if (ctx.customInspect &&
 | |
|       value &&
 | |
|       isFunction(value.inspect) &&
 | |
|       // Filter out the util module, it's inspect function is special
 | |
|       value.inspect !== exports.inspect &&
 | |
|       // Also filter out any prototype objects using the circular check.
 | |
|       !(value.constructor && value.constructor.prototype === value)) {
 | |
|     var ret = value.inspect(recurseTimes, ctx);
 | |
|     if (!isString(ret)) {
 | |
|       ret = formatValue(ctx, ret, recurseTimes);
 | |
|     }
 | |
|     return ret;
 | |
|   }
 | |
| 
 | |
|   // Primitive types cannot have properties
 | |
|   var primitive = formatPrimitive(ctx, value);
 | |
|   if (primitive) {
 | |
|     return primitive;
 | |
|   }
 | |
| 
 | |
|   // Look up the keys of the object.
 | |
|   var keys = Object.keys(value);
 | |
|   var visibleKeys = arrayToHash(keys);
 | |
| 
 | |
|   if (ctx.showHidden) {
 | |
|     keys = Object.getOwnPropertyNames(value);
 | |
|   }
 | |
| 
 | |
|   // IE doesn't make error fields non-enumerable
 | |
|   // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
 | |
|   if (isError(value)
 | |
|       && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
 | |
|     return formatError(value);
 | |
|   }
 | |
| 
 | |
|   // Some type of object without properties can be shortcutted.
 | |
|   if (keys.length === 0) {
 | |
|     if (isFunction(value)) {
 | |
|       var name = value.name ? ': ' + value.name : '';
 | |
|       return ctx.stylize('[Function' + name + ']', 'special');
 | |
|     }
 | |
|     if (isRegExp(value)) {
 | |
|       return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
 | |
|     }
 | |
|     if (isDate(value)) {
 | |
|       return ctx.stylize(Date.prototype.toString.call(value), 'date');
 | |
|     }
 | |
|     if (isError(value)) {
 | |
|       return formatError(value);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   var base = '', array = false, braces = ['{', '}'];
 | |
| 
 | |
|   // Make Array say that they are Array
 | |
|   if (isArray(value)) {
 | |
|     array = true;
 | |
|     braces = ['[', ']'];
 | |
|   }
 | |
| 
 | |
|   // Make functions say that they are functions
 | |
|   if (isFunction(value)) {
 | |
|     var n = value.name ? ': ' + value.name : '';
 | |
|     base = ' [Function' + n + ']';
 | |
|   }
 | |
| 
 | |
|   // Make RegExps say that they are RegExps
 | |
|   if (isRegExp(value)) {
 | |
|     base = ' ' + RegExp.prototype.toString.call(value);
 | |
|   }
 | |
| 
 | |
|   // Make dates with properties first say the date
 | |
|   if (isDate(value)) {
 | |
|     base = ' ' + Date.prototype.toUTCString.call(value);
 | |
|   }
 | |
| 
 | |
|   // Make error with message first say the error
 | |
|   if (isError(value)) {
 | |
|     base = ' ' + formatError(value);
 | |
|   }
 | |
| 
 | |
|   if (keys.length === 0 && (!array || value.length == 0)) {
 | |
|     return braces[0] + base + braces[1];
 | |
|   }
 | |
| 
 | |
|   if (recurseTimes < 0) {
 | |
|     if (isRegExp(value)) {
 | |
|       return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
 | |
|     } else {
 | |
|       return ctx.stylize('[Object]', 'special');
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ctx.seen.push(value);
 | |
| 
 | |
|   var output;
 | |
|   if (array) {
 | |
|     output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
 | |
|   } else {
 | |
|     output = keys.map(function(key) {
 | |
|       return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   ctx.seen.pop();
 | |
| 
 | |
|   return reduceToSingleString(output, base, braces);
 | |
| }
 | |
| 
 | |
| 
 | |
| function formatPrimitive(ctx, value) {
 | |
|   if (isUndefined(value))
 | |
|     return ctx.stylize('undefined', 'undefined');
 | |
|   if (isString(value)) {
 | |
|     var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
 | |
|                                              .replace(/'/g, "\\'")
 | |
|                                              .replace(/\\"/g, '"') + '\'';
 | |
|     return ctx.stylize(simple, 'string');
 | |
|   }
 | |
|   if (isNumber(value))
 | |
|     return ctx.stylize('' + value, 'number');
 | |
|   if (isBoolean(value))
 | |
|     return ctx.stylize('' + value, 'boolean');
 | |
|   // For some reason typeof null is "object", so special case here.
 | |
|   if (isNull(value))
 | |
|     return ctx.stylize('null', 'null');
 | |
| }
 | |
| 
 | |
| 
 | |
| function formatError(value) {
 | |
|   return '[' + Error.prototype.toString.call(value) + ']';
 | |
| }
 | |
| 
 | |
| 
 | |
| function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
 | |
|   var output = [];
 | |
|   for (var i = 0, l = value.length; i < l; ++i) {
 | |
|     if (hasOwnProperty(value, String(i))) {
 | |
|       output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
 | |
|           String(i), true));
 | |
|     } else {
 | |
|       output.push('');
 | |
|     }
 | |
|   }
 | |
|   keys.forEach(function(key) {
 | |
|     if (!key.match(/^\d+$/)) {
 | |
|       output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
 | |
|           key, true));
 | |
|     }
 | |
|   });
 | |
|   return output;
 | |
| }
 | |
| 
 | |
| 
 | |
| function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
 | |
|   var name, str, desc;
 | |
|   desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
 | |
|   if (desc.get) {
 | |
|     if (desc.set) {
 | |
|       str = ctx.stylize('[Getter/Setter]', 'special');
 | |
|     } else {
 | |
|       str = ctx.stylize('[Getter]', 'special');
 | |
|     }
 | |
|   } else {
 | |
|     if (desc.set) {
 | |
|       str = ctx.stylize('[Setter]', 'special');
 | |
|     }
 | |
|   }
 | |
|   if (!hasOwnProperty(visibleKeys, key)) {
 | |
|     name = '[' + key + ']';
 | |
|   }
 | |
|   if (!str) {
 | |
|     if (ctx.seen.indexOf(desc.value) < 0) {
 | |
|       if (isNull(recurseTimes)) {
 | |
|         str = formatValue(ctx, desc.value, null);
 | |
|       } else {
 | |
|         str = formatValue(ctx, desc.value, recurseTimes - 1);
 | |
|       }
 | |
|       if (str.indexOf('\n') > -1) {
 | |
|         if (array) {
 | |
|           str = str.split('\n').map(function(line) {
 | |
|             return '  ' + line;
 | |
|           }).join('\n').substr(2);
 | |
|         } else {
 | |
|           str = '\n' + str.split('\n').map(function(line) {
 | |
|             return '   ' + line;
 | |
|           }).join('\n');
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       str = ctx.stylize('[Circular]', 'special');
 | |
|     }
 | |
|   }
 | |
|   if (isUndefined(name)) {
 | |
|     if (array && key.match(/^\d+$/)) {
 | |
|       return str;
 | |
|     }
 | |
|     name = JSON.stringify('' + key);
 | |
|     if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
 | |
|       name = name.substr(1, name.length - 2);
 | |
|       name = ctx.stylize(name, 'name');
 | |
|     } else {
 | |
|       name = name.replace(/'/g, "\\'")
 | |
|                  .replace(/\\"/g, '"')
 | |
|                  .replace(/(^"|"$)/g, "'");
 | |
|       name = ctx.stylize(name, 'string');
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return name + ': ' + str;
 | |
| }
 | |
| 
 | |
| 
 | |
| function reduceToSingleString(output, base, braces) {
 | |
|   var numLinesEst = 0;
 | |
|   var length = output.reduce(function(prev, cur) {
 | |
|     numLinesEst++;
 | |
|     if (cur.indexOf('\n') >= 0) numLinesEst++;
 | |
|     return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
 | |
|   }, 0);
 | |
| 
 | |
|   if (length > 60) {
 | |
|     return braces[0] +
 | |
|            (base === '' ? '' : base + '\n ') +
 | |
|            ' ' +
 | |
|            output.join(',\n  ') +
 | |
|            ' ' +
 | |
|            braces[1];
 | |
|   }
 | |
| 
 | |
|   return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
 | |
| }
 | |
| 
 | |
| 
 | |
| // NOTE: These type checking functions intentionally don't use `instanceof`
 | |
| // because it is fragile and can be easily faked with `Object.create()`.
 | |
| function isArray(ar) {
 | |
|   return Array.isArray(ar);
 | |
| }
 | |
| exports.isArray = isArray;
 | |
| 
 | |
| function isBoolean(arg) {
 | |
|   return typeof arg === 'boolean';
 | |
| }
 | |
| exports.isBoolean = isBoolean;
 | |
| 
 | |
| function isNull(arg) {
 | |
|   return arg === null;
 | |
| }
 | |
| exports.isNull = isNull;
 | |
| 
 | |
| function isNullOrUndefined(arg) {
 | |
|   return arg == null;
 | |
| }
 | |
| exports.isNullOrUndefined = isNullOrUndefined;
 | |
| 
 | |
| function isNumber(arg) {
 | |
|   return typeof arg === 'number';
 | |
| }
 | |
| exports.isNumber = isNumber;
 | |
| 
 | |
| function isString(arg) {
 | |
|   return typeof arg === 'string';
 | |
| }
 | |
| exports.isString = isString;
 | |
| 
 | |
| function isSymbol(arg) {
 | |
|   return typeof arg === 'symbol';
 | |
| }
 | |
| exports.isSymbol = isSymbol;
 | |
| 
 | |
| function isUndefined(arg) {
 | |
|   return arg === void 0;
 | |
| }
 | |
| exports.isUndefined = isUndefined;
 | |
| 
 | |
| function isRegExp(re) {
 | |
|   return isObject(re) && objectToString(re) === '[object RegExp]';
 | |
| }
 | |
| exports.isRegExp = isRegExp;
 | |
| 
 | |
| function isObject(arg) {
 | |
|   return typeof arg === 'object' && arg !== null;
 | |
| }
 | |
| exports.isObject = isObject;
 | |
| 
 | |
| function isDate(d) {
 | |
|   return isObject(d) && objectToString(d) === '[object Date]';
 | |
| }
 | |
| exports.isDate = isDate;
 | |
| 
 | |
| function isError(e) {
 | |
|   return isObject(e) &&
 | |
|       (objectToString(e) === '[object Error]' || e instanceof Error);
 | |
| }
 | |
| exports.isError = isError;
 | |
| 
 | |
| function isFunction(arg) {
 | |
|   return typeof arg === 'function';
 | |
| }
 | |
| exports.isFunction = isFunction;
 | |
| 
 | |
| function isPrimitive(arg) {
 | |
|   return arg === null ||
 | |
|          typeof arg === 'boolean' ||
 | |
|          typeof arg === 'number' ||
 | |
|          typeof arg === 'string' ||
 | |
|          typeof arg === 'symbol' ||  // ES6 symbol
 | |
|          typeof arg === 'undefined';
 | |
| }
 | |
| exports.isPrimitive = isPrimitive;
 | |
| 
 | |
| exports.isBuffer = require('./support/isBuffer');
 | |
| 
 | |
| function objectToString(o) {
 | |
|   return Object.prototype.toString.call(o);
 | |
| }
 | |
| 
 | |
| 
 | |
| function pad(n) {
 | |
|   return n < 10 ? '0' + n.toString(10) : n.toString(10);
 | |
| }
 | |
| 
 | |
| 
 | |
| var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
 | |
|               'Oct', 'Nov', 'Dec'];
 | |
| 
 | |
| // 26 Feb 16:19:34
 | |
| function timestamp() {
 | |
|   var d = new Date();
 | |
|   var time = [pad(d.getHours()),
 | |
|               pad(d.getMinutes()),
 | |
|               pad(d.getSeconds())].join(':');
 | |
|   return [d.getDate(), months[d.getMonth()], time].join(' ');
 | |
| }
 | |
| 
 | |
| 
 | |
| // log is just a thin wrapper to console.log that prepends a timestamp
 | |
| exports.log = function() {
 | |
|   console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
 | |
| };
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Inherit the prototype methods from one constructor into another.
 | |
|  *
 | |
|  * The Function.prototype.inherits from lang.js rewritten as a standalone
 | |
|  * function (not on Function.prototype). NOTE: If this file is to be loaded
 | |
|  * during bootstrapping this function needs to be rewritten using some native
 | |
|  * functions as prototype setup using normal JavaScript does not work as
 | |
|  * expected during bootstrapping (see mirror.js in r114903).
 | |
|  *
 | |
|  * @param {function} ctor Constructor function which needs to inherit the
 | |
|  *     prototype.
 | |
|  * @param {function} superCtor Constructor function to inherit prototype from.
 | |
|  */
 | |
| exports.inherits = require('inherits');
 | |
| 
 | |
| exports._extend = function(origin, add) {
 | |
|   // Don't do anything if add isn't an object
 | |
|   if (!add || !isObject(add)) return origin;
 | |
| 
 | |
|   var keys = Object.keys(add);
 | |
|   var i = keys.length;
 | |
|   while (i--) {
 | |
|     origin[keys[i]] = add[keys[i]];
 | |
|   }
 | |
|   return origin;
 | |
| };
 | |
| 
 | |
| function hasOwnProperty(obj, prop) {
 | |
|   return Object.prototype.hasOwnProperty.call(obj, prop);
 | |
| }
 | |
| 
 | |
| },{"./support/isBuffer":31,"__browserify_process":29,"inherits":28}],33:[function(require,module,exports){
 | |
| exports = (module.exports = parse);
 | |
| exports.parse = parse;
 | |
| function parse(src, state, options) {
 | |
|   options = options || {};
 | |
|   state = state || exports.defaultState();
 | |
|   var start = options.start || 0;
 | |
|   var end = options.end || src.length;
 | |
|   var index = start;
 | |
|   while (index < end) {
 | |
|     if (state.roundDepth < 0 || state.curlyDepth < 0 || state.squareDepth < 0) {
 | |
|       throw new SyntaxError('Mismatched Bracket: ' + src[index - 1]);
 | |
|     }
 | |
|     exports.parseChar(src[index++], state);
 | |
|   }
 | |
|   return state;
 | |
| }
 | |
| 
 | |
| exports.parseMax = parseMax;
 | |
| function parseMax(src, options) {
 | |
|   options = options || {};
 | |
|   var start = options.start || 0;
 | |
|   var index = start;
 | |
|   var state = exports.defaultState();
 | |
|   while (state.roundDepth >= 0 && state.curlyDepth >= 0 && state.squareDepth >= 0) {
 | |
|     if (index >= src.length) {
 | |
|       throw new Error('The end of the string was reached with no closing bracket found.');
 | |
|     }
 | |
|     exports.parseChar(src[index++], state);
 | |
|   }
 | |
|   var end = index - 1;
 | |
|   return {
 | |
|     start: start,
 | |
|     end: end,
 | |
|     src: src.substring(start, end)
 | |
|   };
 | |
| }
 | |
| 
 | |
| exports.parseUntil = parseUntil;
 | |
| function parseUntil(src, delimiter, options) {
 | |
|   options = options || {};
 | |
|   var includeLineComment = options.includeLineComment || false;
 | |
|   var start = options.start || 0;
 | |
|   var index = start;
 | |
|   var state = exports.defaultState();
 | |
|   while (state.isString() || state.regexp || state.blockComment ||
 | |
|          (!includeLineComment && state.lineComment) || !startsWith(src, delimiter, index)) {
 | |
|     exports.parseChar(src[index++], state);
 | |
|   }
 | |
|   var end = index;
 | |
|   return {
 | |
|     start: start,
 | |
|     end: end,
 | |
|     src: src.substring(start, end)
 | |
|   };
 | |
| }
 | |
| 
 | |
| 
 | |
| exports.parseChar = parseChar;
 | |
| function parseChar(character, state) {
 | |
|   if (character.length !== 1) throw new Error('Character must be a string of length 1');
 | |
|   state = state || exports.defaultState();
 | |
|   var wasComment = state.blockComment || state.lineComment;
 | |
|   var lastChar = state.history ? state.history[0] : '';
 | |
|   if (state.lineComment) {
 | |
|     if (character === '\n') {
 | |
|       state.lineComment = false;
 | |
|     }
 | |
|   } else if (state.blockComment) {
 | |
|     if (state.lastChar === '*' && character === '/') {
 | |
|       state.blockComment = false;
 | |
|     }
 | |
|   } else if (state.singleQuote) {
 | |
|     if (character === '\'' && !state.escaped) {
 | |
|       state.singleQuote = false;
 | |
|     } else if (character === '\\' && !state.escaped) {
 | |
|       state.escaped = true;
 | |
|     } else {
 | |
|       state.escaped = false;
 | |
|     }
 | |
|   } else if (state.doubleQuote) {
 | |
|     if (character === '"' && !state.escaped) {
 | |
|       state.doubleQuote = false;
 | |
|     } else if (character === '\\' && !state.escaped) {
 | |
|       state.escaped = true;
 | |
|     } else {
 | |
|       state.escaped = false;
 | |
|     }
 | |
|   } else if (state.regexp) {
 | |
|     if (character === '/' && !state.escaped) {
 | |
|       state.regexp = false;
 | |
|     } else if (character === '\\' && !state.escaped) {
 | |
|       state.escaped = true;
 | |
|     } else {
 | |
|       state.escaped = false;
 | |
|     }
 | |
|   } else if (lastChar === '/' && character === '/') {
 | |
|     state.history = state.history.substr(1);
 | |
|     state.lineComment = true;
 | |
|   } else if (lastChar === '/' && character === '*') {
 | |
|     state.history = state.history.substr(1);
 | |
|     state.blockComment = true;
 | |
|   } else if (character === '/' && isRegexp(state.history)) {
 | |
|     state.regexp = true;
 | |
|   } else if (character === '\'') {
 | |
|     state.singleQuote = true;
 | |
|   } else if (character === '"') {
 | |
|     state.doubleQuote = true;
 | |
|   } else if (character === '(') {
 | |
|     state.roundDepth++;
 | |
|   } else if (character === ')') {
 | |
|     state.roundDepth--;
 | |
|   } else if (character === '{') {
 | |
|     state.curlyDepth++;
 | |
|   } else if (character === '}') {
 | |
|     state.curlyDepth--;
 | |
|   } else if (character === '[') {
 | |
|     state.squareDepth++;
 | |
|   } else if (character === ']') {
 | |
|     state.squareDepth--;
 | |
|   }
 | |
|   if (!state.blockComment && !state.lineComment && !wasComment) state.history = character + state.history;
 | |
|   return state;
 | |
| }
 | |
| 
 | |
| exports.defaultState = function () { return new State() };
 | |
| function State() {
 | |
|   this.lineComment = false;
 | |
|   this.blockComment = false;
 | |
| 
 | |
|   this.singleQuote = false;
 | |
|   this.doubleQuote = false;
 | |
|   this.regexp = false;
 | |
|   this.escaped = false;
 | |
| 
 | |
|   this.roundDepth = 0;
 | |
|   this.curlyDepth = 0;
 | |
|   this.squareDepth = 0;
 | |
| 
 | |
|   this.history = ''
 | |
| }
 | |
| State.prototype.isString = function () {
 | |
|   return this.singleQuote || this.doubleQuote;
 | |
| }
 | |
| State.prototype.isComment = function () {
 | |
|   return this.lineComment || this.blockComment;
 | |
| }
 | |
| State.prototype.isNesting = function () {
 | |
|   return this.isString() || this.isComment() || this.regexp || this.roundDepth > 0 || this.curlyDepth > 0 || this.squareDepth > 0
 | |
| }
 | |
| 
 | |
| function startsWith(str, start, i) {
 | |
|   return str.substr(i || 0, start.length) === start;
 | |
| }
 | |
| 
 | |
| exports.isPunctuator = isPunctuator
 | |
| function isPunctuator(c) {
 | |
|   var code = c.charCodeAt(0)
 | |
| 
 | |
|   switch (code) {
 | |
|     case 46:   // . dot
 | |
|     case 40:   // ( open bracket
 | |
|     case 41:   // ) close bracket
 | |
|     case 59:   // ; semicolon
 | |
|     case 44:   // , comma
 | |
|     case 123:  // { open curly brace
 | |
|     case 125:  // } close curly brace
 | |
|     case 91:   // [
 | |
|     case 93:   // ]
 | |
|     case 58:   // :
 | |
|     case 63:   // ?
 | |
|     case 126:  // ~
 | |
|     case 37:   // %
 | |
|     case 38:   // &
 | |
|     case 42:   // *:
 | |
|     case 43:   // +
 | |
|     case 45:   // -
 | |
|     case 47:   // /
 | |
|     case 60:   // <
 | |
|     case 62:   // >
 | |
|     case 94:   // ^
 | |
|     case 124:  // |
 | |
|     case 33:   // !
 | |
|     case 61:   // =
 | |
|       return true;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| exports.isKeyword = isKeyword
 | |
| function isKeyword(id) {
 | |
|   return (id === 'if') || (id === 'in') || (id === 'do') || (id === 'var') || (id === 'for') || (id === 'new') ||
 | |
|          (id === 'try') || (id === 'let') || (id === 'this') || (id === 'else') || (id === 'case') ||
 | |
|          (id === 'void') || (id === 'with') || (id === 'enum') || (id === 'while') || (id === 'break') || (id === 'catch') ||
 | |
|          (id === 'throw') || (id === 'const') || (id === 'yield') || (id === 'class') || (id === 'super') ||
 | |
|          (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch') || (id === 'export') ||
 | |
|          (id === 'import') || (id === 'default') || (id === 'finally') || (id === 'extends') || (id === 'function') ||
 | |
|          (id === 'continue') || (id === 'debugger') || (id === 'package') || (id === 'private') || (id === 'interface') ||
 | |
|          (id === 'instanceof') || (id === 'implements') || (id === 'protected') || (id === 'public') || (id === 'static') ||
 | |
|          (id === 'yield') || (id === 'let');
 | |
| }
 | |
| 
 | |
| function isRegexp(history) {
 | |
|   //could be start of regexp or divide sign
 | |
| 
 | |
|   history = history.replace(/^\s*/, '');
 | |
| 
 | |
|   //unless its an `if`, `while`, `for` or `with` it's a divide, so we assume it's a divide
 | |
|   if (history[0] === ')') return false;
 | |
|   //unless it's a function expression, it's a regexp, so we assume it's a regexp
 | |
|   if (history[0] === '}') return true;
 | |
|   //any punctuation means it's a regexp
 | |
|   if (isPunctuator(history[0])) return true;
 | |
|   //if the last thing was a keyword then it must be a regexp (e.g. `typeof /foo/`)
 | |
|   if (/^\w+\b/.test(history) && isKeyword(/^\w+\b/.exec(history)[0].split('').reverse().join(''))) return true;
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| },{}],34:[function(require,module,exports){
 | |
| 'use strict'
 | |
| 
 | |
| var uglify = require('uglify-js')
 | |
| 
 | |
| var lastSRC = '(null)'
 | |
| var lastRes = true
 | |
| 
 | |
| module.exports = isConstant
 | |
| function isConstant(src) {
 | |
|   src = '(' + src + ')'
 | |
|   if (lastSRC === src) return lastRes
 | |
|   lastSRC = src
 | |
|   try {
 | |
|     return lastRes = (detect(src).length === 0)
 | |
|   } catch (ex) {
 | |
|     return lastRes = false
 | |
|   }
 | |
| }
 | |
| isConstant.isConstant = isConstant
 | |
| 
 | |
| isConstant.toConstant = toConstant
 | |
| function toConstant(src) {
 | |
|   if (!isConstant(src)) throw new Error(JSON.stringify(src) + ' is not constant.')
 | |
|   return Function('return (' + src + ')')()
 | |
| }
 | |
| 
 | |
| function detect(src) {
 | |
|   var ast = uglify.parse(src.toString())
 | |
|   ast.figure_out_scope()
 | |
|   var globals = ast.globals
 | |
|     .map(function (node, name) {
 | |
|       return name
 | |
|     })
 | |
|   return globals
 | |
| }
 | |
| },{"uglify-js":45}],35:[function(require,module,exports){
 | |
| /*
 | |
|  * Copyright 2009-2011 Mozilla Foundation and contributors
 | |
|  * Licensed under the New BSD license. See LICENSE.txt or:
 | |
|  * http://opensource.org/licenses/BSD-3-Clause
 | |
|  */
 | |
| exports.SourceMapGenerator = require('./source-map/source-map-generator').SourceMapGenerator;
 | |
| exports.SourceMapConsumer = require('./source-map/source-map-consumer').SourceMapConsumer;
 | |
| exports.SourceNode = require('./source-map/source-node').SourceNode;
 | |
| 
 | |
| },{"./source-map/source-map-consumer":40,"./source-map/source-map-generator":41,"./source-map/source-node":42}],36:[function(require,module,exports){
 | |
| /* -*- Mode: js; js-indent-level: 2; -*- */
 | |
| /*
 | |
|  * Copyright 2011 Mozilla Foundation and contributors
 | |
|  * Licensed under the New BSD license. See LICENSE or:
 | |
|  * http://opensource.org/licenses/BSD-3-Clause
 | |
|  */
 | |
| if (typeof define !== 'function') {
 | |
|     var define = require('amdefine')(module, require);
 | |
| }
 | |
| define(function (require, exports, module) {
 | |
| 
 | |
|   var util = require('./util');
 | |
| 
 | |
|   /**
 | |
|    * A data structure which is a combination of an array and a set. Adding a new
 | |
|    * member is O(1), testing for membership is O(1), and finding the index of an
 | |
|    * element is O(1). Removing elements from the set is not supported. Only
 | |
|    * strings are supported for membership.
 | |
|    */
 | |
|   function ArraySet() {
 | |
|     this._array = [];
 | |
|     this._set = {};
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Static method for creating ArraySet instances from an existing array.
 | |
|    */
 | |
|   ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
 | |
|     var set = new ArraySet();
 | |
|     for (var i = 0, len = aArray.length; i < len; i++) {
 | |
|       set.add(aArray[i], aAllowDuplicates);
 | |
|     }
 | |
|     return set;
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Add the given string to this set.
 | |
|    *
 | |
|    * @param String aStr
 | |
|    */
 | |
|   ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
 | |
|     var isDuplicate = this.has(aStr);
 | |
|     var idx = this._array.length;
 | |
|     if (!isDuplicate || aAllowDuplicates) {
 | |
|       this._array.push(aStr);
 | |
|     }
 | |
|     if (!isDuplicate) {
 | |
|       this._set[util.toSetString(aStr)] = idx;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Is the given string a member of this set?
 | |
|    *
 | |
|    * @param String aStr
 | |
|    */
 | |
|   ArraySet.prototype.has = function ArraySet_has(aStr) {
 | |
|     return Object.prototype.hasOwnProperty.call(this._set,
 | |
|                                                 util.toSetString(aStr));
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * What is the index of the given string in the array?
 | |
|    *
 | |
|    * @param String aStr
 | |
|    */
 | |
|   ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
 | |
|     if (this.has(aStr)) {
 | |
|       return this._set[util.toSetString(aStr)];
 | |
|     }
 | |
|     throw new Error('"' + aStr + '" is not in the set.');
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * What is the element at the given index?
 | |
|    *
 | |
|    * @param Number aIdx
 | |
|    */
 | |
|   ArraySet.prototype.at = function ArraySet_at(aIdx) {
 | |
|     if (aIdx >= 0 && aIdx < this._array.length) {
 | |
|       return this._array[aIdx];
 | |
|     }
 | |
|     throw new Error('No element indexed by ' + aIdx);
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Returns the array representation of this set (which has the proper indices
 | |
|    * indicated by indexOf). Note that this is a copy of the internal array used
 | |
|    * for storing the members so that no one can mess with internal state.
 | |
|    */
 | |
|   ArraySet.prototype.toArray = function ArraySet_toArray() {
 | |
|     return this._array.slice();
 | |
|   };
 | |
| 
 | |
|   exports.ArraySet = ArraySet;
 | |
| 
 | |
| });
 | |
| 
 | |
| },{"./util":43,"amdefine":44}],37:[function(require,module,exports){
 | |
| /* -*- Mode: js; js-indent-level: 2; -*- */
 | |
| /*
 | |
|  * Copyright 2011 Mozilla Foundation and contributors
 | |
|  * Licensed under the New BSD license. See LICENSE or:
 | |
|  * http://opensource.org/licenses/BSD-3-Clause
 | |
|  *
 | |
|  * Based on the Base 64 VLQ implementation in Closure Compiler:
 | |
|  * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
 | |
|  *
 | |
|  * Copyright 2011 The Closure Compiler Authors. All rights reserved.
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions are
 | |
|  * met:
 | |
|  *
 | |
|  *  * Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  *  * Redistributions in binary form must reproduce the above
 | |
|  *    copyright notice, this list of conditions and the following
 | |
|  *    disclaimer in the documentation and/or other materials provided
 | |
|  *    with the distribution.
 | |
|  *  * Neither the name of Google Inc. nor the names of its
 | |
|  *    contributors may be used to endorse or promote products derived
 | |
|  *    from this software without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
|  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | |
|  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | |
|  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | |
|  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | |
|  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | |
|  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | |
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | |
|  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  */
 | |
| if (typeof define !== 'function') {
 | |
|     var define = require('amdefine')(module, require);
 | |
| }
 | |
| define(function (require, exports, module) {
 | |
| 
 | |
|   var base64 = require('./base64');
 | |
| 
 | |
|   // A single base 64 digit can contain 6 bits of data. For the base 64 variable
 | |
|   // length quantities we use in the source map spec, the first bit is the sign,
 | |
|   // the next four bits are the actual value, and the 6th bit is the
 | |
|   // continuation bit. The continuation bit tells us whether there are more
 | |
|   // digits in this value following this digit.
 | |
|   //
 | |
|   //   Continuation
 | |
|   //   |    Sign
 | |
|   //   |    |
 | |
|   //   V    V
 | |
|   //   101011
 | |
| 
 | |
|   var VLQ_BASE_SHIFT = 5;
 | |
| 
 | |
|   // binary: 100000
 | |
|   var VLQ_BASE = 1 << VLQ_BASE_SHIFT;
 | |
| 
 | |
|   // binary: 011111
 | |
|   var VLQ_BASE_MASK = VLQ_BASE - 1;
 | |
| 
 | |
|   // binary: 100000
 | |
|   var VLQ_CONTINUATION_BIT = VLQ_BASE;
 | |
| 
 | |
|   /**
 | |
|    * Converts from a two-complement value to a value where the sign bit is
 | |
|    * is placed in the least significant bit.  For example, as decimals:
 | |
|    *   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
 | |
|    *   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
 | |
|    */
 | |
|   function toVLQSigned(aValue) {
 | |
|     return aValue < 0
 | |
|       ? ((-aValue) << 1) + 1
 | |
|       : (aValue << 1) + 0;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Converts to a two-complement value from a value where the sign bit is
 | |
|    * is placed in the least significant bit.  For example, as decimals:
 | |
|    *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
 | |
|    *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
 | |
|    */
 | |
|   function fromVLQSigned(aValue) {
 | |
|     var isNegative = (aValue & 1) === 1;
 | |
|     var shifted = aValue >> 1;
 | |
|     return isNegative
 | |
|       ? -shifted
 | |
|       : shifted;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the base 64 VLQ encoded value.
 | |
|    */
 | |
|   exports.encode = function base64VLQ_encode(aValue) {
 | |
|     var encoded = "";
 | |
|     var digit;
 | |
| 
 | |
|     var vlq = toVLQSigned(aValue);
 | |
| 
 | |
|     do {
 | |
|       digit = vlq & VLQ_BASE_MASK;
 | |
|       vlq >>>= VLQ_BASE_SHIFT;
 | |
|       if (vlq > 0) {
 | |
|         // There are still more digits in this value, so we must make sure the
 | |
|         // continuation bit is marked.
 | |
|         digit |= VLQ_CONTINUATION_BIT;
 | |
|       }
 | |
|       encoded += base64.encode(digit);
 | |
|     } while (vlq > 0);
 | |
| 
 | |
|     return encoded;
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Decodes the next base 64 VLQ value from the given string and returns the
 | |
|    * value and the rest of the string.
 | |
|    */
 | |
|   exports.decode = function base64VLQ_decode(aStr) {
 | |
|     var i = 0;
 | |
|     var strLen = aStr.length;
 | |
|     var result = 0;
 | |
|     var shift = 0;
 | |
|     var continuation, digit;
 | |
| 
 | |
|     do {
 | |
|       if (i >= strLen) {
 | |
|         throw new Error("Expected more digits in base 64 VLQ value.");
 | |
|       }
 | |
|       digit = base64.decode(aStr.charAt(i++));
 | |
|       continuation = !!(digit & VLQ_CONTINUATION_BIT);
 | |
|       digit &= VLQ_BASE_MASK;
 | |
|       result = result + (digit << shift);
 | |
|       shift += VLQ_BASE_SHIFT;
 | |
|     } while (continuation);
 | |
| 
 | |
|     return {
 | |
|       value: fromVLQSigned(result),
 | |
|       rest: aStr.slice(i)
 | |
|     };
 | |
|   };
 | |
| 
 | |
| });
 | |
| 
 | |
| },{"./base64":38,"amdefine":44}],38:[function(require,module,exports){
 | |
| /* -*- Mode: js; js-indent-level: 2; -*- */
 | |
| /*
 | |
|  * Copyright 2011 Mozilla Foundation and contributors
 | |
|  * Licensed under the New BSD license. See LICENSE or:
 | |
|  * http://opensource.org/licenses/BSD-3-Clause
 | |
|  */
 | |
| if (typeof define !== 'function') {
 | |
|     var define = require('amdefine')(module, require);
 | |
| }
 | |
| define(function (require, exports, module) {
 | |
| 
 | |
|   var charToIntMap = {};
 | |
|   var intToCharMap = {};
 | |
| 
 | |
|   'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
 | |
|     .split('')
 | |
|     .forEach(function (ch, index) {
 | |
|       charToIntMap[ch] = index;
 | |
|       intToCharMap[index] = ch;
 | |
|     });
 | |
| 
 | |
|   /**
 | |
|    * Encode an integer in the range of 0 to 63 to a single base 64 digit.
 | |
|    */
 | |
|   exports.encode = function base64_encode(aNumber) {
 | |
|     if (aNumber in intToCharMap) {
 | |
|       return intToCharMap[aNumber];
 | |
|     }
 | |
|     throw new TypeError("Must be between 0 and 63: " + aNumber);
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Decode a single base 64 digit to an integer.
 | |
|    */
 | |
|   exports.decode = function base64_decode(aChar) {
 | |
|     if (aChar in charToIntMap) {
 | |
|       return charToIntMap[aChar];
 | |
|     }
 | |
|     throw new TypeError("Not a valid base 64 digit: " + aChar);
 | |
|   };
 | |
| 
 | |
| });
 | |
| 
 | |
| },{"amdefine":44}],39:[function(require,module,exports){
 | |
| /* -*- Mode: js; js-indent-level: 2; -*- */
 | |
| /*
 | |
|  * Copyright 2011 Mozilla Foundation and contributors
 | |
|  * Licensed under the New BSD license. See LICENSE or:
 | |
|  * http://opensource.org/licenses/BSD-3-Clause
 | |
|  */
 | |
| if (typeof define !== 'function') {
 | |
|     var define = require('amdefine')(module, require);
 | |
| }
 | |
| define(function (require, exports, module) {
 | |
| 
 | |
|   /**
 | |
|    * Recursive implementation of binary search.
 | |
|    *
 | |
|    * @param aLow Indices here and lower do not contain the needle.
 | |
|    * @param aHigh Indices here and higher do not contain the needle.
 | |
|    * @param aNeedle The element being searched for.
 | |
|    * @param aHaystack The non-empty array being searched.
 | |
|    * @param aCompare Function which takes two elements and returns -1, 0, or 1.
 | |
|    */
 | |
|   function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) {
 | |
|     // This function terminates when one of the following is true:
 | |
|     //
 | |
|     //   1. We find the exact element we are looking for.
 | |
|     //
 | |
|     //   2. We did not find the exact element, but we can return the next
 | |
|     //      closest element that is less than that element.
 | |
|     //
 | |
|     //   3. We did not find the exact element, and there is no next-closest
 | |
|     //      element which is less than the one we are searching for, so we
 | |
|     //      return null.
 | |
|     var mid = Math.floor((aHigh - aLow) / 2) + aLow;
 | |
|     var cmp = aCompare(aNeedle, aHaystack[mid], true);
 | |
|     if (cmp === 0) {
 | |
|       // Found the element we are looking for.
 | |
|       return aHaystack[mid];
 | |
|     }
 | |
|     else if (cmp > 0) {
 | |
|       // aHaystack[mid] is greater than our needle.
 | |
|       if (aHigh - mid > 1) {
 | |
|         // The element is in the upper half.
 | |
|         return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare);
 | |
|       }
 | |
|       // We did not find an exact match, return the next closest one
 | |
|       // (termination case 2).
 | |
|       return aHaystack[mid];
 | |
|     }
 | |
|     else {
 | |
|       // aHaystack[mid] is less than our needle.
 | |
|       if (mid - aLow > 1) {
 | |
|         // The element is in the lower half.
 | |
|         return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare);
 | |
|       }
 | |
|       // The exact needle element was not found in this haystack. Determine if
 | |
|       // we are in termination case (2) or (3) and return the appropriate thing.
 | |
|       return aLow < 0
 | |
|         ? null
 | |
|         : aHaystack[aLow];
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * This is an implementation of binary search which will always try and return
 | |
|    * the next lowest value checked if there is no exact hit. This is because
 | |
|    * mappings between original and generated line/col pairs are single points,
 | |
|    * and there is an implicit region between each of them, so a miss just means
 | |
|    * that you aren't on the very start of a region.
 | |
|    *
 | |
|    * @param aNeedle The element you are looking for.
 | |
|    * @param aHaystack The array that is being searched.
 | |
|    * @param aCompare A function which takes the needle and an element in the
 | |
|    *     array and returns -1, 0, or 1 depending on whether the needle is less
 | |
|    *     than, equal to, or greater than the element, respectively.
 | |
|    */
 | |
|   exports.search = function search(aNeedle, aHaystack, aCompare) {
 | |
|     return aHaystack.length > 0
 | |
|       ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare)
 | |
|       : null;
 | |
|   };
 | |
| 
 | |
| });
 | |
| 
 | |
| },{"amdefine":44}],40:[function(require,module,exports){
 | |
| /* -*- Mode: js; js-indent-level: 2; -*- */
 | |
| /*
 | |
|  * Copyright 2011 Mozilla Foundation and contributors
 | |
|  * Licensed under the New BSD license. See LICENSE or:
 | |
|  * http://opensource.org/licenses/BSD-3-Clause
 | |
|  */
 | |
| if (typeof define !== 'function') {
 | |
|     var define = require('amdefine')(module, require);
 | |
| }
 | |
| define(function (require, exports, module) {
 | |
| 
 | |
|   var util = require('./util');
 | |
|   var binarySearch = require('./binary-search');
 | |
|   var ArraySet = require('./array-set').ArraySet;
 | |
|   var base64VLQ = require('./base64-vlq');
 | |
| 
 | |
|   /**
 | |
|    * A SourceMapConsumer instance represents a parsed source map which we can
 | |
|    * query for information about the original file positions by giving it a file
 | |
|    * position in the generated source.
 | |
|    *
 | |
|    * The only parameter is the raw source map (either as a JSON string, or
 | |
|    * already parsed to an object). According to the spec, source maps have the
 | |
|    * following attributes:
 | |
|    *
 | |
|    *   - version: Which version of the source map spec this map is following.
 | |
|    *   - sources: An array of URLs to the original source files.
 | |
|    *   - names: An array of identifiers which can be referrenced by individual mappings.
 | |
|    *   - sourceRoot: Optional. The URL root from which all sources are relative.
 | |
|    *   - sourcesContent: Optional. An array of contents of the original source files.
 | |
|    *   - mappings: A string of base64 VLQs which contain the actual mappings.
 | |
|    *   - file: The generated file this source map is associated with.
 | |
|    *
 | |
|    * Here is an example source map, taken from the source map spec[0]:
 | |
|    *
 | |
|    *     {
 | |
|    *       version : 3,
 | |
|    *       file: "out.js",
 | |
|    *       sourceRoot : "",
 | |
|    *       sources: ["foo.js", "bar.js"],
 | |
|    *       names: ["src", "maps", "are", "fun"],
 | |
|    *       mappings: "AA,AB;;ABCDE;"
 | |
|    *     }
 | |
|    *
 | |
|    * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
 | |
|    */
 | |
|   function SourceMapConsumer(aSourceMap) {
 | |
|     var sourceMap = aSourceMap;
 | |
|     if (typeof aSourceMap === 'string') {
 | |
|       sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
 | |
|     }
 | |
| 
 | |
|     var version = util.getArg(sourceMap, 'version');
 | |
|     var sources = util.getArg(sourceMap, 'sources');
 | |
|     // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
 | |
|     // requires the array) to play nice here.
 | |
|     var names = util.getArg(sourceMap, 'names', []);
 | |
|     var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
 | |
|     var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
 | |
|     var mappings = util.getArg(sourceMap, 'mappings');
 | |
|     var file = util.getArg(sourceMap, 'file', null);
 | |
| 
 | |
|     // Once again, Sass deviates from the spec and supplies the version as a
 | |
|     // string rather than a number, so we use loose equality checking here.
 | |
|     if (version != this._version) {
 | |
|       throw new Error('Unsupported version: ' + version);
 | |
|     }
 | |
| 
 | |
|     // Pass `true` below to allow duplicate names and sources. While source maps
 | |
|     // are intended to be compressed and deduplicated, the TypeScript compiler
 | |
|     // sometimes generates source maps with duplicates in them. See Github issue
 | |
|     // #72 and bugzil.la/889492.
 | |
|     this._names = ArraySet.fromArray(names, true);
 | |
|     this._sources = ArraySet.fromArray(sources, true);
 | |
| 
 | |
|     this.sourceRoot = sourceRoot;
 | |
|     this.sourcesContent = sourcesContent;
 | |
|     this._mappings = mappings;
 | |
|     this.file = file;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Create a SourceMapConsumer from a SourceMapGenerator.
 | |
|    *
 | |
|    * @param SourceMapGenerator aSourceMap
 | |
|    *        The source map that will be consumed.
 | |
|    * @returns SourceMapConsumer
 | |
|    */
 | |
|   SourceMapConsumer.fromSourceMap =
 | |
|     function SourceMapConsumer_fromSourceMap(aSourceMap) {
 | |
|       var smc = Object.create(SourceMapConsumer.prototype);
 | |
| 
 | |
|       smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
 | |
|       smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
 | |
|       smc.sourceRoot = aSourceMap._sourceRoot;
 | |
|       smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
 | |
|                                                               smc.sourceRoot);
 | |
|       smc.file = aSourceMap._file;
 | |
| 
 | |
|       smc.__generatedMappings = aSourceMap._mappings.slice()
 | |
|         .sort(util.compareByGeneratedPositions);
 | |
|       smc.__originalMappings = aSourceMap._mappings.slice()
 | |
|         .sort(util.compareByOriginalPositions);
 | |
| 
 | |
|       return smc;
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * The version of the source mapping spec that we are consuming.
 | |
|    */
 | |
|   SourceMapConsumer.prototype._version = 3;
 | |
| 
 | |
|   /**
 | |
|    * The list of original sources.
 | |
|    */
 | |
|   Object.defineProperty(SourceMapConsumer.prototype, 'sources', {
 | |
|     get: function () {
 | |
|       return this._sources.toArray().map(function (s) {
 | |
|         return this.sourceRoot ? util.join(this.sourceRoot, s) : s;
 | |
|       }, this);
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   // `__generatedMappings` and `__originalMappings` are arrays that hold the
 | |
|   // parsed mapping coordinates from the source map's "mappings" attribute. They
 | |
|   // are lazily instantiated, accessed via the `_generatedMappings` and
 | |
|   // `_originalMappings` getters respectively, and we only parse the mappings
 | |
|   // and create these arrays once queried for a source location. We jump through
 | |
|   // these hoops because there can be many thousands of mappings, and parsing
 | |
|   // them is expensive, so we only want to do it if we must.
 | |
|   //
 | |
|   // Each object in the arrays is of the form:
 | |
|   //
 | |
|   //     {
 | |
|   //       generatedLine: The line number in the generated code,
 | |
|   //       generatedColumn: The column number in the generated code,
 | |
|   //       source: The path to the original source file that generated this
 | |
|   //               chunk of code,
 | |
|   //       originalLine: The line number in the original source that
 | |
|   //                     corresponds to this chunk of generated code,
 | |
|   //       originalColumn: The column number in the original source that
 | |
|   //                       corresponds to this chunk of generated code,
 | |
|   //       name: The name of the original symbol which generated this chunk of
 | |
|   //             code.
 | |
|   //     }
 | |
|   //
 | |
|   // All properties except for `generatedLine` and `generatedColumn` can be
 | |
|   // `null`.
 | |
|   //
 | |
|   // `_generatedMappings` is ordered by the generated positions.
 | |
|   //
 | |
|   // `_originalMappings` is ordered by the original positions.
 | |
| 
 | |
|   SourceMapConsumer.prototype.__generatedMappings = null;
 | |
|   Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
 | |
|     get: function () {
 | |
|       if (!this.__generatedMappings) {
 | |
|         this.__generatedMappings = [];
 | |
|         this.__originalMappings = [];
 | |
|         this._parseMappings(this._mappings, this.sourceRoot);
 | |
|       }
 | |
| 
 | |
|       return this.__generatedMappings;
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   SourceMapConsumer.prototype.__originalMappings = null;
 | |
|   Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
 | |
|     get: function () {
 | |
|       if (!this.__originalMappings) {
 | |
|         this.__generatedMappings = [];
 | |
|         this.__originalMappings = [];
 | |
|         this._parseMappings(this._mappings, this.sourceRoot);
 | |
|       }
 | |
| 
 | |
|       return this.__originalMappings;
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   /**
 | |
|    * Parse the mappings in a string in to a data structure which we can easily
 | |
|    * query (the ordered arrays in the `this.__generatedMappings` and
 | |
|    * `this.__originalMappings` properties).
 | |
|    */
 | |
|   SourceMapConsumer.prototype._parseMappings =
 | |
|     function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
 | |
|       var generatedLine = 1;
 | |
|       var previousGeneratedColumn = 0;
 | |
|       var previousOriginalLine = 0;
 | |
|       var previousOriginalColumn = 0;
 | |
|       var previousSource = 0;
 | |
|       var previousName = 0;
 | |
|       var mappingSeparator = /^[,;]/;
 | |
|       var str = aStr;
 | |
|       var mapping;
 | |
|       var temp;
 | |
| 
 | |
|       while (str.length > 0) {
 | |
|         if (str.charAt(0) === ';') {
 | |
|           generatedLine++;
 | |
|           str = str.slice(1);
 | |
|           previousGeneratedColumn = 0;
 | |
|         }
 | |
|         else if (str.charAt(0) === ',') {
 | |
|           str = str.slice(1);
 | |
|         }
 | |
|         else {
 | |
|           mapping = {};
 | |
|           mapping.generatedLine = generatedLine;
 | |
| 
 | |
|           // Generated column.
 | |
|           temp = base64VLQ.decode(str);
 | |
|           mapping.generatedColumn = previousGeneratedColumn + temp.value;
 | |
|           previousGeneratedColumn = mapping.generatedColumn;
 | |
|           str = temp.rest;
 | |
| 
 | |
|           if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
 | |
|             // Original source.
 | |
|             temp = base64VLQ.decode(str);
 | |
|             mapping.source = this._sources.at(previousSource + temp.value);
 | |
|             previousSource += temp.value;
 | |
|             str = temp.rest;
 | |
|             if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
 | |
|               throw new Error('Found a source, but no line and column');
 | |
|             }
 | |
| 
 | |
|             // Original line.
 | |
|             temp = base64VLQ.decode(str);
 | |
|             mapping.originalLine = previousOriginalLine + temp.value;
 | |
|             previousOriginalLine = mapping.originalLine;
 | |
|             // Lines are stored 0-based
 | |
|             mapping.originalLine += 1;
 | |
|             str = temp.rest;
 | |
|             if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
 | |
|               throw new Error('Found a source and line, but no column');
 | |
|             }
 | |
| 
 | |
|             // Original column.
 | |
|             temp = base64VLQ.decode(str);
 | |
|             mapping.originalColumn = previousOriginalColumn + temp.value;
 | |
|             previousOriginalColumn = mapping.originalColumn;
 | |
|             str = temp.rest;
 | |
| 
 | |
|             if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
 | |
|               // Original name.
 | |
|               temp = base64VLQ.decode(str);
 | |
|               mapping.name = this._names.at(previousName + temp.value);
 | |
|               previousName += temp.value;
 | |
|               str = temp.rest;
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           this.__generatedMappings.push(mapping);
 | |
|           if (typeof mapping.originalLine === 'number') {
 | |
|             this.__originalMappings.push(mapping);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       this.__originalMappings.sort(util.compareByOriginalPositions);
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Find the mapping that best matches the hypothetical "needle" mapping that
 | |
|    * we are searching for in the given "haystack" of mappings.
 | |
|    */
 | |
|   SourceMapConsumer.prototype._findMapping =
 | |
|     function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
 | |
|                                            aColumnName, aComparator) {
 | |
|       // To return the position we are searching for, we must first find the
 | |
|       // mapping for the given position and then return the opposite position it
 | |
|       // points to. Because the mappings are sorted, we can use binary search to
 | |
|       // find the best mapping.
 | |
| 
 | |
|       if (aNeedle[aLineName] <= 0) {
 | |
|         throw new TypeError('Line must be greater than or equal to 1, got '
 | |
|                             + aNeedle[aLineName]);
 | |
|       }
 | |
|       if (aNeedle[aColumnName] < 0) {
 | |
|         throw new TypeError('Column must be greater than or equal to 0, got '
 | |
|                             + aNeedle[aColumnName]);
 | |
|       }
 | |
| 
 | |
|       return binarySearch.search(aNeedle, aMappings, aComparator);
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Returns the original source, line, and column information for the generated
 | |
|    * source's line and column positions provided. The only argument is an object
 | |
|    * with the following properties:
 | |
|    *
 | |
|    *   - line: The line number in the generated source.
 | |
|    *   - column: The column number in the generated source.
 | |
|    *
 | |
|    * and an object is returned with the following properties:
 | |
|    *
 | |
|    *   - source: The original source file, or null.
 | |
|    *   - line: The line number in the original source, or null.
 | |
|    *   - column: The column number in the original source, or null.
 | |
|    *   - name: The original identifier, or null.
 | |
|    */
 | |
|   SourceMapConsumer.prototype.originalPositionFor =
 | |
|     function SourceMapConsumer_originalPositionFor(aArgs) {
 | |
|       var needle = {
 | |
|         generatedLine: util.getArg(aArgs, 'line'),
 | |
|         generatedColumn: util.getArg(aArgs, 'column')
 | |
|       };
 | |
| 
 | |
|       var mapping = this._findMapping(needle,
 | |
|                                       this._generatedMappings,
 | |
|                                       "generatedLine",
 | |
|                                       "generatedColumn",
 | |
|                                       util.compareByGeneratedPositions);
 | |
| 
 | |
|       if (mapping) {
 | |
|         var source = util.getArg(mapping, 'source', null);
 | |
|         if (source && this.sourceRoot) {
 | |
|           source = util.join(this.sourceRoot, source);
 | |
|         }
 | |
|         return {
 | |
|           source: source,
 | |
|           line: util.getArg(mapping, 'originalLine', null),
 | |
|           column: util.getArg(mapping, 'originalColumn', null),
 | |
|           name: util.getArg(mapping, 'name', null)
 | |
|         };
 | |
|       }
 | |
| 
 | |
|       return {
 | |
|         source: null,
 | |
|         line: null,
 | |
|         column: null,
 | |
|         name: null
 | |
|       };
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Returns the original source content. The only argument is the url of the
 | |
|    * original source file. Returns null if no original source content is
 | |
|    * availible.
 | |
|    */
 | |
|   SourceMapConsumer.prototype.sourceContentFor =
 | |
|     function SourceMapConsumer_sourceContentFor(aSource) {
 | |
|       if (!this.sourcesContent) {
 | |
|         return null;
 | |
|       }
 | |
| 
 | |
|       if (this.sourceRoot) {
 | |
|         aSource = util.relative(this.sourceRoot, aSource);
 | |
|       }
 | |
| 
 | |
|       if (this._sources.has(aSource)) {
 | |
|         return this.sourcesContent[this._sources.indexOf(aSource)];
 | |
|       }
 | |
| 
 | |
|       var url;
 | |
|       if (this.sourceRoot
 | |
|           && (url = util.urlParse(this.sourceRoot))) {
 | |
|         // XXX: file:// URIs and absolute paths lead to unexpected behavior for
 | |
|         // many users. We can help them out when they expect file:// URIs to
 | |
|         // behave like it would if they were running a local HTTP server. See
 | |
|         // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
 | |
|         var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
 | |
|         if (url.scheme == "file"
 | |
|             && this._sources.has(fileUriAbsPath)) {
 | |
|           return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
 | |
|         }
 | |
| 
 | |
|         if ((!url.path || url.path == "/")
 | |
|             && this._sources.has("/" + aSource)) {
 | |
|           return this.sourcesContent[this._sources.indexOf("/" + aSource)];
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       throw new Error('"' + aSource + '" is not in the SourceMap.');
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Returns the generated line and column information for the original source,
 | |
|    * line, and column positions provided. The only argument is an object with
 | |
|    * the following properties:
 | |
|    *
 | |
|    *   - source: The filename of the original source.
 | |
|    *   - line: The line number in the original source.
 | |
|    *   - column: The column number in the original source.
 | |
|    *
 | |
|    * and an object is returned with the following properties:
 | |
|    *
 | |
|    *   - line: The line number in the generated source, or null.
 | |
|    *   - column: The column number in the generated source, or null.
 | |
|    */
 | |
|   SourceMapConsumer.prototype.generatedPositionFor =
 | |
|     function SourceMapConsumer_generatedPositionFor(aArgs) {
 | |
|       var needle = {
 | |
|         source: util.getArg(aArgs, 'source'),
 | |
|         originalLine: util.getArg(aArgs, 'line'),
 | |
|         originalColumn: util.getArg(aArgs, 'column')
 | |
|       };
 | |
| 
 | |
|       if (this.sourceRoot) {
 | |
|         needle.source = util.relative(this.sourceRoot, needle.source);
 | |
|       }
 | |
| 
 | |
|       var mapping = this._findMapping(needle,
 | |
|                                       this._originalMappings,
 | |
|                                       "originalLine",
 | |
|                                       "originalColumn",
 | |
|                                       util.compareByOriginalPositions);
 | |
| 
 | |
|       if (mapping) {
 | |
|         return {
 | |
|           line: util.getArg(mapping, 'generatedLine', null),
 | |
|           column: util.getArg(mapping, 'generatedColumn', null)
 | |
|         };
 | |
|       }
 | |
| 
 | |
|       return {
 | |
|         line: null,
 | |
|         column: null
 | |
|       };
 | |
|     };
 | |
| 
 | |
|   SourceMapConsumer.GENERATED_ORDER = 1;
 | |
|   SourceMapConsumer.ORIGINAL_ORDER = 2;
 | |
| 
 | |
|   /**
 | |
|    * Iterate over each mapping between an original source/line/column and a
 | |
|    * generated line/column in this source map.
 | |
|    *
 | |
|    * @param Function aCallback
 | |
|    *        The function that is called with each mapping.
 | |
|    * @param Object aContext
 | |
|    *        Optional. If specified, this object will be the value of `this` every
 | |
|    *        time that `aCallback` is called.
 | |
|    * @param aOrder
 | |
|    *        Either `SourceMapConsumer.GENERATED_ORDER` or
 | |
|    *        `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
 | |
|    *        iterate over the mappings sorted by the generated file's line/column
 | |
|    *        order or the original's source/line/column order, respectively. Defaults to
 | |
|    *        `SourceMapConsumer.GENERATED_ORDER`.
 | |
|    */
 | |
|   SourceMapConsumer.prototype.eachMapping =
 | |
|     function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
 | |
|       var context = aContext || null;
 | |
|       var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
 | |
| 
 | |
|       var mappings;
 | |
|       switch (order) {
 | |
|       case SourceMapConsumer.GENERATED_ORDER:
 | |
|         mappings = this._generatedMappings;
 | |
|         break;
 | |
|       case SourceMapConsumer.ORIGINAL_ORDER:
 | |
|         mappings = this._originalMappings;
 | |
|         break;
 | |
|       default:
 | |
|         throw new Error("Unknown order of iteration.");
 | |
|       }
 | |
| 
 | |
|       var sourceRoot = this.sourceRoot;
 | |
|       mappings.map(function (mapping) {
 | |
|         var source = mapping.source;
 | |
|         if (source && sourceRoot) {
 | |
|           source = util.join(sourceRoot, source);
 | |
|         }
 | |
|         return {
 | |
|           source: source,
 | |
|           generatedLine: mapping.generatedLine,
 | |
|           generatedColumn: mapping.generatedColumn,
 | |
|           originalLine: mapping.originalLine,
 | |
|           originalColumn: mapping.originalColumn,
 | |
|           name: mapping.name
 | |
|         };
 | |
|       }).forEach(aCallback, context);
 | |
|     };
 | |
| 
 | |
|   exports.SourceMapConsumer = SourceMapConsumer;
 | |
| 
 | |
| });
 | |
| 
 | |
| },{"./array-set":36,"./base64-vlq":37,"./binary-search":39,"./util":43,"amdefine":44}],41:[function(require,module,exports){
 | |
| /* -*- Mode: js; js-indent-level: 2; -*- */
 | |
| /*
 | |
|  * Copyright 2011 Mozilla Foundation and contributors
 | |
|  * Licensed under the New BSD license. See LICENSE or:
 | |
|  * http://opensource.org/licenses/BSD-3-Clause
 | |
|  */
 | |
| if (typeof define !== 'function') {
 | |
|     var define = require('amdefine')(module, require);
 | |
| }
 | |
| define(function (require, exports, module) {
 | |
| 
 | |
|   var base64VLQ = require('./base64-vlq');
 | |
|   var util = require('./util');
 | |
|   var ArraySet = require('./array-set').ArraySet;
 | |
| 
 | |
|   /**
 | |
|    * An instance of the SourceMapGenerator represents a source map which is
 | |
|    * being built incrementally. To create a new one, you must pass an object
 | |
|    * with the following properties:
 | |
|    *
 | |
|    *   - file: The filename of the generated source.
 | |
|    *   - sourceRoot: An optional root for all URLs in this source map.
 | |
|    */
 | |
|   function SourceMapGenerator(aArgs) {
 | |
|     this._file = util.getArg(aArgs, 'file');
 | |
|     this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
 | |
|     this._sources = new ArraySet();
 | |
|     this._names = new ArraySet();
 | |
|     this._mappings = [];
 | |
|     this._sourcesContents = null;
 | |
|   }
 | |
| 
 | |
|   SourceMapGenerator.prototype._version = 3;
 | |
| 
 | |
|   /**
 | |
|    * Creates a new SourceMapGenerator based on a SourceMapConsumer
 | |
|    *
 | |
|    * @param aSourceMapConsumer The SourceMap.
 | |
|    */
 | |
|   SourceMapGenerator.fromSourceMap =
 | |
|     function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
 | |
|       var sourceRoot = aSourceMapConsumer.sourceRoot;
 | |
|       var generator = new SourceMapGenerator({
 | |
|         file: aSourceMapConsumer.file,
 | |
|         sourceRoot: sourceRoot
 | |
|       });
 | |
|       aSourceMapConsumer.eachMapping(function (mapping) {
 | |
|         var newMapping = {
 | |
|           generated: {
 | |
|             line: mapping.generatedLine,
 | |
|             column: mapping.generatedColumn
 | |
|           }
 | |
|         };
 | |
| 
 | |
|         if (mapping.source) {
 | |
|           newMapping.source = mapping.source;
 | |
|           if (sourceRoot) {
 | |
|             newMapping.source = util.relative(sourceRoot, newMapping.source);
 | |
|           }
 | |
| 
 | |
|           newMapping.original = {
 | |
|             line: mapping.originalLine,
 | |
|             column: mapping.originalColumn
 | |
|           };
 | |
| 
 | |
|           if (mapping.name) {
 | |
|             newMapping.name = mapping.name;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         generator.addMapping(newMapping);
 | |
|       });
 | |
|       aSourceMapConsumer.sources.forEach(function (sourceFile) {
 | |
|         var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 | |
|         if (content) {
 | |
|           generator.setSourceContent(sourceFile, content);
 | |
|         }
 | |
|       });
 | |
|       return generator;
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Add a single mapping from original source line and column to the generated
 | |
|    * source's line and column for this source map being created. The mapping
 | |
|    * object should have the following properties:
 | |
|    *
 | |
|    *   - generated: An object with the generated line and column positions.
 | |
|    *   - original: An object with the original line and column positions.
 | |
|    *   - source: The original source file (relative to the sourceRoot).
 | |
|    *   - name: An optional original token name for this mapping.
 | |
|    */
 | |
|   SourceMapGenerator.prototype.addMapping =
 | |
|     function SourceMapGenerator_addMapping(aArgs) {
 | |
|       var generated = util.getArg(aArgs, 'generated');
 | |
|       var original = util.getArg(aArgs, 'original', null);
 | |
|       var source = util.getArg(aArgs, 'source', null);
 | |
|       var name = util.getArg(aArgs, 'name', null);
 | |
| 
 | |
|       this._validateMapping(generated, original, source, name);
 | |
| 
 | |
|       if (source && !this._sources.has(source)) {
 | |
|         this._sources.add(source);
 | |
|       }
 | |
| 
 | |
|       if (name && !this._names.has(name)) {
 | |
|         this._names.add(name);
 | |
|       }
 | |
| 
 | |
|       this._mappings.push({
 | |
|         generatedLine: generated.line,
 | |
|         generatedColumn: generated.column,
 | |
|         originalLine: original != null && original.line,
 | |
|         originalColumn: original != null && original.column,
 | |
|         source: source,
 | |
|         name: name
 | |
|       });
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Set the source content for a source file.
 | |
|    */
 | |
|   SourceMapGenerator.prototype.setSourceContent =
 | |
|     function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
 | |
|       var source = aSourceFile;
 | |
|       if (this._sourceRoot) {
 | |
|         source = util.relative(this._sourceRoot, source);
 | |
|       }
 | |
| 
 | |
|       if (aSourceContent !== null) {
 | |
|         // Add the source content to the _sourcesContents map.
 | |
|         // Create a new _sourcesContents map if the property is null.
 | |
|         if (!this._sourcesContents) {
 | |
|           this._sourcesContents = {};
 | |
|         }
 | |
|         this._sourcesContents[util.toSetString(source)] = aSourceContent;
 | |
|       } else {
 | |
|         // Remove the source file from the _sourcesContents map.
 | |
|         // If the _sourcesContents map is empty, set the property to null.
 | |
|         delete this._sourcesContents[util.toSetString(source)];
 | |
|         if (Object.keys(this._sourcesContents).length === 0) {
 | |
|           this._sourcesContents = null;
 | |
|         }
 | |
|       }
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Applies the mappings of a sub-source-map for a specific source file to the
 | |
|    * source map being generated. Each mapping to the supplied source file is
 | |
|    * rewritten using the supplied source map. Note: The resolution for the
 | |
|    * resulting mappings is the minimium of this map and the supplied map.
 | |
|    *
 | |
|    * @param aSourceMapConsumer The source map to be applied.
 | |
|    * @param aSourceFile Optional. The filename of the source file.
 | |
|    *        If omitted, SourceMapConsumer's file property will be used.
 | |
|    */
 | |
|   SourceMapGenerator.prototype.applySourceMap =
 | |
|     function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) {
 | |
|       // If aSourceFile is omitted, we will use the file property of the SourceMap
 | |
|       if (!aSourceFile) {
 | |
|         aSourceFile = aSourceMapConsumer.file;
 | |
|       }
 | |
|       var sourceRoot = this._sourceRoot;
 | |
|       // Make "aSourceFile" relative if an absolute Url is passed.
 | |
|       if (sourceRoot) {
 | |
|         aSourceFile = util.relative(sourceRoot, aSourceFile);
 | |
|       }
 | |
|       // Applying the SourceMap can add and remove items from the sources and
 | |
|       // the names array.
 | |
|       var newSources = new ArraySet();
 | |
|       var newNames = new ArraySet();
 | |
| 
 | |
|       // Find mappings for the "aSourceFile"
 | |
|       this._mappings.forEach(function (mapping) {
 | |
|         if (mapping.source === aSourceFile && mapping.originalLine) {
 | |
|           // Check if it can be mapped by the source map, then update the mapping.
 | |
|           var original = aSourceMapConsumer.originalPositionFor({
 | |
|             line: mapping.originalLine,
 | |
|             column: mapping.originalColumn
 | |
|           });
 | |
|           if (original.source !== null) {
 | |
|             // Copy mapping
 | |
|             if (sourceRoot) {
 | |
|               mapping.source = util.relative(sourceRoot, original.source);
 | |
|             } else {
 | |
|               mapping.source = original.source;
 | |
|             }
 | |
|             mapping.originalLine = original.line;
 | |
|             mapping.originalColumn = original.column;
 | |
|             if (original.name !== null && mapping.name !== null) {
 | |
|               // Only use the identifier name if it's an identifier
 | |
|               // in both SourceMaps
 | |
|               mapping.name = original.name;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         var source = mapping.source;
 | |
|         if (source && !newSources.has(source)) {
 | |
|           newSources.add(source);
 | |
|         }
 | |
| 
 | |
|         var name = mapping.name;
 | |
|         if (name && !newNames.has(name)) {
 | |
|           newNames.add(name);
 | |
|         }
 | |
| 
 | |
|       }, this);
 | |
|       this._sources = newSources;
 | |
|       this._names = newNames;
 | |
| 
 | |
|       // Copy sourcesContents of applied map.
 | |
|       aSourceMapConsumer.sources.forEach(function (sourceFile) {
 | |
|         var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 | |
|         if (content) {
 | |
|           if (sourceRoot) {
 | |
|             sourceFile = util.relative(sourceRoot, sourceFile);
 | |
|           }
 | |
|           this.setSourceContent(sourceFile, content);
 | |
|         }
 | |
|       }, this);
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * A mapping can have one of the three levels of data:
 | |
|    *
 | |
|    *   1. Just the generated position.
 | |
|    *   2. The Generated position, original position, and original source.
 | |
|    *   3. Generated and original position, original source, as well as a name
 | |
|    *      token.
 | |
|    *
 | |
|    * To maintain consistency, we validate that any new mapping being added falls
 | |
|    * in to one of these categories.
 | |
|    */
 | |
|   SourceMapGenerator.prototype._validateMapping =
 | |
|     function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
 | |
|                                                 aName) {
 | |
|       if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
 | |
|           && aGenerated.line > 0 && aGenerated.column >= 0
 | |
|           && !aOriginal && !aSource && !aName) {
 | |
|         // Case 1.
 | |
|         return;
 | |
|       }
 | |
|       else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
 | |
|                && aOriginal && 'line' in aOriginal && 'column' in aOriginal
 | |
|                && aGenerated.line > 0 && aGenerated.column >= 0
 | |
|                && aOriginal.line > 0 && aOriginal.column >= 0
 | |
|                && aSource) {
 | |
|         // Cases 2 and 3.
 | |
|         return;
 | |
|       }
 | |
|       else {
 | |
|         throw new Error('Invalid mapping: ' + JSON.stringify({
 | |
|           generated: aGenerated,
 | |
|           source: aSource,
 | |
|           orginal: aOriginal,
 | |
|           name: aName
 | |
|         }));
 | |
|       }
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Serialize the accumulated mappings in to the stream of base 64 VLQs
 | |
|    * specified by the source map format.
 | |
|    */
 | |
|   SourceMapGenerator.prototype._serializeMappings =
 | |
|     function SourceMapGenerator_serializeMappings() {
 | |
|       var previousGeneratedColumn = 0;
 | |
|       var previousGeneratedLine = 1;
 | |
|       var previousOriginalColumn = 0;
 | |
|       var previousOriginalLine = 0;
 | |
|       var previousName = 0;
 | |
|       var previousSource = 0;
 | |
|       var result = '';
 | |
|       var mapping;
 | |
| 
 | |
|       // The mappings must be guaranteed to be in sorted order before we start
 | |
|       // serializing them or else the generated line numbers (which are defined
 | |
|       // via the ';' separators) will be all messed up. Note: it might be more
 | |
|       // performant to maintain the sorting as we insert them, rather than as we
 | |
|       // serialize them, but the big O is the same either way.
 | |
|       this._mappings.sort(util.compareByGeneratedPositions);
 | |
| 
 | |
|       for (var i = 0, len = this._mappings.length; i < len; i++) {
 | |
|         mapping = this._mappings[i];
 | |
| 
 | |
|         if (mapping.generatedLine !== previousGeneratedLine) {
 | |
|           previousGeneratedColumn = 0;
 | |
|           while (mapping.generatedLine !== previousGeneratedLine) {
 | |
|             result += ';';
 | |
|             previousGeneratedLine++;
 | |
|           }
 | |
|         }
 | |
|         else {
 | |
|           if (i > 0) {
 | |
|             if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) {
 | |
|               continue;
 | |
|             }
 | |
|             result += ',';
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         result += base64VLQ.encode(mapping.generatedColumn
 | |
|                                    - previousGeneratedColumn);
 | |
|         previousGeneratedColumn = mapping.generatedColumn;
 | |
| 
 | |
|         if (mapping.source) {
 | |
|           result += base64VLQ.encode(this._sources.indexOf(mapping.source)
 | |
|                                      - previousSource);
 | |
|           previousSource = this._sources.indexOf(mapping.source);
 | |
| 
 | |
|           // lines are stored 0-based in SourceMap spec version 3
 | |
|           result += base64VLQ.encode(mapping.originalLine - 1
 | |
|                                      - previousOriginalLine);
 | |
|           previousOriginalLine = mapping.originalLine - 1;
 | |
| 
 | |
|           result += base64VLQ.encode(mapping.originalColumn
 | |
|                                      - previousOriginalColumn);
 | |
|           previousOriginalColumn = mapping.originalColumn;
 | |
| 
 | |
|           if (mapping.name) {
 | |
|             result += base64VLQ.encode(this._names.indexOf(mapping.name)
 | |
|                                        - previousName);
 | |
|             previousName = this._names.indexOf(mapping.name);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return result;
 | |
|     };
 | |
| 
 | |
|   SourceMapGenerator.prototype._generateSourcesContent =
 | |
|     function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
 | |
|       return aSources.map(function (source) {
 | |
|         if (!this._sourcesContents) {
 | |
|           return null;
 | |
|         }
 | |
|         if (aSourceRoot) {
 | |
|           source = util.relative(aSourceRoot, source);
 | |
|         }
 | |
|         var key = util.toSetString(source);
 | |
|         return Object.prototype.hasOwnProperty.call(this._sourcesContents,
 | |
|                                                     key)
 | |
|           ? this._sourcesContents[key]
 | |
|           : null;
 | |
|       }, this);
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Externalize the source map.
 | |
|    */
 | |
|   SourceMapGenerator.prototype.toJSON =
 | |
|     function SourceMapGenerator_toJSON() {
 | |
|       var map = {
 | |
|         version: this._version,
 | |
|         file: this._file,
 | |
|         sources: this._sources.toArray(),
 | |
|         names: this._names.toArray(),
 | |
|         mappings: this._serializeMappings()
 | |
|       };
 | |
|       if (this._sourceRoot) {
 | |
|         map.sourceRoot = this._sourceRoot;
 | |
|       }
 | |
|       if (this._sourcesContents) {
 | |
|         map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
 | |
|       }
 | |
| 
 | |
|       return map;
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Render the source map being generated to a string.
 | |
|    */
 | |
|   SourceMapGenerator.prototype.toString =
 | |
|     function SourceMapGenerator_toString() {
 | |
|       return JSON.stringify(this);
 | |
|     };
 | |
| 
 | |
|   exports.SourceMapGenerator = SourceMapGenerator;
 | |
| 
 | |
| });
 | |
| 
 | |
| },{"./array-set":36,"./base64-vlq":37,"./util":43,"amdefine":44}],42:[function(require,module,exports){
 | |
| /* -*- Mode: js; js-indent-level: 2; -*- */
 | |
| /*
 | |
|  * Copyright 2011 Mozilla Foundation and contributors
 | |
|  * Licensed under the New BSD license. See LICENSE or:
 | |
|  * http://opensource.org/licenses/BSD-3-Clause
 | |
|  */
 | |
| if (typeof define !== 'function') {
 | |
|     var define = require('amdefine')(module, require);
 | |
| }
 | |
| define(function (require, exports, module) {
 | |
| 
 | |
|   var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;
 | |
|   var util = require('./util');
 | |
| 
 | |
|   /**
 | |
|    * SourceNodes provide a way to abstract over interpolating/concatenating
 | |
|    * snippets of generated JavaScript source code while maintaining the line and
 | |
|    * column information associated with the original source code.
 | |
|    *
 | |
|    * @param aLine The original line number.
 | |
|    * @param aColumn The original column number.
 | |
|    * @param aSource The original source's filename.
 | |
|    * @param aChunks Optional. An array of strings which are snippets of
 | |
|    *        generated JS, or other SourceNodes.
 | |
|    * @param aName The original identifier.
 | |
|    */
 | |
|   function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
 | |
|     this.children = [];
 | |
|     this.sourceContents = {};
 | |
|     this.line = aLine === undefined ? null : aLine;
 | |
|     this.column = aColumn === undefined ? null : aColumn;
 | |
|     this.source = aSource === undefined ? null : aSource;
 | |
|     this.name = aName === undefined ? null : aName;
 | |
|     if (aChunks != null) this.add(aChunks);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Creates a SourceNode from generated code and a SourceMapConsumer.
 | |
|    *
 | |
|    * @param aGeneratedCode The generated code
 | |
|    * @param aSourceMapConsumer The SourceMap for the generated code
 | |
|    */
 | |
|   SourceNode.fromStringWithSourceMap =
 | |
|     function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) {
 | |
|       // The SourceNode we want to fill with the generated code
 | |
|       // and the SourceMap
 | |
|       var node = new SourceNode();
 | |
| 
 | |
|       // The generated code
 | |
|       // Processed fragments are removed from this array.
 | |
|       var remainingLines = aGeneratedCode.split('\n');
 | |
| 
 | |
|       // We need to remember the position of "remainingLines"
 | |
|       var lastGeneratedLine = 1, lastGeneratedColumn = 0;
 | |
| 
 | |
|       // The generate SourceNodes we need a code range.
 | |
|       // To extract it current and last mapping is used.
 | |
|       // Here we store the last mapping.
 | |
|       var lastMapping = null;
 | |
| 
 | |
|       aSourceMapConsumer.eachMapping(function (mapping) {
 | |
|         if (lastMapping === null) {
 | |
|           // We add the generated code until the first mapping
 | |
|           // to the SourceNode without any mapping.
 | |
|           // Each line is added as separate string.
 | |
|           while (lastGeneratedLine < mapping.generatedLine) {
 | |
|             node.add(remainingLines.shift() + "\n");
 | |
|             lastGeneratedLine++;
 | |
|           }
 | |
|           if (lastGeneratedColumn < mapping.generatedColumn) {
 | |
|             var nextLine = remainingLines[0];
 | |
|             node.add(nextLine.substr(0, mapping.generatedColumn));
 | |
|             remainingLines[0] = nextLine.substr(mapping.generatedColumn);
 | |
|             lastGeneratedColumn = mapping.generatedColumn;
 | |
|           }
 | |
|         } else {
 | |
|           // We add the code from "lastMapping" to "mapping":
 | |
|           // First check if there is a new line in between.
 | |
|           if (lastGeneratedLine < mapping.generatedLine) {
 | |
|             var code = "";
 | |
|             // Associate full lines with "lastMapping"
 | |
|             do {
 | |
|               code += remainingLines.shift() + "\n";
 | |
|               lastGeneratedLine++;
 | |
|               lastGeneratedColumn = 0;
 | |
|             } while (lastGeneratedLine < mapping.generatedLine);
 | |
|             // When we reached the correct line, we add code until we
 | |
|             // reach the correct column too.
 | |
|             if (lastGeneratedColumn < mapping.generatedColumn) {
 | |
|               var nextLine = remainingLines[0];
 | |
|               code += nextLine.substr(0, mapping.generatedColumn);
 | |
|               remainingLines[0] = nextLine.substr(mapping.generatedColumn);
 | |
|               lastGeneratedColumn = mapping.generatedColumn;
 | |
|             }
 | |
|             // Create the SourceNode.
 | |
|             addMappingWithCode(lastMapping, code);
 | |
|           } else {
 | |
|             // There is no new line in between.
 | |
|             // Associate the code between "lastGeneratedColumn" and
 | |
|             // "mapping.generatedColumn" with "lastMapping"
 | |
|             var nextLine = remainingLines[0];
 | |
|             var code = nextLine.substr(0, mapping.generatedColumn -
 | |
|                                           lastGeneratedColumn);
 | |
|             remainingLines[0] = nextLine.substr(mapping.generatedColumn -
 | |
|                                                 lastGeneratedColumn);
 | |
|             lastGeneratedColumn = mapping.generatedColumn;
 | |
|             addMappingWithCode(lastMapping, code);
 | |
|           }
 | |
|         }
 | |
|         lastMapping = mapping;
 | |
|       }, this);
 | |
|       // We have processed all mappings.
 | |
|       // Associate the remaining code in the current line with "lastMapping"
 | |
|       // and add the remaining lines without any mapping
 | |
|       addMappingWithCode(lastMapping, remainingLines.join("\n"));
 | |
| 
 | |
|       // Copy sourcesContent into SourceNode
 | |
|       aSourceMapConsumer.sources.forEach(function (sourceFile) {
 | |
|         var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 | |
|         if (content) {
 | |
|           node.setSourceContent(sourceFile, content);
 | |
|         }
 | |
|       });
 | |
| 
 | |
|       return node;
 | |
| 
 | |
|       function addMappingWithCode(mapping, code) {
 | |
|         if (mapping === null || mapping.source === undefined) {
 | |
|           node.add(code);
 | |
|         } else {
 | |
|           node.add(new SourceNode(mapping.originalLine,
 | |
|                                   mapping.originalColumn,
 | |
|                                   mapping.source,
 | |
|                                   code,
 | |
|                                   mapping.name));
 | |
|         }
 | |
|       }
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Add a chunk of generated JS to this source node.
 | |
|    *
 | |
|    * @param aChunk A string snippet of generated JS code, another instance of
 | |
|    *        SourceNode, or an array where each member is one of those things.
 | |
|    */
 | |
|   SourceNode.prototype.add = function SourceNode_add(aChunk) {
 | |
|     if (Array.isArray(aChunk)) {
 | |
|       aChunk.forEach(function (chunk) {
 | |
|         this.add(chunk);
 | |
|       }, this);
 | |
|     }
 | |
|     else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
 | |
|       if (aChunk) {
 | |
|         this.children.push(aChunk);
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       throw new TypeError(
 | |
|         "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
 | |
|       );
 | |
|     }
 | |
|     return this;
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Add a chunk of generated JS to the beginning of this source node.
 | |
|    *
 | |
|    * @param aChunk A string snippet of generated JS code, another instance of
 | |
|    *        SourceNode, or an array where each member is one of those things.
 | |
|    */
 | |
|   SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
 | |
|     if (Array.isArray(aChunk)) {
 | |
|       for (var i = aChunk.length-1; i >= 0; i--) {
 | |
|         this.prepend(aChunk[i]);
 | |
|       }
 | |
|     }
 | |
|     else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
 | |
|       this.children.unshift(aChunk);
 | |
|     }
 | |
|     else {
 | |
|       throw new TypeError(
 | |
|         "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
 | |
|       );
 | |
|     }
 | |
|     return this;
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Walk over the tree of JS snippets in this node and its children. The
 | |
|    * walking function is called once for each snippet of JS and is passed that
 | |
|    * snippet and the its original associated source's line/column location.
 | |
|    *
 | |
|    * @param aFn The traversal function.
 | |
|    */
 | |
|   SourceNode.prototype.walk = function SourceNode_walk(aFn) {
 | |
|     var chunk;
 | |
|     for (var i = 0, len = this.children.length; i < len; i++) {
 | |
|       chunk = this.children[i];
 | |
|       if (chunk instanceof SourceNode) {
 | |
|         chunk.walk(aFn);
 | |
|       }
 | |
|       else {
 | |
|         if (chunk !== '') {
 | |
|           aFn(chunk, { source: this.source,
 | |
|                        line: this.line,
 | |
|                        column: this.column,
 | |
|                        name: this.name });
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
 | |
|    * each of `this.children`.
 | |
|    *
 | |
|    * @param aSep The separator.
 | |
|    */
 | |
|   SourceNode.prototype.join = function SourceNode_join(aSep) {
 | |
|     var newChildren;
 | |
|     var i;
 | |
|     var len = this.children.length;
 | |
|     if (len > 0) {
 | |
|       newChildren = [];
 | |
|       for (i = 0; i < len-1; i++) {
 | |
|         newChildren.push(this.children[i]);
 | |
|         newChildren.push(aSep);
 | |
|       }
 | |
|       newChildren.push(this.children[i]);
 | |
|       this.children = newChildren;
 | |
|     }
 | |
|     return this;
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Call String.prototype.replace on the very right-most source snippet. Useful
 | |
|    * for trimming whitespace from the end of a source node, etc.
 | |
|    *
 | |
|    * @param aPattern The pattern to replace.
 | |
|    * @param aReplacement The thing to replace the pattern with.
 | |
|    */
 | |
|   SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
 | |
|     var lastChild = this.children[this.children.length - 1];
 | |
|     if (lastChild instanceof SourceNode) {
 | |
|       lastChild.replaceRight(aPattern, aReplacement);
 | |
|     }
 | |
|     else if (typeof lastChild === 'string') {
 | |
|       this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
 | |
|     }
 | |
|     else {
 | |
|       this.children.push(''.replace(aPattern, aReplacement));
 | |
|     }
 | |
|     return this;
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Set the source content for a source file. This will be added to the SourceMapGenerator
 | |
|    * in the sourcesContent field.
 | |
|    *
 | |
|    * @param aSourceFile The filename of the source file
 | |
|    * @param aSourceContent The content of the source file
 | |
|    */
 | |
|   SourceNode.prototype.setSourceContent =
 | |
|     function SourceNode_setSourceContent(aSourceFile, aSourceContent) {
 | |
|       this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Walk over the tree of SourceNodes. The walking function is called for each
 | |
|    * source file content and is passed the filename and source content.
 | |
|    *
 | |
|    * @param aFn The traversal function.
 | |
|    */
 | |
|   SourceNode.prototype.walkSourceContents =
 | |
|     function SourceNode_walkSourceContents(aFn) {
 | |
|       for (var i = 0, len = this.children.length; i < len; i++) {
 | |
|         if (this.children[i] instanceof SourceNode) {
 | |
|           this.children[i].walkSourceContents(aFn);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       var sources = Object.keys(this.sourceContents);
 | |
|       for (var i = 0, len = sources.length; i < len; i++) {
 | |
|         aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
 | |
|       }
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Return the string representation of this source node. Walks over the tree
 | |
|    * and concatenates all the various snippets together to one string.
 | |
|    */
 | |
|   SourceNode.prototype.toString = function SourceNode_toString() {
 | |
|     var str = "";
 | |
|     this.walk(function (chunk) {
 | |
|       str += chunk;
 | |
|     });
 | |
|     return str;
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Returns the string representation of this source node along with a source
 | |
|    * map.
 | |
|    */
 | |
|   SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
 | |
|     var generated = {
 | |
|       code: "",
 | |
|       line: 1,
 | |
|       column: 0
 | |
|     };
 | |
|     var map = new SourceMapGenerator(aArgs);
 | |
|     var sourceMappingActive = false;
 | |
|     var lastOriginalSource = null;
 | |
|     var lastOriginalLine = null;
 | |
|     var lastOriginalColumn = null;
 | |
|     var lastOriginalName = null;
 | |
|     this.walk(function (chunk, original) {
 | |
|       generated.code += chunk;
 | |
|       if (original.source !== null
 | |
|           && original.line !== null
 | |
|           && original.column !== null) {
 | |
|         if(lastOriginalSource !== original.source
 | |
|            || lastOriginalLine !== original.line
 | |
|            || lastOriginalColumn !== original.column
 | |
|            || lastOriginalName !== original.name) {
 | |
|           map.addMapping({
 | |
|             source: original.source,
 | |
|             original: {
 | |
|               line: original.line,
 | |
|               column: original.column
 | |
|             },
 | |
|             generated: {
 | |
|               line: generated.line,
 | |
|               column: generated.column
 | |
|             },
 | |
|             name: original.name
 | |
|           });
 | |
|         }
 | |
|         lastOriginalSource = original.source;
 | |
|         lastOriginalLine = original.line;
 | |
|         lastOriginalColumn = original.column;
 | |
|         lastOriginalName = original.name;
 | |
|         sourceMappingActive = true;
 | |
|       } else if (sourceMappingActive) {
 | |
|         map.addMapping({
 | |
|           generated: {
 | |
|             line: generated.line,
 | |
|             column: generated.column
 | |
|           }
 | |
|         });
 | |
|         lastOriginalSource = null;
 | |
|         sourceMappingActive = false;
 | |
|       }
 | |
|       chunk.split('').forEach(function (ch) {
 | |
|         if (ch === '\n') {
 | |
|           generated.line++;
 | |
|           generated.column = 0;
 | |
|         } else {
 | |
|           generated.column++;
 | |
|         }
 | |
|       });
 | |
|     });
 | |
|     this.walkSourceContents(function (sourceFile, sourceContent) {
 | |
|       map.setSourceContent(sourceFile, sourceContent);
 | |
|     });
 | |
| 
 | |
|     return { code: generated.code, map: map };
 | |
|   };
 | |
| 
 | |
|   exports.SourceNode = SourceNode;
 | |
| 
 | |
| });
 | |
| 
 | |
| },{"./source-map-generator":41,"./util":43,"amdefine":44}],43:[function(require,module,exports){
 | |
| /* -*- Mode: js; js-indent-level: 2; -*- */
 | |
| /*
 | |
|  * Copyright 2011 Mozilla Foundation and contributors
 | |
|  * Licensed under the New BSD license. See LICENSE or:
 | |
|  * http://opensource.org/licenses/BSD-3-Clause
 | |
|  */
 | |
| if (typeof define !== 'function') {
 | |
|     var define = require('amdefine')(module, require);
 | |
| }
 | |
| define(function (require, exports, module) {
 | |
| 
 | |
|   /**
 | |
|    * This is a helper function for getting values from parameter/options
 | |
|    * objects.
 | |
|    *
 | |
|    * @param args The object we are extracting values from
 | |
|    * @param name The name of the property we are getting.
 | |
|    * @param defaultValue An optional value to return if the property is missing
 | |
|    * from the object. If this is not specified and the property is missing, an
 | |
|    * error will be thrown.
 | |
|    */
 | |
|   function getArg(aArgs, aName, aDefaultValue) {
 | |
|     if (aName in aArgs) {
 | |
|       return aArgs[aName];
 | |
|     } else if (arguments.length === 3) {
 | |
|       return aDefaultValue;
 | |
|     } else {
 | |
|       throw new Error('"' + aName + '" is a required argument.');
 | |
|     }
 | |
|   }
 | |
|   exports.getArg = getArg;
 | |
| 
 | |
|   var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/;
 | |
|   var dataUrlRegexp = /^data:.+\,.+/;
 | |
| 
 | |
|   function urlParse(aUrl) {
 | |
|     var match = aUrl.match(urlRegexp);
 | |
|     if (!match) {
 | |
|       return null;
 | |
|     }
 | |
|     return {
 | |
|       scheme: match[1],
 | |
|       auth: match[3],
 | |
|       host: match[4],
 | |
|       port: match[6],
 | |
|       path: match[7]
 | |
|     };
 | |
|   }
 | |
|   exports.urlParse = urlParse;
 | |
| 
 | |
|   function urlGenerate(aParsedUrl) {
 | |
|     var url = aParsedUrl.scheme + "://";
 | |
|     if (aParsedUrl.auth) {
 | |
|       url += aParsedUrl.auth + "@"
 | |
|     }
 | |
|     if (aParsedUrl.host) {
 | |
|       url += aParsedUrl.host;
 | |
|     }
 | |
|     if (aParsedUrl.port) {
 | |
|       url += ":" + aParsedUrl.port
 | |
|     }
 | |
|     if (aParsedUrl.path) {
 | |
|       url += aParsedUrl.path;
 | |
|     }
 | |
|     return url;
 | |
|   }
 | |
|   exports.urlGenerate = urlGenerate;
 | |
| 
 | |
|   function join(aRoot, aPath) {
 | |
|     var url;
 | |
| 
 | |
|     if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) {
 | |
|       return aPath;
 | |
|     }
 | |
| 
 | |
|     if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) {
 | |
|       url.path = aPath;
 | |
|       return urlGenerate(url);
 | |
|     }
 | |
| 
 | |
|     return aRoot.replace(/\/$/, '') + '/' + aPath;
 | |
|   }
 | |
|   exports.join = join;
 | |
| 
 | |
|   /**
 | |
|    * Because behavior goes wacky when you set `__proto__` on objects, we
 | |
|    * have to prefix all the strings in our set with an arbitrary character.
 | |
|    *
 | |
|    * See https://github.com/mozilla/source-map/pull/31 and
 | |
|    * https://github.com/mozilla/source-map/issues/30
 | |
|    *
 | |
|    * @param String aStr
 | |
|    */
 | |
|   function toSetString(aStr) {
 | |
|     return '$' + aStr;
 | |
|   }
 | |
|   exports.toSetString = toSetString;
 | |
| 
 | |
|   function fromSetString(aStr) {
 | |
|     return aStr.substr(1);
 | |
|   }
 | |
|   exports.fromSetString = fromSetString;
 | |
| 
 | |
|   function relative(aRoot, aPath) {
 | |
|     aRoot = aRoot.replace(/\/$/, '');
 | |
| 
 | |
|     var url = urlParse(aRoot);
 | |
|     if (aPath.charAt(0) == "/" && url && url.path == "/") {
 | |
|       return aPath.slice(1);
 | |
|     }
 | |
| 
 | |
|     return aPath.indexOf(aRoot + '/') === 0
 | |
|       ? aPath.substr(aRoot.length + 1)
 | |
|       : aPath;
 | |
|   }
 | |
|   exports.relative = relative;
 | |
| 
 | |
|   function strcmp(aStr1, aStr2) {
 | |
|     var s1 = aStr1 || "";
 | |
|     var s2 = aStr2 || "";
 | |
|     return (s1 > s2) - (s1 < s2);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Comparator between two mappings where the original positions are compared.
 | |
|    *
 | |
|    * Optionally pass in `true` as `onlyCompareGenerated` to consider two
 | |
|    * mappings with the same original source/line/column, but different generated
 | |
|    * line and column the same. Useful when searching for a mapping with a
 | |
|    * stubbed out mapping.
 | |
|    */
 | |
|   function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
 | |
|     var cmp;
 | |
| 
 | |
|     cmp = strcmp(mappingA.source, mappingB.source);
 | |
|     if (cmp) {
 | |
|       return cmp;
 | |
|     }
 | |
| 
 | |
|     cmp = mappingA.originalLine - mappingB.originalLine;
 | |
|     if (cmp) {
 | |
|       return cmp;
 | |
|     }
 | |
| 
 | |
|     cmp = mappingA.originalColumn - mappingB.originalColumn;
 | |
|     if (cmp || onlyCompareOriginal) {
 | |
|       return cmp;
 | |
|     }
 | |
| 
 | |
|     cmp = strcmp(mappingA.name, mappingB.name);
 | |
|     if (cmp) {
 | |
|       return cmp;
 | |
|     }
 | |
| 
 | |
|     cmp = mappingA.generatedLine - mappingB.generatedLine;
 | |
|     if (cmp) {
 | |
|       return cmp;
 | |
|     }
 | |
| 
 | |
|     return mappingA.generatedColumn - mappingB.generatedColumn;
 | |
|   };
 | |
|   exports.compareByOriginalPositions = compareByOriginalPositions;
 | |
| 
 | |
|   /**
 | |
|    * Comparator between two mappings where the generated positions are
 | |
|    * compared.
 | |
|    *
 | |
|    * Optionally pass in `true` as `onlyCompareGenerated` to consider two
 | |
|    * mappings with the same generated line and column, but different
 | |
|    * source/name/original line and column the same. Useful when searching for a
 | |
|    * mapping with a stubbed out mapping.
 | |
|    */
 | |
|   function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) {
 | |
|     var cmp;
 | |
| 
 | |
|     cmp = mappingA.generatedLine - mappingB.generatedLine;
 | |
|     if (cmp) {
 | |
|       return cmp;
 | |
|     }
 | |
| 
 | |
|     cmp = mappingA.generatedColumn - mappingB.generatedColumn;
 | |
|     if (cmp || onlyCompareGenerated) {
 | |
|       return cmp;
 | |
|     }
 | |
| 
 | |
|     cmp = strcmp(mappingA.source, mappingB.source);
 | |
|     if (cmp) {
 | |
|       return cmp;
 | |
|     }
 | |
| 
 | |
|     cmp = mappingA.originalLine - mappingB.originalLine;
 | |
|     if (cmp) {
 | |
|       return cmp;
 | |
|     }
 | |
| 
 | |
|     cmp = mappingA.originalColumn - mappingB.originalColumn;
 | |
|     if (cmp) {
 | |
|       return cmp;
 | |
|     }
 | |
| 
 | |
|     return strcmp(mappingA.name, mappingB.name);
 | |
|   };
 | |
|   exports.compareByGeneratedPositions = compareByGeneratedPositions;
 | |
| 
 | |
| });
 | |
| 
 | |
| },{"amdefine":44}],44:[function(require,module,exports){
 | |
| var process=require("__browserify_process"),__filename="/../node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/amdefine.js";/** vim: et:ts=4:sw=4:sts=4
 | |
|  * @license amdefine 0.1.0 Copyright (c) 2011, The Dojo Foundation All Rights Reserved.
 | |
|  * Available via the MIT or new BSD license.
 | |
|  * see: http://github.com/jrburke/amdefine for details
 | |
|  */
 | |
| 
 | |
| /*jslint node: true */
 | |
| /*global module, process */
 | |
| 'use strict';
 | |
| 
 | |
| /**
 | |
|  * Creates a define for node.
 | |
|  * @param {Object} module the "module" object that is defined by Node for the
 | |
|  * current module.
 | |
|  * @param {Function} [requireFn]. Node's require function for the current module.
 | |
|  * It only needs to be passed in Node versions before 0.5, when module.require
 | |
|  * did not exist.
 | |
|  * @returns {Function} a define function that is usable for the current node
 | |
|  * module.
 | |
|  */
 | |
| function amdefine(module, requireFn) {
 | |
|     'use strict';
 | |
|     var defineCache = {},
 | |
|         loaderCache = {},
 | |
|         alreadyCalled = false,
 | |
|         path = require('path'),
 | |
|         makeRequire, stringRequire;
 | |
| 
 | |
|     /**
 | |
|      * Trims the . and .. from an array of path segments.
 | |
|      * It will keep a leading path segment if a .. will become
 | |
|      * the first path segment, to help with module name lookups,
 | |
|      * which act like paths, but can be remapped. But the end result,
 | |
|      * all paths that use this function should look normalized.
 | |
|      * NOTE: this method MODIFIES the input array.
 | |
|      * @param {Array} ary the array of path segments.
 | |
|      */
 | |
|     function trimDots(ary) {
 | |
|         var i, part;
 | |
|         for (i = 0; ary[i]; i+= 1) {
 | |
|             part = ary[i];
 | |
|             if (part === '.') {
 | |
|                 ary.splice(i, 1);
 | |
|                 i -= 1;
 | |
|             } else if (part === '..') {
 | |
|                 if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
 | |
|                     //End of the line. Keep at least one non-dot
 | |
|                     //path segment at the front so it can be mapped
 | |
|                     //correctly to disk. Otherwise, there is likely
 | |
|                     //no path mapping for a path starting with '..'.
 | |
|                     //This can still fail, but catches the most reasonable
 | |
|                     //uses of ..
 | |
|                     break;
 | |
|                 } else if (i > 0) {
 | |
|                     ary.splice(i - 1, 2);
 | |
|                     i -= 2;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     function normalize(name, baseName) {
 | |
|         var baseParts;
 | |
| 
 | |
|         //Adjust any relative paths.
 | |
|         if (name && name.charAt(0) === '.') {
 | |
|             //If have a base name, try to normalize against it,
 | |
|             //otherwise, assume it is a top-level require that will
 | |
|             //be relative to baseUrl in the end.
 | |
|             if (baseName) {
 | |
|                 baseParts = baseName.split('/');
 | |
|                 baseParts = baseParts.slice(0, baseParts.length - 1);
 | |
|                 baseParts = baseParts.concat(name.split('/'));
 | |
|                 trimDots(baseParts);
 | |
|                 name = baseParts.join('/');
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return name;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Create the normalize() function passed to a loader plugin's
 | |
|      * normalize method.
 | |
|      */
 | |
|     function makeNormalize(relName) {
 | |
|         return function (name) {
 | |
|             return normalize(name, relName);
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     function makeLoad(id) {
 | |
|         function load(value) {
 | |
|             loaderCache[id] = value;
 | |
|         }
 | |
| 
 | |
|         load.fromText = function (id, text) {
 | |
|             //This one is difficult because the text can/probably uses
 | |
|             //define, and any relative paths and requires should be relative
 | |
|             //to that id was it would be found on disk. But this would require
 | |
|             //bootstrapping a module/require fairly deeply from node core.
 | |
|             //Not sure how best to go about that yet.
 | |
|             throw new Error('amdefine does not implement load.fromText');
 | |
|         };
 | |
| 
 | |
|         return load;
 | |
|     }
 | |
| 
 | |
|     makeRequire = function (systemRequire, exports, module, relId) {
 | |
|         function amdRequire(deps, callback) {
 | |
|             if (typeof deps === 'string') {
 | |
|                 //Synchronous, single module require('')
 | |
|                 return stringRequire(systemRequire, exports, module, deps, relId);
 | |
|             } else {
 | |
|                 //Array of dependencies with a callback.
 | |
| 
 | |
|                 //Convert the dependencies to modules.
 | |
|                 deps = deps.map(function (depName) {
 | |
|                     return stringRequire(systemRequire, exports, module, depName, relId);
 | |
|                 });
 | |
| 
 | |
|                 //Wait for next tick to call back the require call.
 | |
|                 process.nextTick(function () {
 | |
|                     callback.apply(null, deps);
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         amdRequire.toUrl = function (filePath) {
 | |
|             if (filePath.indexOf('.') === 0) {
 | |
|                 return normalize(filePath, path.dirname(module.filename));
 | |
|             } else {
 | |
|                 return filePath;
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         return amdRequire;
 | |
|     };
 | |
| 
 | |
|     //Favor explicit value, passed in if the module wants to support Node 0.4.
 | |
|     requireFn = requireFn || function req() {
 | |
|         return module.require.apply(module, arguments);
 | |
|     };
 | |
| 
 | |
|     function runFactory(id, deps, factory) {
 | |
|         var r, e, m, result;
 | |
| 
 | |
|         if (id) {
 | |
|             e = loaderCache[id] = {};
 | |
|             m = {
 | |
|                 id: id,
 | |
|                 uri: __filename,
 | |
|                 exports: e
 | |
|             };
 | |
|             r = makeRequire(requireFn, e, m, id);
 | |
|         } else {
 | |
|             //Only support one define call per file
 | |
|             if (alreadyCalled) {
 | |
|                 throw new Error('amdefine with no module ID cannot be called more than once per file.');
 | |
|             }
 | |
|             alreadyCalled = true;
 | |
| 
 | |
|             //Use the real variables from node
 | |
|             //Use module.exports for exports, since
 | |
|             //the exports in here is amdefine exports.
 | |
|             e = module.exports;
 | |
|             m = module;
 | |
|             r = makeRequire(requireFn, e, m, module.id);
 | |
|         }
 | |
| 
 | |
|         //If there are dependencies, they are strings, so need
 | |
|         //to convert them to dependency values.
 | |
|         if (deps) {
 | |
|             deps = deps.map(function (depName) {
 | |
|                 return r(depName);
 | |
|             });
 | |
|         }
 | |
| 
 | |
|         //Call the factory with the right dependencies.
 | |
|         if (typeof factory === 'function') {
 | |
|             result = factory.apply(m.exports, deps);
 | |
|         } else {
 | |
|             result = factory;
 | |
|         }
 | |
| 
 | |
|         if (result !== undefined) {
 | |
|             m.exports = result;
 | |
|             if (id) {
 | |
|                 loaderCache[id] = m.exports;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     stringRequire = function (systemRequire, exports, module, id, relId) {
 | |
|         //Split the ID by a ! so that
 | |
|         var index = id.indexOf('!'),
 | |
|             originalId = id,
 | |
|             prefix, plugin;
 | |
| 
 | |
|         if (index === -1) {
 | |
|             id = normalize(id, relId);
 | |
| 
 | |
|             //Straight module lookup. If it is one of the special dependencies,
 | |
|             //deal with it, otherwise, delegate to node.
 | |
|             if (id === 'require') {
 | |
|                 return makeRequire(systemRequire, exports, module, relId);
 | |
|             } else if (id === 'exports') {
 | |
|                 return exports;
 | |
|             } else if (id === 'module') {
 | |
|                 return module;
 | |
|             } else if (loaderCache.hasOwnProperty(id)) {
 | |
|                 return loaderCache[id];
 | |
|             } else if (defineCache[id]) {
 | |
|                 runFactory.apply(null, defineCache[id]);
 | |
|                 return loaderCache[id];
 | |
|             } else {
 | |
|                 if(systemRequire) {
 | |
|                     return systemRequire(originalId);
 | |
|                 } else {
 | |
|                     throw new Error('No module with ID: ' + id);
 | |
|                 }
 | |
|             }
 | |
|         } else {
 | |
|             //There is a plugin in play.
 | |
|             prefix = id.substring(0, index);
 | |
|             id = id.substring(index + 1, id.length);
 | |
| 
 | |
|             plugin = stringRequire(systemRequire, exports, module, prefix, relId);
 | |
| 
 | |
|             if (plugin.normalize) {
 | |
|                 id = plugin.normalize(id, makeNormalize(relId));
 | |
|             } else {
 | |
|                 //Normalize the ID normally.
 | |
|                 id = normalize(id, relId);
 | |
|             }
 | |
| 
 | |
|             if (loaderCache[id]) {
 | |
|                 return loaderCache[id];
 | |
|             } else {
 | |
|                 plugin.load(id, makeRequire(systemRequire, exports, module, relId), makeLoad(id), {});
 | |
| 
 | |
|                 return loaderCache[id];
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     //Create a define function specific to the module asking for amdefine.
 | |
|     function define(id, deps, factory) {
 | |
|         if (Array.isArray(id)) {
 | |
|             factory = deps;
 | |
|             deps = id;
 | |
|             id = undefined;
 | |
|         } else if (typeof id !== 'string') {
 | |
|             factory = id;
 | |
|             id = deps = undefined;
 | |
|         }
 | |
| 
 | |
|         if (deps && !Array.isArray(deps)) {
 | |
|             factory = deps;
 | |
|             deps = undefined;
 | |
|         }
 | |
| 
 | |
|         if (!deps) {
 | |
|             deps = ['require', 'exports', 'module'];
 | |
|         }
 | |
| 
 | |
|         //Set up properties for this module. If an ID, then use
 | |
|         //internal cache. If no ID, then use the external variables
 | |
|         //for this node module.
 | |
|         if (id) {
 | |
|             //Put the module in deep freeze until there is a
 | |
|             //require call for it.
 | |
|             defineCache[id] = [id, deps, factory];
 | |
|         } else {
 | |
|             runFactory(id, deps, factory);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     //define.require, which has access to all the values in the
 | |
|     //cache. Useful for AMD modules that all have IDs in the file,
 | |
|     //but need to finally export a value to node based on one of those
 | |
|     //IDs.
 | |
|     define.require = function (id) {
 | |
|         if (loaderCache[id]) {
 | |
|             return loaderCache[id];
 | |
|         }
 | |
| 
 | |
|         if (defineCache[id]) {
 | |
|             runFactory.apply(null, defineCache[id]);
 | |
|             return loaderCache[id];
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     define.amd = {};
 | |
| 
 | |
|     return define;
 | |
| }
 | |
| 
 | |
| module.exports = amdefine;
 | |
| 
 | |
| },{"__browserify_process":29,"path":30}],45:[function(require,module,exports){
 | |
| var sys = require("util");
 | |
| var MOZ_SourceMap = require("source-map");
 | |
| var UglifyJS = exports;
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| function array_to_hash(a) {
 | |
|     var ret = Object.create(null);
 | |
|     for (var i = 0; i < a.length; ++i)
 | |
|         ret[a[i]] = true;
 | |
|     return ret;
 | |
| };
 | |
| 
 | |
| function slice(a, start) {
 | |
|     return Array.prototype.slice.call(a, start || 0);
 | |
| };
 | |
| 
 | |
| function characters(str) {
 | |
|     return str.split("");
 | |
| };
 | |
| 
 | |
| function member(name, array) {
 | |
|     for (var i = array.length; --i >= 0;)
 | |
|         if (array[i] == name)
 | |
|             return true;
 | |
|     return false;
 | |
| };
 | |
| 
 | |
| function find_if(func, array) {
 | |
|     for (var i = 0, n = array.length; i < n; ++i) {
 | |
|         if (func(array[i]))
 | |
|             return array[i];
 | |
|     }
 | |
| };
 | |
| 
 | |
| function repeat_string(str, i) {
 | |
|     if (i <= 0) return "";
 | |
|     if (i == 1) return str;
 | |
|     var d = repeat_string(str, i >> 1);
 | |
|     d += d;
 | |
|     if (i & 1) d += str;
 | |
|     return d;
 | |
| };
 | |
| 
 | |
| function DefaultsError(msg, defs) {
 | |
|     Error.call(this, msg);
 | |
|     this.msg = msg;
 | |
|     this.defs = defs;
 | |
| };
 | |
| DefaultsError.prototype = Object.create(Error.prototype);
 | |
| DefaultsError.prototype.constructor = DefaultsError;
 | |
| 
 | |
| DefaultsError.croak = function(msg, defs) {
 | |
|     throw new DefaultsError(msg, defs);
 | |
| };
 | |
| 
 | |
| function defaults(args, defs, croak) {
 | |
|     if (args === true)
 | |
|         args = {};
 | |
|     var ret = args || {};
 | |
|     if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i))
 | |
|         DefaultsError.croak("`" + i + "` is not a supported option", defs);
 | |
|     for (var i in defs) if (defs.hasOwnProperty(i)) {
 | |
|         ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i];
 | |
|     }
 | |
|     return ret;
 | |
| };
 | |
| 
 | |
| function merge(obj, ext) {
 | |
|     for (var i in ext) if (ext.hasOwnProperty(i)) {
 | |
|         obj[i] = ext[i];
 | |
|     }
 | |
|     return obj;
 | |
| };
 | |
| 
 | |
| function noop() {};
 | |
| 
 | |
| var MAP = (function(){
 | |
|     function MAP(a, f, backwards) {
 | |
|         var ret = [], top = [], i;
 | |
|         function doit() {
 | |
|             var val = f(a[i], i);
 | |
|             var is_last = val instanceof Last;
 | |
|             if (is_last) val = val.v;
 | |
|             if (val instanceof AtTop) {
 | |
|                 val = val.v;
 | |
|                 if (val instanceof Splice) {
 | |
|                     top.push.apply(top, backwards ? val.v.slice().reverse() : val.v);
 | |
|                 } else {
 | |
|                     top.push(val);
 | |
|                 }
 | |
|             }
 | |
|             else if (val !== skip) {
 | |
|                 if (val instanceof Splice) {
 | |
|                     ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
 | |
|                 } else {
 | |
|                     ret.push(val);
 | |
|                 }
 | |
|             }
 | |
|             return is_last;
 | |
|         };
 | |
|         if (a instanceof Array) {
 | |
|             if (backwards) {
 | |
|                 for (i = a.length; --i >= 0;) if (doit()) break;
 | |
|                 ret.reverse();
 | |
|                 top.reverse();
 | |
|             } else {
 | |
|                 for (i = 0; i < a.length; ++i) if (doit()) break;
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             for (i in a) if (a.hasOwnProperty(i)) if (doit()) break;
 | |
|         }
 | |
|         return top.concat(ret);
 | |
|     };
 | |
|     MAP.at_top = function(val) { return new AtTop(val) };
 | |
|     MAP.splice = function(val) { return new Splice(val) };
 | |
|     MAP.last = function(val) { return new Last(val) };
 | |
|     var skip = MAP.skip = {};
 | |
|     function AtTop(val) { this.v = val };
 | |
|     function Splice(val) { this.v = val };
 | |
|     function Last(val) { this.v = val };
 | |
|     return MAP;
 | |
| })();
 | |
| 
 | |
| function push_uniq(array, el) {
 | |
|     if (array.indexOf(el) < 0)
 | |
|         array.push(el);
 | |
| };
 | |
| 
 | |
| function string_template(text, props) {
 | |
|     return text.replace(/\{(.+?)\}/g, function(str, p){
 | |
|         return props[p];
 | |
|     });
 | |
| };
 | |
| 
 | |
| function remove(array, el) {
 | |
|     for (var i = array.length; --i >= 0;) {
 | |
|         if (array[i] === el) array.splice(i, 1);
 | |
|     }
 | |
| };
 | |
| 
 | |
| function mergeSort(array, cmp) {
 | |
|     if (array.length < 2) return array.slice();
 | |
|     function merge(a, b) {
 | |
|         var r = [], ai = 0, bi = 0, i = 0;
 | |
|         while (ai < a.length && bi < b.length) {
 | |
|             cmp(a[ai], b[bi]) <= 0
 | |
|                 ? r[i++] = a[ai++]
 | |
|                 : r[i++] = b[bi++];
 | |
|         }
 | |
|         if (ai < a.length) r.push.apply(r, a.slice(ai));
 | |
|         if (bi < b.length) r.push.apply(r, b.slice(bi));
 | |
|         return r;
 | |
|     };
 | |
|     function _ms(a) {
 | |
|         if (a.length <= 1)
 | |
|             return a;
 | |
|         var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m);
 | |
|         left = _ms(left);
 | |
|         right = _ms(right);
 | |
|         return merge(left, right);
 | |
|     };
 | |
|     return _ms(array);
 | |
| };
 | |
| 
 | |
| function set_difference(a, b) {
 | |
|     return a.filter(function(el){
 | |
|         return b.indexOf(el) < 0;
 | |
|     });
 | |
| };
 | |
| 
 | |
| function set_intersection(a, b) {
 | |
|     return a.filter(function(el){
 | |
|         return b.indexOf(el) >= 0;
 | |
|     });
 | |
| };
 | |
| 
 | |
| // this function is taken from Acorn [1], written by Marijn Haverbeke
 | |
| // [1] https://github.com/marijnh/acorn
 | |
| function makePredicate(words) {
 | |
|     if (!(words instanceof Array)) words = words.split(" ");
 | |
|     var f = "", cats = [];
 | |
|     out: for (var i = 0; i < words.length; ++i) {
 | |
|         for (var j = 0; j < cats.length; ++j)
 | |
|             if (cats[j][0].length == words[i].length) {
 | |
|                 cats[j].push(words[i]);
 | |
|                 continue out;
 | |
|             }
 | |
|         cats.push([words[i]]);
 | |
|     }
 | |
|     function compareTo(arr) {
 | |
|         if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
 | |
|         f += "switch(str){";
 | |
|         for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":";
 | |
|         f += "return true}return false;";
 | |
|     }
 | |
|     // When there are more than three length categories, an outer
 | |
|     // switch first dispatches on the lengths, to save on comparisons.
 | |
|     if (cats.length > 3) {
 | |
|         cats.sort(function(a, b) {return b.length - a.length;});
 | |
|         f += "switch(str.length){";
 | |
|         for (var i = 0; i < cats.length; ++i) {
 | |
|             var cat = cats[i];
 | |
|             f += "case " + cat[0].length + ":";
 | |
|             compareTo(cat);
 | |
|         }
 | |
|         f += "}";
 | |
|         // Otherwise, simply generate a flat `switch` statement.
 | |
|     } else {
 | |
|         compareTo(words);
 | |
|     }
 | |
|     return new Function("str", f);
 | |
| };
 | |
| 
 | |
| function all(array, predicate) {
 | |
|     for (var i = array.length; --i >= 0;)
 | |
|         if (!predicate(array[i]))
 | |
|             return false;
 | |
|     return true;
 | |
| };
 | |
| 
 | |
| function Dictionary() {
 | |
|     this._values = Object.create(null);
 | |
|     this._size = 0;
 | |
| };
 | |
| Dictionary.prototype = {
 | |
|     set: function(key, val) {
 | |
|         if (!this.has(key)) ++this._size;
 | |
|         this._values["$" + key] = val;
 | |
|         return this;
 | |
|     },
 | |
|     add: function(key, val) {
 | |
|         if (this.has(key)) {
 | |
|             this.get(key).push(val);
 | |
|         } else {
 | |
|             this.set(key, [ val ]);
 | |
|         }
 | |
|         return this;
 | |
|     },
 | |
|     get: function(key) { return this._values["$" + key] },
 | |
|     del: function(key) {
 | |
|         if (this.has(key)) {
 | |
|             --this._size;
 | |
|             delete this._values["$" + key];
 | |
|         }
 | |
|         return this;
 | |
|     },
 | |
|     has: function(key) { return ("$" + key) in this._values },
 | |
|     each: function(f) {
 | |
|         for (var i in this._values)
 | |
|             f(this._values[i], i.substr(1));
 | |
|     },
 | |
|     size: function() {
 | |
|         return this._size;
 | |
|     },
 | |
|     map: function(f) {
 | |
|         var ret = [];
 | |
|         for (var i in this._values)
 | |
|             ret.push(f(this._values[i], i.substr(1)));
 | |
|         return ret;
 | |
|     }
 | |
| };
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| function DEFNODE(type, props, methods, base) {
 | |
|     if (arguments.length < 4) base = AST_Node;
 | |
|     if (!props) props = [];
 | |
|     else props = props.split(/\s+/);
 | |
|     var self_props = props;
 | |
|     if (base && base.PROPS)
 | |
|         props = props.concat(base.PROPS);
 | |
|     var code = "return function AST_" + type + "(props){ if (props) { ";
 | |
|     for (var i = props.length; --i >= 0;) {
 | |
|         code += "this." + props[i] + " = props." + props[i] + ";";
 | |
|     }
 | |
|     var proto = base && new base;
 | |
|     if (proto && proto.initialize || (methods && methods.initialize))
 | |
|         code += "this.initialize();";
 | |
|     code += "}}";
 | |
|     var ctor = new Function(code)();
 | |
|     if (proto) {
 | |
|         ctor.prototype = proto;
 | |
|         ctor.BASE = base;
 | |
|     }
 | |
|     if (base) base.SUBCLASSES.push(ctor);
 | |
|     ctor.prototype.CTOR = ctor;
 | |
|     ctor.PROPS = props || null;
 | |
|     ctor.SELF_PROPS = self_props;
 | |
|     ctor.SUBCLASSES = [];
 | |
|     if (type) {
 | |
|         ctor.prototype.TYPE = ctor.TYPE = type;
 | |
|     }
 | |
|     if (methods) for (i in methods) if (methods.hasOwnProperty(i)) {
 | |
|         if (/^\$/.test(i)) {
 | |
|             ctor[i.substr(1)] = methods[i];
 | |
|         } else {
 | |
|             ctor.prototype[i] = methods[i];
 | |
|         }
 | |
|     }
 | |
|     ctor.DEFMETHOD = function(name, method) {
 | |
|         this.prototype[name] = method;
 | |
|     };
 | |
|     return ctor;
 | |
| };
 | |
| 
 | |
| var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", {
 | |
| }, null);
 | |
| 
 | |
| var AST_Node = DEFNODE("Node", "start end", {
 | |
|     clone: function() {
 | |
|         return new this.CTOR(this);
 | |
|     },
 | |
|     $documentation: "Base class of all AST nodes",
 | |
|     $propdoc: {
 | |
|         start: "[AST_Token] The first token of this node",
 | |
|         end: "[AST_Token] The last token of this node"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this);
 | |
|     },
 | |
|     walk: function(visitor) {
 | |
|         return this._walk(visitor); // not sure the indirection will be any help
 | |
|     }
 | |
| }, null);
 | |
| 
 | |
| AST_Node.warn_function = null;
 | |
| AST_Node.warn = function(txt, props) {
 | |
|     if (AST_Node.warn_function)
 | |
|         AST_Node.warn_function(string_template(txt, props));
 | |
| };
 | |
| 
 | |
| /* -----[ statements ]----- */
 | |
| 
 | |
| var AST_Statement = DEFNODE("Statement", null, {
 | |
|     $documentation: "Base class of all statements",
 | |
| });
 | |
| 
 | |
| var AST_Debugger = DEFNODE("Debugger", null, {
 | |
|     $documentation: "Represents a debugger statement",
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_Directive = DEFNODE("Directive", "value scope", {
 | |
|     $documentation: "Represents a directive, like \"use strict\";",
 | |
|     $propdoc: {
 | |
|         value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
 | |
|         scope: "[AST_Scope/S] The scope that this directive affects"
 | |
|     },
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
 | |
|     $documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
 | |
|     $propdoc: {
 | |
|         body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Statement);
 | |
| 
 | |
| function walk_body(node, visitor) {
 | |
|     if (node.body instanceof AST_Statement) {
 | |
|         node.body._walk(visitor);
 | |
|     }
 | |
|     else node.body.forEach(function(stat){
 | |
|         stat._walk(visitor);
 | |
|     });
 | |
| };
 | |
| 
 | |
| var AST_Block = DEFNODE("Block", "body", {
 | |
|     $documentation: "A body of statements (usually bracketed)",
 | |
|     $propdoc: {
 | |
|         body: "[AST_Statement*] an array of statements"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             walk_body(this, visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_BlockStatement = DEFNODE("BlockStatement", null, {
 | |
|     $documentation: "A block statement",
 | |
| }, AST_Block);
 | |
| 
 | |
| var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
 | |
|     $documentation: "The empty statement (empty block or simply a semicolon)",
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this);
 | |
|     }
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
 | |
|     $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
 | |
|     $propdoc: {
 | |
|         body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
 | |
|     $documentation: "Statement with a label",
 | |
|     $propdoc: {
 | |
|         label: "[AST_Label] a label definition"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.label._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_StatementWithBody);
 | |
| 
 | |
| var AST_IterationStatement = DEFNODE("IterationStatement", null, {
 | |
|     $documentation: "Internal class.  All loops inherit from it."
 | |
| }, AST_StatementWithBody);
 | |
| 
 | |
| var AST_DWLoop = DEFNODE("DWLoop", "condition", {
 | |
|     $documentation: "Base class for do/while statements",
 | |
|     $propdoc: {
 | |
|         condition: "[AST_Node] the loop condition.  Should not be instanceof AST_Statement"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.condition._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_IterationStatement);
 | |
| 
 | |
| var AST_Do = DEFNODE("Do", null, {
 | |
|     $documentation: "A `do` statement",
 | |
| }, AST_DWLoop);
 | |
| 
 | |
| var AST_While = DEFNODE("While", null, {
 | |
|     $documentation: "A `while` statement",
 | |
| }, AST_DWLoop);
 | |
| 
 | |
| var AST_For = DEFNODE("For", "init condition step", {
 | |
|     $documentation: "A `for` statement",
 | |
|     $propdoc: {
 | |
|         init: "[AST_Node?] the `for` initialization code, or null if empty",
 | |
|         condition: "[AST_Node?] the `for` termination clause, or null if empty",
 | |
|         step: "[AST_Node?] the `for` update clause, or null if empty"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             if (this.init) this.init._walk(visitor);
 | |
|             if (this.condition) this.condition._walk(visitor);
 | |
|             if (this.step) this.step._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_IterationStatement);
 | |
| 
 | |
| var AST_ForIn = DEFNODE("ForIn", "init name object", {
 | |
|     $documentation: "A `for ... in` statement",
 | |
|     $propdoc: {
 | |
|         init: "[AST_Node] the `for/in` initialization code",
 | |
|         name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
 | |
|         object: "[AST_Node] the object that we're looping through"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.init._walk(visitor);
 | |
|             this.object._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_IterationStatement);
 | |
| 
 | |
| var AST_With = DEFNODE("With", "expression", {
 | |
|     $documentation: "A `with` statement",
 | |
|     $propdoc: {
 | |
|         expression: "[AST_Node] the `with` expression"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_StatementWithBody);
 | |
| 
 | |
| /* -----[ scope and functions ]----- */
 | |
| 
 | |
| var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
 | |
|     $documentation: "Base class for all statements introducing a lexical scope",
 | |
|     $propdoc: {
 | |
|         directives: "[string*/S] an array of directives declared in this scope",
 | |
|         variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
 | |
|         functions: "[Object/S] like `variables`, but only lists function declarations",
 | |
|         uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
 | |
|         uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
 | |
|         parent_scope: "[AST_Scope?/S] link to the parent scope",
 | |
|         enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
 | |
|         cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
 | |
|     },
 | |
| }, AST_Block);
 | |
| 
 | |
| var AST_Toplevel = DEFNODE("Toplevel", "globals", {
 | |
|     $documentation: "The toplevel scope",
 | |
|     $propdoc: {
 | |
|         globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
 | |
|     },
 | |
|     wrap_enclose: function(arg_parameter_pairs) {
 | |
|         var self = this;
 | |
|         var args = [];
 | |
|         var parameters = [];
 | |
| 
 | |
|         arg_parameter_pairs.forEach(function(pair) {
 | |
|             var split = pair.split(":");
 | |
| 
 | |
|             args.push(split[0]);
 | |
|             parameters.push(split[1]);
 | |
|         });
 | |
| 
 | |
|         var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
 | |
|         wrapped_tl = parse(wrapped_tl);
 | |
|         wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
 | |
|             if (node instanceof AST_Directive && node.value == "$ORIG") {
 | |
|                 return MAP.splice(self.body);
 | |
|             }
 | |
|         }));
 | |
|         return wrapped_tl;
 | |
|     },
 | |
|     wrap_commonjs: function(name, export_all) {
 | |
|         var self = this;
 | |
|         var to_export = [];
 | |
|         if (export_all) {
 | |
|             self.figure_out_scope();
 | |
|             self.walk(new TreeWalker(function(node){
 | |
|                 if (node instanceof AST_SymbolDeclaration && node.definition().global) {
 | |
|                     if (!find_if(function(n){ return n.name == node.name }, to_export))
 | |
|                         to_export.push(node);
 | |
|                 }
 | |
|             }));
 | |
|         }
 | |
|         var wrapped_tl = "(function(exports, global){ global['" + name + "'] = exports; '$ORIG'; '$EXPORTS'; }({}, (function(){return this}())))";
 | |
|         wrapped_tl = parse(wrapped_tl);
 | |
|         wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
 | |
|             if (node instanceof AST_SimpleStatement) {
 | |
|                 node = node.body;
 | |
|                 if (node instanceof AST_String) switch (node.getValue()) {
 | |
|                   case "$ORIG":
 | |
|                     return MAP.splice(self.body);
 | |
|                   case "$EXPORTS":
 | |
|                     var body = [];
 | |
|                     to_export.forEach(function(sym){
 | |
|                         body.push(new AST_SimpleStatement({
 | |
|                             body: new AST_Assign({
 | |
|                                 left: new AST_Sub({
 | |
|                                     expression: new AST_SymbolRef({ name: "exports" }),
 | |
|                                     property: new AST_String({ value: sym.name }),
 | |
|                                 }),
 | |
|                                 operator: "=",
 | |
|                                 right: new AST_SymbolRef(sym),
 | |
|                             }),
 | |
|                         }));
 | |
|                     });
 | |
|                     return MAP.splice(body);
 | |
|                 }
 | |
|             }
 | |
|         }));
 | |
|         return wrapped_tl;
 | |
|     }
 | |
| }, AST_Scope);
 | |
| 
 | |
| var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
 | |
|     $documentation: "Base class for functions",
 | |
|     $propdoc: {
 | |
|         name: "[AST_SymbolDeclaration?] the name of this function",
 | |
|         argnames: "[AST_SymbolFunarg*] array of function arguments",
 | |
|         uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             if (this.name) this.name._walk(visitor);
 | |
|             this.argnames.forEach(function(arg){
 | |
|                 arg._walk(visitor);
 | |
|             });
 | |
|             walk_body(this, visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Scope);
 | |
| 
 | |
| var AST_Accessor = DEFNODE("Accessor", null, {
 | |
|     $documentation: "A setter/getter function.  The `name` property is always null."
 | |
| }, AST_Lambda);
 | |
| 
 | |
| var AST_Function = DEFNODE("Function", null, {
 | |
|     $documentation: "A function expression"
 | |
| }, AST_Lambda);
 | |
| 
 | |
| var AST_Defun = DEFNODE("Defun", null, {
 | |
|     $documentation: "A function definition"
 | |
| }, AST_Lambda);
 | |
| 
 | |
| /* -----[ JUMPS ]----- */
 | |
| 
 | |
| var AST_Jump = DEFNODE("Jump", null, {
 | |
|     $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_Exit = DEFNODE("Exit", "value", {
 | |
|     $documentation: "Base class for “exits” (`return` and `throw`)",
 | |
|     $propdoc: {
 | |
|         value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, this.value && function(){
 | |
|             this.value._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Jump);
 | |
| 
 | |
| var AST_Return = DEFNODE("Return", null, {
 | |
|     $documentation: "A `return` statement"
 | |
| }, AST_Exit);
 | |
| 
 | |
| var AST_Throw = DEFNODE("Throw", null, {
 | |
|     $documentation: "A `throw` statement"
 | |
| }, AST_Exit);
 | |
| 
 | |
| var AST_LoopControl = DEFNODE("LoopControl", "label", {
 | |
|     $documentation: "Base class for loop control statements (`break` and `continue`)",
 | |
|     $propdoc: {
 | |
|         label: "[AST_LabelRef?] the label, or null if none",
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, this.label && function(){
 | |
|             this.label._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Jump);
 | |
| 
 | |
| var AST_Break = DEFNODE("Break", null, {
 | |
|     $documentation: "A `break` statement"
 | |
| }, AST_LoopControl);
 | |
| 
 | |
| var AST_Continue = DEFNODE("Continue", null, {
 | |
|     $documentation: "A `continue` statement"
 | |
| }, AST_LoopControl);
 | |
| 
 | |
| /* -----[ IF ]----- */
 | |
| 
 | |
| var AST_If = DEFNODE("If", "condition alternative", {
 | |
|     $documentation: "A `if` statement",
 | |
|     $propdoc: {
 | |
|         condition: "[AST_Node] the `if` condition",
 | |
|         alternative: "[AST_Statement?] the `else` part, or null if not present"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.condition._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|             if (this.alternative) this.alternative._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_StatementWithBody);
 | |
| 
 | |
| /* -----[ SWITCH ]----- */
 | |
| 
 | |
| var AST_Switch = DEFNODE("Switch", "expression", {
 | |
|     $documentation: "A `switch` statement",
 | |
|     $propdoc: {
 | |
|         expression: "[AST_Node] the `switch` “discriminant”"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|             walk_body(this, visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Block);
 | |
| 
 | |
| var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
 | |
|     $documentation: "Base class for `switch` branches",
 | |
| }, AST_Block);
 | |
| 
 | |
| var AST_Default = DEFNODE("Default", null, {
 | |
|     $documentation: "A `default` switch branch",
 | |
| }, AST_SwitchBranch);
 | |
| 
 | |
| var AST_Case = DEFNODE("Case", "expression", {
 | |
|     $documentation: "A `case` switch branch",
 | |
|     $propdoc: {
 | |
|         expression: "[AST_Node] the `case` expression"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|             walk_body(this, visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_SwitchBranch);
 | |
| 
 | |
| /* -----[ EXCEPTIONS ]----- */
 | |
| 
 | |
| var AST_Try = DEFNODE("Try", "bcatch bfinally", {
 | |
|     $documentation: "A `try` statement",
 | |
|     $propdoc: {
 | |
|         bcatch: "[AST_Catch?] the catch block, or null if not present",
 | |
|         bfinally: "[AST_Finally?] the finally block, or null if not present"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             walk_body(this, visitor);
 | |
|             if (this.bcatch) this.bcatch._walk(visitor);
 | |
|             if (this.bfinally) this.bfinally._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Block);
 | |
| 
 | |
| var AST_Catch = DEFNODE("Catch", "argname", {
 | |
|     $documentation: "A `catch` node; only makes sense as part of a `try` statement",
 | |
|     $propdoc: {
 | |
|         argname: "[AST_SymbolCatch] symbol for the exception"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.argname._walk(visitor);
 | |
|             walk_body(this, visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Block);
 | |
| 
 | |
| var AST_Finally = DEFNODE("Finally", null, {
 | |
|     $documentation: "A `finally` node; only makes sense as part of a `try` statement"
 | |
| }, AST_Block);
 | |
| 
 | |
| /* -----[ VAR/CONST ]----- */
 | |
| 
 | |
| var AST_Definitions = DEFNODE("Definitions", "definitions", {
 | |
|     $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
 | |
|     $propdoc: {
 | |
|         definitions: "[AST_VarDef*] array of variable definitions"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.definitions.forEach(function(def){
 | |
|                 def._walk(visitor);
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_Var = DEFNODE("Var", null, {
 | |
|     $documentation: "A `var` statement"
 | |
| }, AST_Definitions);
 | |
| 
 | |
| var AST_Const = DEFNODE("Const", null, {
 | |
|     $documentation: "A `const` statement"
 | |
| }, AST_Definitions);
 | |
| 
 | |
| var AST_VarDef = DEFNODE("VarDef", "name value", {
 | |
|     $documentation: "A variable declaration; only appears in a AST_Definitions node",
 | |
|     $propdoc: {
 | |
|         name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
 | |
|         value: "[AST_Node?] initializer, or null of there's no initializer"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.name._walk(visitor);
 | |
|             if (this.value) this.value._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| /* -----[ OTHER ]----- */
 | |
| 
 | |
| var AST_Call = DEFNODE("Call", "expression args", {
 | |
|     $documentation: "A function call expression",
 | |
|     $propdoc: {
 | |
|         expression: "[AST_Node] expression to invoke as function",
 | |
|         args: "[AST_Node*] array of arguments"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|             this.args.forEach(function(arg){
 | |
|                 arg._walk(visitor);
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_New = DEFNODE("New", null, {
 | |
|     $documentation: "An object instantiation.  Derives from a function call since it has exactly the same properties"
 | |
| }, AST_Call);
 | |
| 
 | |
| var AST_Seq = DEFNODE("Seq", "car cdr", {
 | |
|     $documentation: "A sequence expression (two comma-separated expressions)",
 | |
|     $propdoc: {
 | |
|         car: "[AST_Node] first element in sequence",
 | |
|         cdr: "[AST_Node] second element in sequence"
 | |
|     },
 | |
|     $cons: function(x, y) {
 | |
|         var seq = new AST_Seq(x);
 | |
|         seq.car = x;
 | |
|         seq.cdr = y;
 | |
|         return seq;
 | |
|     },
 | |
|     $from_array: function(array) {
 | |
|         if (array.length == 0) return null;
 | |
|         if (array.length == 1) return array[0].clone();
 | |
|         var list = null;
 | |
|         for (var i = array.length; --i >= 0;) {
 | |
|             list = AST_Seq.cons(array[i], list);
 | |
|         }
 | |
|         var p = list;
 | |
|         while (p) {
 | |
|             if (p.cdr && !p.cdr.cdr) {
 | |
|                 p.cdr = p.cdr.car;
 | |
|                 break;
 | |
|             }
 | |
|             p = p.cdr;
 | |
|         }
 | |
|         return list;
 | |
|     },
 | |
|     to_array: function() {
 | |
|         var p = this, a = [];
 | |
|         while (p) {
 | |
|             a.push(p.car);
 | |
|             if (p.cdr && !(p.cdr instanceof AST_Seq)) {
 | |
|                 a.push(p.cdr);
 | |
|                 break;
 | |
|             }
 | |
|             p = p.cdr;
 | |
|         }
 | |
|         return a;
 | |
|     },
 | |
|     add: function(node) {
 | |
|         var p = this;
 | |
|         while (p) {
 | |
|             if (!(p.cdr instanceof AST_Seq)) {
 | |
|                 var cell = AST_Seq.cons(p.cdr, node);
 | |
|                 return p.cdr = cell;
 | |
|             }
 | |
|             p = p.cdr;
 | |
|         }
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.car._walk(visitor);
 | |
|             if (this.cdr) this.cdr._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
 | |
|     $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
 | |
|     $propdoc: {
 | |
|         expression: "[AST_Node] the “container” expression",
 | |
|         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"
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_Dot = DEFNODE("Dot", null, {
 | |
|     $documentation: "A dotted property access expression",
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_PropAccess);
 | |
| 
 | |
| var AST_Sub = DEFNODE("Sub", null, {
 | |
|     $documentation: "Index-style property access, i.e. `a[\"foo\"]`",
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|             this.property._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_PropAccess);
 | |
| 
 | |
| var AST_Unary = DEFNODE("Unary", "operator expression", {
 | |
|     $documentation: "Base class for unary expressions",
 | |
|     $propdoc: {
 | |
|         operator: "[string] the operator",
 | |
|         expression: "[AST_Node] expression that this unary operator applies to"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, {
 | |
|     $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`"
 | |
| }, AST_Unary);
 | |
| 
 | |
| var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
 | |
|     $documentation: "Unary postfix expression, i.e. `i++`"
 | |
| }, AST_Unary);
 | |
| 
 | |
| var AST_Binary = DEFNODE("Binary", "left operator right", {
 | |
|     $documentation: "Binary expression, i.e. `a + b`",
 | |
|     $propdoc: {
 | |
|         left: "[AST_Node] left-hand side expression",
 | |
|         operator: "[string] the operator",
 | |
|         right: "[AST_Node] right-hand side expression"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.left._walk(visitor);
 | |
|             this.right._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
 | |
|     $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
 | |
|     $propdoc: {
 | |
|         condition: "[AST_Node]",
 | |
|         consequent: "[AST_Node]",
 | |
|         alternative: "[AST_Node]"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.condition._walk(visitor);
 | |
|             this.consequent._walk(visitor);
 | |
|             this.alternative._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_Assign = DEFNODE("Assign", null, {
 | |
|     $documentation: "An assignment expression — `a = b + 5`",
 | |
| }, AST_Binary);
 | |
| 
 | |
| /* -----[ LITERALS ]----- */
 | |
| 
 | |
| var AST_Array = DEFNODE("Array", "elements", {
 | |
|     $documentation: "An array literal",
 | |
|     $propdoc: {
 | |
|         elements: "[AST_Node*] array of elements"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.elements.forEach(function(el){
 | |
|                 el._walk(visitor);
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_Object = DEFNODE("Object", "properties", {
 | |
|     $documentation: "An object literal",
 | |
|     $propdoc: {
 | |
|         properties: "[AST_ObjectProperty*] array of properties"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.properties.forEach(function(prop){
 | |
|                 prop._walk(visitor);
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
 | |
|     $documentation: "Base class for literal object properties",
 | |
|     $propdoc: {
 | |
|         key: "[string] the property name converted to a string for ObjectKeyVal.  For setters and getters this is an arbitrary AST_Node.",
 | |
|         value: "[AST_Node] property value.  For setters and getters this is an AST_Function."
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.value._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
 | |
|     $documentation: "A key: value object property",
 | |
| }, AST_ObjectProperty);
 | |
| 
 | |
| var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
 | |
|     $documentation: "An object setter property",
 | |
| }, AST_ObjectProperty);
 | |
| 
 | |
| var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
 | |
|     $documentation: "An object getter property",
 | |
| }, AST_ObjectProperty);
 | |
| 
 | |
| var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
 | |
|     $propdoc: {
 | |
|         name: "[string] name of this symbol",
 | |
|         scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
 | |
|         thedef: "[SymbolDef/S] the definition of this symbol"
 | |
|     },
 | |
|     $documentation: "Base class for all symbols",
 | |
| });
 | |
| 
 | |
| var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
 | |
|     $documentation: "The name of a property accessor (setter/getter function)"
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
 | |
|     $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
 | |
|     $propdoc: {
 | |
|         init: "[AST_Node*/S] array of initializers for this declaration."
 | |
|     }
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_SymbolVar = DEFNODE("SymbolVar", null, {
 | |
|     $documentation: "Symbol defining a variable",
 | |
| }, AST_SymbolDeclaration);
 | |
| 
 | |
| var AST_SymbolConst = DEFNODE("SymbolConst", null, {
 | |
|     $documentation: "A constant declaration"
 | |
| }, AST_SymbolDeclaration);
 | |
| 
 | |
| var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
 | |
|     $documentation: "Symbol naming a function argument",
 | |
| }, AST_SymbolVar);
 | |
| 
 | |
| var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
 | |
|     $documentation: "Symbol defining a function",
 | |
| }, AST_SymbolDeclaration);
 | |
| 
 | |
| var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
 | |
|     $documentation: "Symbol naming a function expression",
 | |
| }, AST_SymbolDeclaration);
 | |
| 
 | |
| var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
 | |
|     $documentation: "Symbol naming the exception in catch",
 | |
| }, AST_SymbolDeclaration);
 | |
| 
 | |
| var AST_Label = DEFNODE("Label", "references", {
 | |
|     $documentation: "Symbol naming a label (declaration)",
 | |
|     $propdoc: {
 | |
|         references: "[AST_LoopControl*] a list of nodes referring to this label"
 | |
|     },
 | |
|     initialize: function() {
 | |
|         this.references = [];
 | |
|         this.thedef = this;
 | |
|     }
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_SymbolRef = DEFNODE("SymbolRef", null, {
 | |
|     $documentation: "Reference to some symbol (not definition/declaration)",
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_LabelRef = DEFNODE("LabelRef", null, {
 | |
|     $documentation: "Reference to a label symbol",
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_This = DEFNODE("This", null, {
 | |
|     $documentation: "The `this` symbol",
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_Constant = DEFNODE("Constant", null, {
 | |
|     $documentation: "Base class for all constants",
 | |
|     getValue: function() {
 | |
|         return this.value;
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_String = DEFNODE("String", "value", {
 | |
|     $documentation: "A string literal",
 | |
|     $propdoc: {
 | |
|         value: "[string] the contents of this string"
 | |
|     }
 | |
| }, AST_Constant);
 | |
| 
 | |
| var AST_Number = DEFNODE("Number", "value", {
 | |
|     $documentation: "A number literal",
 | |
|     $propdoc: {
 | |
|         value: "[number] the numeric value"
 | |
|     }
 | |
| }, AST_Constant);
 | |
| 
 | |
| var AST_RegExp = DEFNODE("RegExp", "value", {
 | |
|     $documentation: "A regexp literal",
 | |
|     $propdoc: {
 | |
|         value: "[RegExp] the actual regexp"
 | |
|     }
 | |
| }, AST_Constant);
 | |
| 
 | |
| var AST_Atom = DEFNODE("Atom", null, {
 | |
|     $documentation: "Base class for atoms",
 | |
| }, AST_Constant);
 | |
| 
 | |
| var AST_Null = DEFNODE("Null", null, {
 | |
|     $documentation: "The `null` atom",
 | |
|     value: null
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_NaN = DEFNODE("NaN", null, {
 | |
|     $documentation: "The impossible value",
 | |
|     value: 0/0
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_Undefined = DEFNODE("Undefined", null, {
 | |
|     $documentation: "The `undefined` value",
 | |
|     value: (function(){}())
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_Hole = DEFNODE("Hole", null, {
 | |
|     $documentation: "A hole in an array",
 | |
|     value: (function(){}())
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_Infinity = DEFNODE("Infinity", null, {
 | |
|     $documentation: "The `Infinity` value",
 | |
|     value: 1/0
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_Boolean = DEFNODE("Boolean", null, {
 | |
|     $documentation: "Base class for booleans",
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_False = DEFNODE("False", null, {
 | |
|     $documentation: "The `false` atom",
 | |
|     value: false
 | |
| }, AST_Boolean);
 | |
| 
 | |
| var AST_True = DEFNODE("True", null, {
 | |
|     $documentation: "The `true` atom",
 | |
|     value: true
 | |
| }, AST_Boolean);
 | |
| 
 | |
| /* -----[ TreeWalker ]----- */
 | |
| 
 | |
| function TreeWalker(callback) {
 | |
|     this.visit = callback;
 | |
|     this.stack = [];
 | |
| };
 | |
| TreeWalker.prototype = {
 | |
|     _visit: function(node, descend) {
 | |
|         this.stack.push(node);
 | |
|         var ret = this.visit(node, descend ? function(){
 | |
|             descend.call(node);
 | |
|         } : noop);
 | |
|         if (!ret && descend) {
 | |
|             descend.call(node);
 | |
|         }
 | |
|         this.stack.pop();
 | |
|         return ret;
 | |
|     },
 | |
|     parent: function(n) {
 | |
|         return this.stack[this.stack.length - 2 - (n || 0)];
 | |
|     },
 | |
|     push: function (node) {
 | |
|         this.stack.push(node);
 | |
|     },
 | |
|     pop: function() {
 | |
|         return this.stack.pop();
 | |
|     },
 | |
|     self: function() {
 | |
|         return this.stack[this.stack.length - 1];
 | |
|     },
 | |
|     find_parent: function(type) {
 | |
|         var stack = this.stack;
 | |
|         for (var i = stack.length; --i >= 0;) {
 | |
|             var x = stack[i];
 | |
|             if (x instanceof type) return x;
 | |
|         }
 | |
|     },
 | |
|     has_directive: function(type) {
 | |
|         return this.find_parent(AST_Scope).has_directive(type);
 | |
|     },
 | |
|     in_boolean_context: function() {
 | |
|         var stack = this.stack;
 | |
|         var i = stack.length, self = stack[--i];
 | |
|         while (i > 0) {
 | |
|             var p = stack[--i];
 | |
|             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))
 | |
|             {
 | |
|                 return true;
 | |
|             }
 | |
|             if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")))
 | |
|                 return false;
 | |
|             self = p;
 | |
|         }
 | |
|     },
 | |
|     loopcontrol_target: function(label) {
 | |
|         var stack = this.stack;
 | |
|         if (label) for (var i = stack.length; --i >= 0;) {
 | |
|             var x = stack[i];
 | |
|             if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
 | |
|                 return x.body;
 | |
|             }
 | |
|         } else for (var i = stack.length; --i >= 0;) {
 | |
|             var x = stack[i];
 | |
|             if (x instanceof AST_Switch || x instanceof AST_IterationStatement)
 | |
|                 return x;
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
|     Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/).
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| 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';
 | |
| var KEYWORDS_ATOM = 'false null true';
 | |
| 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;
 | |
| var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
 | |
| 
 | |
| KEYWORDS = makePredicate(KEYWORDS);
 | |
| RESERVED_WORDS = makePredicate(RESERVED_WORDS);
 | |
| KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION);
 | |
| KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
 | |
| 
 | |
| var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
 | |
| 
 | |
| var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
 | |
| var RE_OCT_NUMBER = /^0[0-7]+$/;
 | |
| var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
 | |
| 
 | |
| var OPERATORS = makePredicate([
 | |
|     "in",
 | |
|     "instanceof",
 | |
|     "typeof",
 | |
|     "new",
 | |
|     "void",
 | |
|     "delete",
 | |
|     "++",
 | |
|     "--",
 | |
|     "+",
 | |
|     "-",
 | |
|     "!",
 | |
|     "~",
 | |
|     "&",
 | |
|     "|",
 | |
|     "^",
 | |
|     "*",
 | |
|     "/",
 | |
|     "%",
 | |
|     ">>",
 | |
|     "<<",
 | |
|     ">>>",
 | |
|     "<",
 | |
|     ">",
 | |
|     "<=",
 | |
|     ">=",
 | |
|     "==",
 | |
|     "===",
 | |
|     "!=",
 | |
|     "!==",
 | |
|     "?",
 | |
|     "=",
 | |
|     "+=",
 | |
|     "-=",
 | |
|     "/=",
 | |
|     "*=",
 | |
|     "%=",
 | |
|     ">>=",
 | |
|     "<<=",
 | |
|     ">>>=",
 | |
|     "|=",
 | |
|     "^=",
 | |
|     "&=",
 | |
|     "&&",
 | |
|     "||"
 | |
| ]);
 | |
| 
 | |
| 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"));
 | |
| 
 | |
| var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
 | |
| 
 | |
| var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
 | |
| 
 | |
| var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
 | |
| 
 | |
| /* -----[ Tokenizer ]----- */
 | |
| 
 | |
| // regexps adapted from http://xregexp.com/plugins/#unicode
 | |
| var UNICODE = {
 | |
|     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]"),
 | |
|     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]"),
 | |
|     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]"),
 | |
|     connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
 | |
| };
 | |
| 
 | |
| function is_letter(code) {
 | |
|     return (code >= 97 && code <= 122)
 | |
|         || (code >= 65 && code <= 90)
 | |
|         || (code >= 0xaa && UNICODE.letter.test(String.fromCharCode(code)));
 | |
| };
 | |
| 
 | |
| function is_digit(code) {
 | |
|     return code >= 48 && code <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9
 | |
| };
 | |
| 
 | |
| function is_alphanumeric_char(code) {
 | |
|     return is_digit(code) || is_letter(code);
 | |
| };
 | |
| 
 | |
| function is_unicode_combining_mark(ch) {
 | |
|     return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
 | |
| };
 | |
| 
 | |
| function is_unicode_connector_punctuation(ch) {
 | |
|     return UNICODE.connector_punctuation.test(ch);
 | |
| };
 | |
| 
 | |
| function is_identifier(name) {
 | |
|     return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name);
 | |
| };
 | |
| 
 | |
| function is_identifier_start(code) {
 | |
|     return code == 36 || code == 95 || is_letter(code);
 | |
| };
 | |
| 
 | |
| function is_identifier_char(ch) {
 | |
|     var code = ch.charCodeAt(0);
 | |
|     return is_identifier_start(code)
 | |
|         || is_digit(code)
 | |
|         || code == 8204 // \u200c: zero-width non-joiner <ZWNJ>
 | |
|         || code == 8205 // \u200d: zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
 | |
|         || is_unicode_combining_mark(ch)
 | |
|         || is_unicode_connector_punctuation(ch)
 | |
|     ;
 | |
| };
 | |
| 
 | |
| function is_identifier_string(str){
 | |
|     var i = str.length;
 | |
|     if (i == 0) return false;
 | |
|     if (!is_identifier_start(str.charCodeAt(0))) return false;
 | |
|     while (--i >= 0) {
 | |
|         if (!is_identifier_char(str.charAt(i)))
 | |
|             return false;
 | |
|     }
 | |
|     return true;
 | |
| };
 | |
| 
 | |
| function parse_js_number(num) {
 | |
|     if (RE_HEX_NUMBER.test(num)) {
 | |
|         return parseInt(num.substr(2), 16);
 | |
|     } else if (RE_OCT_NUMBER.test(num)) {
 | |
|         return parseInt(num.substr(1), 8);
 | |
|     } else if (RE_DEC_NUMBER.test(num)) {
 | |
|         return parseFloat(num);
 | |
|     }
 | |
| };
 | |
| 
 | |
| function JS_Parse_Error(message, line, col, pos) {
 | |
|     this.message = message;
 | |
|     this.line = line;
 | |
|     this.col = col;
 | |
|     this.pos = pos;
 | |
|     this.stack = new Error().stack;
 | |
| };
 | |
| 
 | |
| JS_Parse_Error.prototype.toString = function() {
 | |
|     return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
 | |
| };
 | |
| 
 | |
| function js_error(message, filename, line, col, pos) {
 | |
|     throw new JS_Parse_Error(message, line, col, pos);
 | |
| };
 | |
| 
 | |
| function is_token(token, type, val) {
 | |
|     return token.type == type && (val == null || token.value == val);
 | |
| };
 | |
| 
 | |
| var EX_EOF = {};
 | |
| 
 | |
| function tokenizer($TEXT, filename, html5_comments) {
 | |
| 
 | |
|     var S = {
 | |
|         text            : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ''),
 | |
|         filename        : filename,
 | |
|         pos             : 0,
 | |
|         tokpos          : 0,
 | |
|         line            : 1,
 | |
|         tokline         : 0,
 | |
|         col             : 0,
 | |
|         tokcol          : 0,
 | |
|         newline_before  : false,
 | |
|         regex_allowed   : false,
 | |
|         comments_before : []
 | |
|     };
 | |
| 
 | |
|     function peek() { return S.text.charAt(S.pos); };
 | |
| 
 | |
|     function next(signal_eof, in_string) {
 | |
|         var ch = S.text.charAt(S.pos++);
 | |
|         if (signal_eof && !ch)
 | |
|             throw EX_EOF;
 | |
|         if (ch == "\n") {
 | |
|             S.newline_before = S.newline_before || !in_string;
 | |
|             ++S.line;
 | |
|             S.col = 0;
 | |
|         } else {
 | |
|             ++S.col;
 | |
|         }
 | |
|         return ch;
 | |
|     };
 | |
| 
 | |
|     function forward(i) {
 | |
|         while (i-- > 0) next();
 | |
|     };
 | |
| 
 | |
|     function looking_at(str) {
 | |
|         return S.text.substr(S.pos, str.length) == str;
 | |
|     };
 | |
| 
 | |
|     function find(what, signal_eof) {
 | |
|         var pos = S.text.indexOf(what, S.pos);
 | |
|         if (signal_eof && pos == -1) throw EX_EOF;
 | |
|         return pos;
 | |
|     };
 | |
| 
 | |
|     function start_token() {
 | |
|         S.tokline = S.line;
 | |
|         S.tokcol = S.col;
 | |
|         S.tokpos = S.pos;
 | |
|     };
 | |
| 
 | |
|     var prev_was_dot = false;
 | |
|     function token(type, value, is_comment) {
 | |
|         S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) ||
 | |
|                            (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
 | |
|                            (type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
 | |
|         prev_was_dot = (type == "punc" && value == ".");
 | |
|         var ret = {
 | |
|             type   : type,
 | |
|             value  : value,
 | |
|             line   : S.tokline,
 | |
|             col    : S.tokcol,
 | |
|             pos    : S.tokpos,
 | |
|             endpos : S.pos,
 | |
|             nlb    : S.newline_before,
 | |
|             file   : filename
 | |
|         };
 | |
|         if (!is_comment) {
 | |
|             ret.comments_before = S.comments_before;
 | |
|             S.comments_before = [];
 | |
|             // make note of any newlines in the comments that came before
 | |
|             for (var i = 0, len = ret.comments_before.length; i < len; i++) {
 | |
|                 ret.nlb = ret.nlb || ret.comments_before[i].nlb;
 | |
|             }
 | |
|         }
 | |
|         S.newline_before = false;
 | |
|         return new AST_Token(ret);
 | |
|     };
 | |
| 
 | |
|     function skip_whitespace() {
 | |
|         while (WHITESPACE_CHARS(peek()))
 | |
|             next();
 | |
|     };
 | |
| 
 | |
|     function read_while(pred) {
 | |
|         var ret = "", ch, i = 0;
 | |
|         while ((ch = peek()) && pred(ch, i++))
 | |
|             ret += next();
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     function parse_error(err) {
 | |
|         js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
 | |
|     };
 | |
| 
 | |
|     function read_num(prefix) {
 | |
|         var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
 | |
|         var num = read_while(function(ch, i){
 | |
|             var code = ch.charCodeAt(0);
 | |
|             switch (code) {
 | |
|               case 120: case 88: // xX
 | |
|                 return has_x ? false : (has_x = true);
 | |
|               case 101: case 69: // eE
 | |
|                 return has_x ? true : has_e ? false : (has_e = after_e = true);
 | |
|               case 45: // -
 | |
|                 return after_e || (i == 0 && !prefix);
 | |
|               case 43: // +
 | |
|                 return after_e;
 | |
|               case (after_e = false, 46): // .
 | |
|                 return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
 | |
|             }
 | |
|             return is_alphanumeric_char(code);
 | |
|         });
 | |
|         if (prefix) num = prefix + num;
 | |
|         var valid = parse_js_number(num);
 | |
|         if (!isNaN(valid)) {
 | |
|             return token("num", valid);
 | |
|         } else {
 | |
|             parse_error("Invalid syntax: " + num);
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function read_escaped_char(in_string) {
 | |
|         var ch = next(true, in_string);
 | |
|         switch (ch.charCodeAt(0)) {
 | |
|           case 110 : return "\n";
 | |
|           case 114 : return "\r";
 | |
|           case 116 : return "\t";
 | |
|           case 98  : return "\b";
 | |
|           case 118 : return "\u000b"; // \v
 | |
|           case 102 : return "\f";
 | |
|           case 48  : return "\0";
 | |
|           case 120 : return String.fromCharCode(hex_bytes(2)); // \x
 | |
|           case 117 : return String.fromCharCode(hex_bytes(4)); // \u
 | |
|           case 10  : return ""; // newline
 | |
|           default  : return ch;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function hex_bytes(n) {
 | |
|         var num = 0;
 | |
|         for (; n > 0; --n) {
 | |
|             var digit = parseInt(next(true), 16);
 | |
|             if (isNaN(digit))
 | |
|                 parse_error("Invalid hex-character pattern in string");
 | |
|             num = (num << 4) | digit;
 | |
|         }
 | |
|         return num;
 | |
|     };
 | |
| 
 | |
|     var read_string = with_eof_error("Unterminated string constant", function(){
 | |
|         var quote = next(), ret = "";
 | |
|         for (;;) {
 | |
|             var ch = next(true);
 | |
|             if (ch == "\\") {
 | |
|                 // read OctalEscapeSequence (XXX: deprecated if "strict mode")
 | |
|                 // https://github.com/mishoo/UglifyJS/issues/178
 | |
|                 var octal_len = 0, first = null;
 | |
|                 ch = read_while(function(ch){
 | |
|                     if (ch >= "0" && ch <= "7") {
 | |
|                         if (!first) {
 | |
|                             first = ch;
 | |
|                             return ++octal_len;
 | |
|                         }
 | |
|                         else if (first <= "3" && octal_len <= 2) return ++octal_len;
 | |
|                         else if (first >= "4" && octal_len <= 1) return ++octal_len;
 | |
|                     }
 | |
|                     return false;
 | |
|                 });
 | |
|                 if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
 | |
|                 else ch = read_escaped_char(true);
 | |
|             }
 | |
|             else if (ch == quote) break;
 | |
|             ret += ch;
 | |
|         }
 | |
|         return token("string", ret);
 | |
|     });
 | |
| 
 | |
|     function skip_line_comment(type) {
 | |
|         var regex_allowed = S.regex_allowed;
 | |
|         var i = find("\n"), ret;
 | |
|         if (i == -1) {
 | |
|             ret = S.text.substr(S.pos);
 | |
|             S.pos = S.text.length;
 | |
|         } else {
 | |
|             ret = S.text.substring(S.pos, i);
 | |
|             S.pos = i;
 | |
|         }
 | |
|         S.comments_before.push(token(type, ret, true));
 | |
|         S.regex_allowed = regex_allowed;
 | |
|         return next_token();
 | |
|     };
 | |
| 
 | |
|     var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
 | |
|         var regex_allowed = S.regex_allowed;
 | |
|         var i = find("*/", true);
 | |
|         var text = S.text.substring(S.pos, i);
 | |
|         var a = text.split("\n"), n = a.length;
 | |
|         // update stream position
 | |
|         S.pos = i + 2;
 | |
|         S.line += n - 1;
 | |
|         if (n > 1) S.col = a[n - 1].length;
 | |
|         else S.col += a[n - 1].length;
 | |
|         S.col += 2;
 | |
|         var nlb = S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
 | |
|         S.comments_before.push(token("comment2", text, true));
 | |
|         S.regex_allowed = regex_allowed;
 | |
|         S.newline_before = nlb;
 | |
|         return next_token();
 | |
|     });
 | |
| 
 | |
|     function read_name() {
 | |
|         var backslash = false, name = "", ch, escaped = false, hex;
 | |
|         while ((ch = peek()) != null) {
 | |
|             if (!backslash) {
 | |
|                 if (ch == "\\") escaped = backslash = true, next();
 | |
|                 else if (is_identifier_char(ch)) name += next();
 | |
|                 else break;
 | |
|             }
 | |
|             else {
 | |
|                 if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
 | |
|                 ch = read_escaped_char();
 | |
|                 if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
 | |
|                 name += ch;
 | |
|                 backslash = false;
 | |
|             }
 | |
|         }
 | |
|         if (KEYWORDS(name) && escaped) {
 | |
|             hex = name.charCodeAt(0).toString(16).toUpperCase();
 | |
|             name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
 | |
|         }
 | |
|         return name;
 | |
|     };
 | |
| 
 | |
|     var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
 | |
|         var prev_backslash = false, ch, in_class = false;
 | |
|         while ((ch = next(true))) if (prev_backslash) {
 | |
|             regexp += "\\" + ch;
 | |
|             prev_backslash = false;
 | |
|         } else if (ch == "[") {
 | |
|             in_class = true;
 | |
|             regexp += ch;
 | |
|         } else if (ch == "]" && in_class) {
 | |
|             in_class = false;
 | |
|             regexp += ch;
 | |
|         } else if (ch == "/" && !in_class) {
 | |
|             break;
 | |
|         } else if (ch == "\\") {
 | |
|             prev_backslash = true;
 | |
|         } else {
 | |
|             regexp += ch;
 | |
|         }
 | |
|         var mods = read_name();
 | |
|         return token("regexp", new RegExp(regexp, mods));
 | |
|     });
 | |
| 
 | |
|     function read_operator(prefix) {
 | |
|         function grow(op) {
 | |
|             if (!peek()) return op;
 | |
|             var bigger = op + peek();
 | |
|             if (OPERATORS(bigger)) {
 | |
|                 next();
 | |
|                 return grow(bigger);
 | |
|             } else {
 | |
|                 return op;
 | |
|             }
 | |
|         };
 | |
|         return token("operator", grow(prefix || next()));
 | |
|     };
 | |
| 
 | |
|     function handle_slash() {
 | |
|         next();
 | |
|         switch (peek()) {
 | |
|           case "/":
 | |
|             next();
 | |
|             return skip_line_comment("comment1");
 | |
|           case "*":
 | |
|             next();
 | |
|             return skip_multiline_comment();
 | |
|         }
 | |
|         return S.regex_allowed ? read_regexp("") : read_operator("/");
 | |
|     };
 | |
| 
 | |
|     function handle_dot() {
 | |
|         next();
 | |
|         return is_digit(peek().charCodeAt(0))
 | |
|             ? read_num(".")
 | |
|             : token("punc", ".");
 | |
|     };
 | |
| 
 | |
|     function read_word() {
 | |
|         var word = read_name();
 | |
|         if (prev_was_dot) return token("name", word);
 | |
|         return KEYWORDS_ATOM(word) ? token("atom", word)
 | |
|             : !KEYWORDS(word) ? token("name", word)
 | |
|             : OPERATORS(word) ? token("operator", word)
 | |
|             : token("keyword", word);
 | |
|     };
 | |
| 
 | |
|     function with_eof_error(eof_error, cont) {
 | |
|         return function(x) {
 | |
|             try {
 | |
|                 return cont(x);
 | |
|             } catch(ex) {
 | |
|                 if (ex === EX_EOF) parse_error(eof_error);
 | |
|                 else throw ex;
 | |
|             }
 | |
|         };
 | |
|     };
 | |
| 
 | |
|     function next_token(force_regexp) {
 | |
|         if (force_regexp != null)
 | |
|             return read_regexp(force_regexp);
 | |
|         skip_whitespace();
 | |
|         start_token();
 | |
|         if (html5_comments) {
 | |
|             if (looking_at("<!--")) {
 | |
|                 forward(4);
 | |
|                 return skip_line_comment("comment3");
 | |
|             }
 | |
|             if (looking_at("-->") && S.newline_before) {
 | |
|                 forward(3);
 | |
|                 return skip_line_comment("comment4");
 | |
|             }
 | |
|         }
 | |
|         var ch = peek();
 | |
|         if (!ch) return token("eof");
 | |
|         var code = ch.charCodeAt(0);
 | |
|         switch (code) {
 | |
|           case 34: case 39: return read_string();
 | |
|           case 46: return handle_dot();
 | |
|           case 47: return handle_slash();
 | |
|         }
 | |
|         if (is_digit(code)) return read_num();
 | |
|         if (PUNC_CHARS(ch)) return token("punc", next());
 | |
|         if (OPERATOR_CHARS(ch)) return read_operator();
 | |
|         if (code == 92 || is_identifier_start(code)) return read_word();
 | |
|         parse_error("Unexpected character '" + ch + "'");
 | |
|     };
 | |
| 
 | |
|     next_token.context = function(nc) {
 | |
|         if (nc) S = nc;
 | |
|         return S;
 | |
|     };
 | |
| 
 | |
|     return next_token;
 | |
| 
 | |
| };
 | |
| 
 | |
| /* -----[ Parser (constants) ]----- */
 | |
| 
 | |
| var UNARY_PREFIX = makePredicate([
 | |
|     "typeof",
 | |
|     "void",
 | |
|     "delete",
 | |
|     "--",
 | |
|     "++",
 | |
|     "!",
 | |
|     "~",
 | |
|     "-",
 | |
|     "+"
 | |
| ]);
 | |
| 
 | |
| var UNARY_POSTFIX = makePredicate([ "--", "++" ]);
 | |
| 
 | |
| var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
 | |
| 
 | |
| var PRECEDENCE = (function(a, ret){
 | |
|     for (var i = 0; i < a.length; ++i) {
 | |
|         var b = a[i];
 | |
|         for (var j = 0; j < b.length; ++j) {
 | |
|             ret[b[j]] = i + 1;
 | |
|         }
 | |
|     }
 | |
|     return ret;
 | |
| })(
 | |
|     [
 | |
|         ["||"],
 | |
|         ["&&"],
 | |
|         ["|"],
 | |
|         ["^"],
 | |
|         ["&"],
 | |
|         ["==", "===", "!=", "!=="],
 | |
|         ["<", ">", "<=", ">=", "in", "instanceof"],
 | |
|         [">>", "<<", ">>>"],
 | |
|         ["+", "-"],
 | |
|         ["*", "/", "%"]
 | |
|     ],
 | |
|     {}
 | |
| );
 | |
| 
 | |
| var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
 | |
| 
 | |
| var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
 | |
| 
 | |
| /* -----[ Parser ]----- */
 | |
| 
 | |
| function parse($TEXT, options) {
 | |
| 
 | |
|     options = defaults(options, {
 | |
|         strict         : false,
 | |
|         filename       : null,
 | |
|         toplevel       : null,
 | |
|         expression     : false,
 | |
|         html5_comments : true,
 | |
|     });
 | |
| 
 | |
|     var S = {
 | |
|         input         : (typeof $TEXT == "string"
 | |
|                          ? tokenizer($TEXT, options.filename,
 | |
|                                      options.html5_comments)
 | |
|                          : $TEXT),
 | |
|         token         : null,
 | |
|         prev          : null,
 | |
|         peeked        : null,
 | |
|         in_function   : 0,
 | |
|         in_directives : true,
 | |
|         in_loop       : 0,
 | |
|         labels        : []
 | |
|     };
 | |
| 
 | |
|     S.token = next();
 | |
| 
 | |
|     function is(type, value) {
 | |
|         return is_token(S.token, type, value);
 | |
|     };
 | |
| 
 | |
|     function peek() { return S.peeked || (S.peeked = S.input()); };
 | |
| 
 | |
|     function next() {
 | |
|         S.prev = S.token;
 | |
|         if (S.peeked) {
 | |
|             S.token = S.peeked;
 | |
|             S.peeked = null;
 | |
|         } else {
 | |
|             S.token = S.input();
 | |
|         }
 | |
|         S.in_directives = S.in_directives && (
 | |
|             S.token.type == "string" || is("punc", ";")
 | |
|         );
 | |
|         return S.token;
 | |
|     };
 | |
| 
 | |
|     function prev() {
 | |
|         return S.prev;
 | |
|     };
 | |
| 
 | |
|     function croak(msg, line, col, pos) {
 | |
|         var ctx = S.input.context();
 | |
|         js_error(msg,
 | |
|                  ctx.filename,
 | |
|                  line != null ? line : ctx.tokline,
 | |
|                  col != null ? col : ctx.tokcol,
 | |
|                  pos != null ? pos : ctx.tokpos);
 | |
|     };
 | |
| 
 | |
|     function token_error(token, msg) {
 | |
|         croak(msg, token.line, token.col);
 | |
|     };
 | |
| 
 | |
|     function unexpected(token) {
 | |
|         if (token == null)
 | |
|             token = S.token;
 | |
|         token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
 | |
|     };
 | |
| 
 | |
|     function expect_token(type, val) {
 | |
|         if (is(type, val)) {
 | |
|             return next();
 | |
|         }
 | |
|         token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
 | |
|     };
 | |
| 
 | |
|     function expect(punc) { return expect_token("punc", punc); };
 | |
| 
 | |
|     function can_insert_semicolon() {
 | |
|         return !options.strict && (
 | |
|             S.token.nlb || is("eof") || is("punc", "}")
 | |
|         );
 | |
|     };
 | |
| 
 | |
|     function semicolon() {
 | |
|         if (is("punc", ";")) next();
 | |
|         else if (!can_insert_semicolon()) unexpected();
 | |
|     };
 | |
| 
 | |
|     function parenthesised() {
 | |
|         expect("(");
 | |
|         var exp = expression(true);
 | |
|         expect(")");
 | |
|         return exp;
 | |
|     };
 | |
| 
 | |
|     function embed_tokens(parser) {
 | |
|         return function() {
 | |
|             var start = S.token;
 | |
|             var expr = parser();
 | |
|             var end = prev();
 | |
|             expr.start = start;
 | |
|             expr.end = end;
 | |
|             return expr;
 | |
|         };
 | |
|     };
 | |
| 
 | |
|     function handle_regexp() {
 | |
|         if (is("operator", "/") || is("operator", "/=")) {
 | |
|             S.peeked = null;
 | |
|             S.token = S.input(S.token.value.substr(1)); // force regexp
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     var statement = embed_tokens(function() {
 | |
|         var tmp;
 | |
|         handle_regexp();
 | |
|         switch (S.token.type) {
 | |
|           case "string":
 | |
|             var dir = S.in_directives, stat = simple_statement();
 | |
|             // XXXv2: decide how to fix directives
 | |
|             if (dir && stat.body instanceof AST_String && !is("punc", ","))
 | |
|                 return new AST_Directive({ value: stat.body.value });
 | |
|             return stat;
 | |
|           case "num":
 | |
|           case "regexp":
 | |
|           case "operator":
 | |
|           case "atom":
 | |
|             return simple_statement();
 | |
| 
 | |
|           case "name":
 | |
|             return is_token(peek(), "punc", ":")
 | |
|                 ? labeled_statement()
 | |
|                 : simple_statement();
 | |
| 
 | |
|           case "punc":
 | |
|             switch (S.token.value) {
 | |
|               case "{":
 | |
|                 return new AST_BlockStatement({
 | |
|                     start : S.token,
 | |
|                     body  : block_(),
 | |
|                     end   : prev()
 | |
|                 });
 | |
|               case "[":
 | |
|               case "(":
 | |
|                 return simple_statement();
 | |
|               case ";":
 | |
|                 next();
 | |
|                 return new AST_EmptyStatement();
 | |
|               default:
 | |
|                 unexpected();
 | |
|             }
 | |
| 
 | |
|           case "keyword":
 | |
|             switch (tmp = S.token.value, next(), tmp) {
 | |
|               case "break":
 | |
|                 return break_cont(AST_Break);
 | |
| 
 | |
|               case "continue":
 | |
|                 return break_cont(AST_Continue);
 | |
| 
 | |
|               case "debugger":
 | |
|                 semicolon();
 | |
|                 return new AST_Debugger();
 | |
| 
 | |
|               case "do":
 | |
|                 return new AST_Do({
 | |
|                     body      : in_loop(statement),
 | |
|                     condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), tmp)
 | |
|                 });
 | |
| 
 | |
|               case "while":
 | |
|                 return new AST_While({
 | |
|                     condition : parenthesised(),
 | |
|                     body      : in_loop(statement)
 | |
|                 });
 | |
| 
 | |
|               case "for":
 | |
|                 return for_();
 | |
| 
 | |
|               case "function":
 | |
|                 return function_(AST_Defun);
 | |
| 
 | |
|               case "if":
 | |
|                 return if_();
 | |
| 
 | |
|               case "return":
 | |
|                 if (S.in_function == 0)
 | |
|                     croak("'return' outside of function");
 | |
|                 return new AST_Return({
 | |
|                     value: ( is("punc", ";")
 | |
|                              ? (next(), null)
 | |
|                              : can_insert_semicolon()
 | |
|                              ? null
 | |
|                              : (tmp = expression(true), semicolon(), tmp) )
 | |
|                 });
 | |
| 
 | |
|               case "switch":
 | |
|                 return new AST_Switch({
 | |
|                     expression : parenthesised(),
 | |
|                     body       : in_loop(switch_body_)
 | |
|                 });
 | |
| 
 | |
|               case "throw":
 | |
|                 if (S.token.nlb)
 | |
|                     croak("Illegal newline after 'throw'");
 | |
|                 return new AST_Throw({
 | |
|                     value: (tmp = expression(true), semicolon(), tmp)
 | |
|                 });
 | |
| 
 | |
|               case "try":
 | |
|                 return try_();
 | |
| 
 | |
|               case "var":
 | |
|                 return tmp = var_(), semicolon(), tmp;
 | |
| 
 | |
|               case "const":
 | |
|                 return tmp = const_(), semicolon(), tmp;
 | |
| 
 | |
|               case "with":
 | |
|                 return new AST_With({
 | |
|                     expression : parenthesised(),
 | |
|                     body       : statement()
 | |
|                 });
 | |
| 
 | |
|               default:
 | |
|                 unexpected();
 | |
|             }
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     function labeled_statement() {
 | |
|         var label = as_symbol(AST_Label);
 | |
|         if (find_if(function(l){ return l.name == label.name }, S.labels)) {
 | |
|             // ECMA-262, 12.12: An ECMAScript program is considered
 | |
|             // syntactically incorrect if it contains a
 | |
|             // LabelledStatement that is enclosed by a
 | |
|             // LabelledStatement with the same Identifier as label.
 | |
|             croak("Label " + label.name + " defined twice");
 | |
|         }
 | |
|         expect(":");
 | |
|         S.labels.push(label);
 | |
|         var stat = statement();
 | |
|         S.labels.pop();
 | |
|         if (!(stat instanceof AST_IterationStatement)) {
 | |
|             // check for `continue` that refers to this label.
 | |
|             // those should be reported as syntax errors.
 | |
|             // https://github.com/mishoo/UglifyJS2/issues/287
 | |
|             label.references.forEach(function(ref){
 | |
|                 if (ref instanceof AST_Continue) {
 | |
|                     ref = ref.label.start;
 | |
|                     croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
 | |
|                           ref.line, ref.col, ref.pos);
 | |
|                 }
 | |
|             });
 | |
|         }
 | |
|         return new AST_LabeledStatement({ body: stat, label: label });
 | |
|     };
 | |
| 
 | |
|     function simple_statement(tmp) {
 | |
|         return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) });
 | |
|     };
 | |
| 
 | |
|     function break_cont(type) {
 | |
|         var label = null, ldef;
 | |
|         if (!can_insert_semicolon()) {
 | |
|             label = as_symbol(AST_LabelRef, true);
 | |
|         }
 | |
|         if (label != null) {
 | |
|             ldef = find_if(function(l){ return l.name == label.name }, S.labels);
 | |
|             if (!ldef)
 | |
|                 croak("Undefined label " + label.name);
 | |
|             label.thedef = ldef;
 | |
|         }
 | |
|         else if (S.in_loop == 0)
 | |
|             croak(type.TYPE + " not inside a loop or switch");
 | |
|         semicolon();
 | |
|         var stat = new type({ label: label });
 | |
|         if (ldef) ldef.references.push(stat);
 | |
|         return stat;
 | |
|     };
 | |
| 
 | |
|     function for_() {
 | |
|         expect("(");
 | |
|         var init = null;
 | |
|         if (!is("punc", ";")) {
 | |
|             init = is("keyword", "var")
 | |
|                 ? (next(), var_(true))
 | |
|                 : expression(true, true);
 | |
|             if (is("operator", "in")) {
 | |
|                 if (init instanceof AST_Var && init.definitions.length > 1)
 | |
|                     croak("Only one variable declaration allowed in for..in loop");
 | |
|                 next();
 | |
|                 return for_in(init);
 | |
|             }
 | |
|         }
 | |
|         return regular_for(init);
 | |
|     };
 | |
| 
 | |
|     function regular_for(init) {
 | |
|         expect(";");
 | |
|         var test = is("punc", ";") ? null : expression(true);
 | |
|         expect(";");
 | |
|         var step = is("punc", ")") ? null : expression(true);
 | |
|         expect(")");
 | |
|         return new AST_For({
 | |
|             init      : init,
 | |
|             condition : test,
 | |
|             step      : step,
 | |
|             body      : in_loop(statement)
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function for_in(init) {
 | |
|         var lhs = init instanceof AST_Var ? init.definitions[0].name : null;
 | |
|         var obj = expression(true);
 | |
|         expect(")");
 | |
|         return new AST_ForIn({
 | |
|             init   : init,
 | |
|             name   : lhs,
 | |
|             object : obj,
 | |
|             body   : in_loop(statement)
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     var function_ = function(ctor) {
 | |
|         var in_statement = ctor === AST_Defun;
 | |
|         var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
 | |
|         if (in_statement && !name)
 | |
|             unexpected();
 | |
|         expect("(");
 | |
|         return new ctor({
 | |
|             name: name,
 | |
|             argnames: (function(first, a){
 | |
|                 while (!is("punc", ")")) {
 | |
|                     if (first) first = false; else expect(",");
 | |
|                     a.push(as_symbol(AST_SymbolFunarg));
 | |
|                 }
 | |
|                 next();
 | |
|                 return a;
 | |
|             })(true, []),
 | |
|             body: (function(loop, labels){
 | |
|                 ++S.in_function;
 | |
|                 S.in_directives = true;
 | |
|                 S.in_loop = 0;
 | |
|                 S.labels = [];
 | |
|                 var a = block_();
 | |
|                 --S.in_function;
 | |
|                 S.in_loop = loop;
 | |
|                 S.labels = labels;
 | |
|                 return a;
 | |
|             })(S.in_loop, S.labels)
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function if_() {
 | |
|         var cond = parenthesised(), body = statement(), belse = null;
 | |
|         if (is("keyword", "else")) {
 | |
|             next();
 | |
|             belse = statement();
 | |
|         }
 | |
|         return new AST_If({
 | |
|             condition   : cond,
 | |
|             body        : body,
 | |
|             alternative : belse
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function block_() {
 | |
|         expect("{");
 | |
|         var a = [];
 | |
|         while (!is("punc", "}")) {
 | |
|             if (is("eof")) unexpected();
 | |
|             a.push(statement());
 | |
|         }
 | |
|         next();
 | |
|         return a;
 | |
|     };
 | |
| 
 | |
|     function switch_body_() {
 | |
|         expect("{");
 | |
|         var a = [], cur = null, branch = null, tmp;
 | |
|         while (!is("punc", "}")) {
 | |
|             if (is("eof")) unexpected();
 | |
|             if (is("keyword", "case")) {
 | |
|                 if (branch) branch.end = prev();
 | |
|                 cur = [];
 | |
|                 branch = new AST_Case({
 | |
|                     start      : (tmp = S.token, next(), tmp),
 | |
|                     expression : expression(true),
 | |
|                     body       : cur
 | |
|                 });
 | |
|                 a.push(branch);
 | |
|                 expect(":");
 | |
|             }
 | |
|             else if (is("keyword", "default")) {
 | |
|                 if (branch) branch.end = prev();
 | |
|                 cur = [];
 | |
|                 branch = new AST_Default({
 | |
|                     start : (tmp = S.token, next(), expect(":"), tmp),
 | |
|                     body  : cur
 | |
|                 });
 | |
|                 a.push(branch);
 | |
|             }
 | |
|             else {
 | |
|                 if (!cur) unexpected();
 | |
|                 cur.push(statement());
 | |
|             }
 | |
|         }
 | |
|         if (branch) branch.end = prev();
 | |
|         next();
 | |
|         return a;
 | |
|     };
 | |
| 
 | |
|     function try_() {
 | |
|         var body = block_(), bcatch = null, bfinally = null;
 | |
|         if (is("keyword", "catch")) {
 | |
|             var start = S.token;
 | |
|             next();
 | |
|             expect("(");
 | |
|             var name = as_symbol(AST_SymbolCatch);
 | |
|             expect(")");
 | |
|             bcatch = new AST_Catch({
 | |
|                 start   : start,
 | |
|                 argname : name,
 | |
|                 body    : block_(),
 | |
|                 end     : prev()
 | |
|             });
 | |
|         }
 | |
|         if (is("keyword", "finally")) {
 | |
|             var start = S.token;
 | |
|             next();
 | |
|             bfinally = new AST_Finally({
 | |
|                 start : start,
 | |
|                 body  : block_(),
 | |
|                 end   : prev()
 | |
|             });
 | |
|         }
 | |
|         if (!bcatch && !bfinally)
 | |
|             croak("Missing catch/finally blocks");
 | |
|         return new AST_Try({
 | |
|             body     : body,
 | |
|             bcatch   : bcatch,
 | |
|             bfinally : bfinally
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function vardefs(no_in, in_const) {
 | |
|         var a = [];
 | |
|         for (;;) {
 | |
|             a.push(new AST_VarDef({
 | |
|                 start : S.token,
 | |
|                 name  : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
 | |
|                 value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
 | |
|                 end   : prev()
 | |
|             }));
 | |
|             if (!is("punc", ","))
 | |
|                 break;
 | |
|             next();
 | |
|         }
 | |
|         return a;
 | |
|     };
 | |
| 
 | |
|     var var_ = function(no_in) {
 | |
|         return new AST_Var({
 | |
|             start       : prev(),
 | |
|             definitions : vardefs(no_in, false),
 | |
|             end         : prev()
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     var const_ = function() {
 | |
|         return new AST_Const({
 | |
|             start       : prev(),
 | |
|             definitions : vardefs(false, true),
 | |
|             end         : prev()
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     var new_ = function() {
 | |
|         var start = S.token;
 | |
|         expect_token("operator", "new");
 | |
|         var newexp = expr_atom(false), args;
 | |
|         if (is("punc", "(")) {
 | |
|             next();
 | |
|             args = expr_list(")");
 | |
|         } else {
 | |
|             args = [];
 | |
|         }
 | |
|         return subscripts(new AST_New({
 | |
|             start      : start,
 | |
|             expression : newexp,
 | |
|             args       : args,
 | |
|             end        : prev()
 | |
|         }), true);
 | |
|     };
 | |
| 
 | |
|     function as_atom_node() {
 | |
|         var tok = S.token, ret;
 | |
|         switch (tok.type) {
 | |
|           case "name":
 | |
|           case "keyword":
 | |
|             ret = _make_symbol(AST_SymbolRef);
 | |
|             break;
 | |
|           case "num":
 | |
|             ret = new AST_Number({ start: tok, end: tok, value: tok.value });
 | |
|             break;
 | |
|           case "string":
 | |
|             ret = new AST_String({ start: tok, end: tok, value: tok.value });
 | |
|             break;
 | |
|           case "regexp":
 | |
|             ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
 | |
|             break;
 | |
|           case "atom":
 | |
|             switch (tok.value) {
 | |
|               case "false":
 | |
|                 ret = new AST_False({ start: tok, end: tok });
 | |
|                 break;
 | |
|               case "true":
 | |
|                 ret = new AST_True({ start: tok, end: tok });
 | |
|                 break;
 | |
|               case "null":
 | |
|                 ret = new AST_Null({ start: tok, end: tok });
 | |
|                 break;
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         next();
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     var expr_atom = function(allow_calls) {
 | |
|         if (is("operator", "new")) {
 | |
|             return new_();
 | |
|         }
 | |
|         var start = S.token;
 | |
|         if (is("punc")) {
 | |
|             switch (start.value) {
 | |
|               case "(":
 | |
|                 next();
 | |
|                 var ex = expression(true);
 | |
|                 ex.start = start;
 | |
|                 ex.end = S.token;
 | |
|                 expect(")");
 | |
|                 return subscripts(ex, allow_calls);
 | |
|               case "[":
 | |
|                 return subscripts(array_(), allow_calls);
 | |
|               case "{":
 | |
|                 return subscripts(object_(), allow_calls);
 | |
|             }
 | |
|             unexpected();
 | |
|         }
 | |
|         if (is("keyword", "function")) {
 | |
|             next();
 | |
|             var func = function_(AST_Function);
 | |
|             func.start = start;
 | |
|             func.end = prev();
 | |
|             return subscripts(func, allow_calls);
 | |
|         }
 | |
|         if (ATOMIC_START_TOKEN[S.token.type]) {
 | |
|             return subscripts(as_atom_node(), allow_calls);
 | |
|         }
 | |
|         unexpected();
 | |
|     };
 | |
| 
 | |
|     function expr_list(closing, allow_trailing_comma, allow_empty) {
 | |
|         var first = true, a = [];
 | |
|         while (!is("punc", closing)) {
 | |
|             if (first) first = false; else expect(",");
 | |
|             if (allow_trailing_comma && is("punc", closing)) break;
 | |
|             if (is("punc", ",") && allow_empty) {
 | |
|                 a.push(new AST_Hole({ start: S.token, end: S.token }));
 | |
|             } else {
 | |
|                 a.push(expression(false));
 | |
|             }
 | |
|         }
 | |
|         next();
 | |
|         return a;
 | |
|     };
 | |
| 
 | |
|     var array_ = embed_tokens(function() {
 | |
|         expect("[");
 | |
|         return new AST_Array({
 | |
|             elements: expr_list("]", !options.strict, true)
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     var object_ = embed_tokens(function() {
 | |
|         expect("{");
 | |
|         var first = true, a = [];
 | |
|         while (!is("punc", "}")) {
 | |
|             if (first) first = false; else expect(",");
 | |
|             if (!options.strict && is("punc", "}"))
 | |
|                 // allow trailing comma
 | |
|                 break;
 | |
|             var start = S.token;
 | |
|             var type = start.type;
 | |
|             var name = as_property_name();
 | |
|             if (type == "name" && !is("punc", ":")) {
 | |
|                 if (name == "get") {
 | |
|                     a.push(new AST_ObjectGetter({
 | |
|                         start : start,
 | |
|                         key   : as_atom_node(),
 | |
|                         value : function_(AST_Accessor),
 | |
|                         end   : prev()
 | |
|                     }));
 | |
|                     continue;
 | |
|                 }
 | |
|                 if (name == "set") {
 | |
|                     a.push(new AST_ObjectSetter({
 | |
|                         start : start,
 | |
|                         key   : as_atom_node(),
 | |
|                         value : function_(AST_Accessor),
 | |
|                         end   : prev()
 | |
|                     }));
 | |
|                     continue;
 | |
|                 }
 | |
|             }
 | |
|             expect(":");
 | |
|             a.push(new AST_ObjectKeyVal({
 | |
|                 start : start,
 | |
|                 key   : name,
 | |
|                 value : expression(false),
 | |
|                 end   : prev()
 | |
|             }));
 | |
|         }
 | |
|         next();
 | |
|         return new AST_Object({ properties: a });
 | |
|     });
 | |
| 
 | |
|     function as_property_name() {
 | |
|         var tmp = S.token;
 | |
|         next();
 | |
|         switch (tmp.type) {
 | |
|           case "num":
 | |
|           case "string":
 | |
|           case "name":
 | |
|           case "operator":
 | |
|           case "keyword":
 | |
|           case "atom":
 | |
|             return tmp.value;
 | |
|           default:
 | |
|             unexpected();
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function as_name() {
 | |
|         var tmp = S.token;
 | |
|         next();
 | |
|         switch (tmp.type) {
 | |
|           case "name":
 | |
|           case "operator":
 | |
|           case "keyword":
 | |
|           case "atom":
 | |
|             return tmp.value;
 | |
|           default:
 | |
|             unexpected();
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function _make_symbol(type) {
 | |
|         var name = S.token.value;
 | |
|         return new (name == "this" ? AST_This : type)({
 | |
|             name  : String(name),
 | |
|             start : S.token,
 | |
|             end   : S.token
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function as_symbol(type, noerror) {
 | |
|         if (!is("name")) {
 | |
|             if (!noerror) croak("Name expected");
 | |
|             return null;
 | |
|         }
 | |
|         var sym = _make_symbol(type);
 | |
|         next();
 | |
|         return sym;
 | |
|     };
 | |
| 
 | |
|     var subscripts = function(expr, allow_calls) {
 | |
|         var start = expr.start;
 | |
|         if (is("punc", ".")) {
 | |
|             next();
 | |
|             return subscripts(new AST_Dot({
 | |
|                 start      : start,
 | |
|                 expression : expr,
 | |
|                 property   : as_name(),
 | |
|                 end        : prev()
 | |
|             }), allow_calls);
 | |
|         }
 | |
|         if (is("punc", "[")) {
 | |
|             next();
 | |
|             var prop = expression(true);
 | |
|             expect("]");
 | |
|             return subscripts(new AST_Sub({
 | |
|                 start      : start,
 | |
|                 expression : expr,
 | |
|                 property   : prop,
 | |
|                 end        : prev()
 | |
|             }), allow_calls);
 | |
|         }
 | |
|         if (allow_calls && is("punc", "(")) {
 | |
|             next();
 | |
|             return subscripts(new AST_Call({
 | |
|                 start      : start,
 | |
|                 expression : expr,
 | |
|                 args       : expr_list(")"),
 | |
|                 end        : prev()
 | |
|             }), true);
 | |
|         }
 | |
|         return expr;
 | |
|     };
 | |
| 
 | |
|     var maybe_unary = function(allow_calls) {
 | |
|         var start = S.token;
 | |
|         if (is("operator") && UNARY_PREFIX(start.value)) {
 | |
|             next();
 | |
|             handle_regexp();
 | |
|             var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls));
 | |
|             ex.start = start;
 | |
|             ex.end = prev();
 | |
|             return ex;
 | |
|         }
 | |
|         var val = expr_atom(allow_calls);
 | |
|         while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
 | |
|             val = make_unary(AST_UnaryPostfix, S.token.value, val);
 | |
|             val.start = start;
 | |
|             val.end = S.token;
 | |
|             next();
 | |
|         }
 | |
|         return val;
 | |
|     };
 | |
| 
 | |
|     function make_unary(ctor, op, expr) {
 | |
|         if ((op == "++" || op == "--") && !is_assignable(expr))
 | |
|             croak("Invalid use of " + op + " operator");
 | |
|         return new ctor({ operator: op, expression: expr });
 | |
|     };
 | |
| 
 | |
|     var expr_op = function(left, min_prec, no_in) {
 | |
|         var op = is("operator") ? S.token.value : null;
 | |
|         if (op == "in" && no_in) op = null;
 | |
|         var prec = op != null ? PRECEDENCE[op] : null;
 | |
|         if (prec != null && prec > min_prec) {
 | |
|             next();
 | |
|             var right = expr_op(maybe_unary(true), prec, no_in);
 | |
|             return expr_op(new AST_Binary({
 | |
|                 start    : left.start,
 | |
|                 left     : left,
 | |
|                 operator : op,
 | |
|                 right    : right,
 | |
|                 end      : right.end
 | |
|             }), min_prec, no_in);
 | |
|         }
 | |
|         return left;
 | |
|     };
 | |
| 
 | |
|     function expr_ops(no_in) {
 | |
|         return expr_op(maybe_unary(true), 0, no_in);
 | |
|     };
 | |
| 
 | |
|     var maybe_conditional = function(no_in) {
 | |
|         var start = S.token;
 | |
|         var expr = expr_ops(no_in);
 | |
|         if (is("operator", "?")) {
 | |
|             next();
 | |
|             var yes = expression(false);
 | |
|             expect(":");
 | |
|             return new AST_Conditional({
 | |
|                 start       : start,
 | |
|                 condition   : expr,
 | |
|                 consequent  : yes,
 | |
|                 alternative : expression(false, no_in),
 | |
|                 end         : peek()
 | |
|             });
 | |
|         }
 | |
|         return expr;
 | |
|     };
 | |
| 
 | |
|     function is_assignable(expr) {
 | |
|         if (!options.strict) return true;
 | |
|         if (expr instanceof AST_This) return false;
 | |
|         return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
 | |
|     };
 | |
| 
 | |
|     var maybe_assign = function(no_in) {
 | |
|         var start = S.token;
 | |
|         var left = maybe_conditional(no_in), val = S.token.value;
 | |
|         if (is("operator") && ASSIGNMENT(val)) {
 | |
|             if (is_assignable(left)) {
 | |
|                 next();
 | |
|                 return new AST_Assign({
 | |
|                     start    : start,
 | |
|                     left     : left,
 | |
|                     operator : val,
 | |
|                     right    : maybe_assign(no_in),
 | |
|                     end      : prev()
 | |
|                 });
 | |
|             }
 | |
|             croak("Invalid assignment");
 | |
|         }
 | |
|         return left;
 | |
|     };
 | |
| 
 | |
|     var expression = function(commas, no_in) {
 | |
|         var start = S.token;
 | |
|         var expr = maybe_assign(no_in);
 | |
|         if (commas && is("punc", ",")) {
 | |
|             next();
 | |
|             return new AST_Seq({
 | |
|                 start  : start,
 | |
|                 car    : expr,
 | |
|                 cdr    : expression(true, no_in),
 | |
|                 end    : peek()
 | |
|             });
 | |
|         }
 | |
|         return expr;
 | |
|     };
 | |
| 
 | |
|     function in_loop(cont) {
 | |
|         ++S.in_loop;
 | |
|         var ret = cont();
 | |
|         --S.in_loop;
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     if (options.expression) {
 | |
|         return expression(true);
 | |
|     }
 | |
| 
 | |
|     return (function(){
 | |
|         var start = S.token;
 | |
|         var body = [];
 | |
|         while (!is("eof"))
 | |
|             body.push(statement());
 | |
|         var end = prev();
 | |
|         var toplevel = options.toplevel;
 | |
|         if (toplevel) {
 | |
|             toplevel.body = toplevel.body.concat(body);
 | |
|             toplevel.end = end;
 | |
|         } else {
 | |
|             toplevel = new AST_Toplevel({ start: start, body: body, end: end });
 | |
|         }
 | |
|         return toplevel;
 | |
|     })();
 | |
| 
 | |
| };
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| // Tree transformer helpers.
 | |
| 
 | |
| function TreeTransformer(before, after) {
 | |
|     TreeWalker.call(this);
 | |
|     this.before = before;
 | |
|     this.after = after;
 | |
| }
 | |
| TreeTransformer.prototype = new TreeWalker;
 | |
| 
 | |
| (function(undefined){
 | |
| 
 | |
|     function _(node, descend) {
 | |
|         node.DEFMETHOD("transform", function(tw, in_list){
 | |
|             var x, y;
 | |
|             tw.push(this);
 | |
|             if (tw.before) x = tw.before(this, descend, in_list);
 | |
|             if (x === undefined) {
 | |
|                 if (!tw.after) {
 | |
|                     x = this;
 | |
|                     descend(x, tw);
 | |
|                 } else {
 | |
|                     tw.stack[tw.stack.length - 1] = x = this.clone();
 | |
|                     descend(x, tw);
 | |
|                     y = tw.after(x, in_list);
 | |
|                     if (y !== undefined) x = y;
 | |
|                 }
 | |
|             }
 | |
|             tw.pop();
 | |
|             return x;
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function do_list(list, tw) {
 | |
|         return MAP(list, function(node){
 | |
|             return node.transform(tw, true);
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     _(AST_Node, noop);
 | |
| 
 | |
|     _(AST_LabeledStatement, function(self, tw){
 | |
|         self.label = self.label.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_SimpleStatement, function(self, tw){
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Block, function(self, tw){
 | |
|         self.body = do_list(self.body, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_DWLoop, function(self, tw){
 | |
|         self.condition = self.condition.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_For, function(self, tw){
 | |
|         if (self.init) self.init = self.init.transform(tw);
 | |
|         if (self.condition) self.condition = self.condition.transform(tw);
 | |
|         if (self.step) self.step = self.step.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_ForIn, function(self, tw){
 | |
|         self.init = self.init.transform(tw);
 | |
|         self.object = self.object.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_With, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Exit, function(self, tw){
 | |
|         if (self.value) self.value = self.value.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_LoopControl, function(self, tw){
 | |
|         if (self.label) self.label = self.label.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_If, function(self, tw){
 | |
|         self.condition = self.condition.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|         if (self.alternative) self.alternative = self.alternative.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Switch, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|         self.body = do_list(self.body, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Case, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|         self.body = do_list(self.body, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Try, function(self, tw){
 | |
|         self.body = do_list(self.body, tw);
 | |
|         if (self.bcatch) self.bcatch = self.bcatch.transform(tw);
 | |
|         if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Catch, function(self, tw){
 | |
|         self.argname = self.argname.transform(tw);
 | |
|         self.body = do_list(self.body, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Definitions, function(self, tw){
 | |
|         self.definitions = do_list(self.definitions, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_VarDef, function(self, tw){
 | |
|         self.name = self.name.transform(tw);
 | |
|         if (self.value) self.value = self.value.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Lambda, function(self, tw){
 | |
|         if (self.name) self.name = self.name.transform(tw);
 | |
|         self.argnames = do_list(self.argnames, tw);
 | |
|         self.body = do_list(self.body, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Call, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|         self.args = do_list(self.args, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Seq, function(self, tw){
 | |
|         self.car = self.car.transform(tw);
 | |
|         self.cdr = self.cdr.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Dot, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Sub, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|         self.property = self.property.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Unary, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Binary, function(self, tw){
 | |
|         self.left = self.left.transform(tw);
 | |
|         self.right = self.right.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Conditional, function(self, tw){
 | |
|         self.condition = self.condition.transform(tw);
 | |
|         self.consequent = self.consequent.transform(tw);
 | |
|         self.alternative = self.alternative.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Array, function(self, tw){
 | |
|         self.elements = do_list(self.elements, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Object, function(self, tw){
 | |
|         self.properties = do_list(self.properties, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_ObjectProperty, function(self, tw){
 | |
|         self.value = self.value.transform(tw);
 | |
|     });
 | |
| 
 | |
| })();
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| function SymbolDef(scope, index, orig) {
 | |
|     this.name = orig.name;
 | |
|     this.orig = [ orig ];
 | |
|     this.scope = scope;
 | |
|     this.references = [];
 | |
|     this.global = false;
 | |
|     this.mangled_name = null;
 | |
|     this.undeclared = false;
 | |
|     this.constant = false;
 | |
|     this.index = index;
 | |
| };
 | |
| 
 | |
| SymbolDef.prototype = {
 | |
|     unmangleable: function(options) {
 | |
|         return (this.global && !(options && options.toplevel))
 | |
|             || this.undeclared
 | |
|             || (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with));
 | |
|     },
 | |
|     mangle: function(options) {
 | |
|         if (!this.mangled_name && !this.unmangleable(options)) {
 | |
|             var s = this.scope;
 | |
|             if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda)
 | |
|                 s = s.parent_scope;
 | |
|             this.mangled_name = s.next_mangled(options, this);
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
 | |
|     options = defaults(options, {
 | |
|         screw_ie8: false
 | |
|     });
 | |
| 
 | |
|     // pass 1: setup scope chaining and handle definitions
 | |
|     var self = this;
 | |
|     var scope = self.parent_scope = null;
 | |
|     var defun = null;
 | |
|     var nesting = 0;
 | |
|     var tw = new TreeWalker(function(node, descend){
 | |
|         if (options.screw_ie8 && node instanceof AST_Catch) {
 | |
|             var save_scope = scope;
 | |
|             scope = new AST_Scope(node);
 | |
|             scope.init_scope_vars(nesting);
 | |
|             scope.parent_scope = save_scope;
 | |
|             descend();
 | |
|             scope = save_scope;
 | |
|             return true;
 | |
|         }
 | |
|         if (node instanceof AST_Scope) {
 | |
|             node.init_scope_vars(nesting);
 | |
|             var save_scope = node.parent_scope = scope;
 | |
|             var save_defun = defun;
 | |
|             defun = scope = node;
 | |
|             ++nesting; descend(); --nesting;
 | |
|             scope = save_scope;
 | |
|             defun = save_defun;
 | |
|             return true;        // don't descend again in TreeWalker
 | |
|         }
 | |
|         if (node instanceof AST_Directive) {
 | |
|             node.scope = scope;
 | |
|             push_uniq(scope.directives, node.value);
 | |
|             return true;
 | |
|         }
 | |
|         if (node instanceof AST_With) {
 | |
|             for (var s = scope; s; s = s.parent_scope)
 | |
|                 s.uses_with = true;
 | |
|             return;
 | |
|         }
 | |
|         if (node instanceof AST_Symbol) {
 | |
|             node.scope = scope;
 | |
|         }
 | |
|         if (node instanceof AST_SymbolLambda) {
 | |
|             defun.def_function(node);
 | |
|         }
 | |
|         else if (node instanceof AST_SymbolDefun) {
 | |
|             // Careful here, the scope where this should be defined is
 | |
|             // the parent scope.  The reason is that we enter a new
 | |
|             // scope when we encounter the AST_Defun node (which is
 | |
|             // instanceof AST_Scope) but we get to the symbol a bit
 | |
|             // later.
 | |
|             (node.scope = defun.parent_scope).def_function(node);
 | |
|         }
 | |
|         else if (node instanceof AST_SymbolVar
 | |
|                  || node instanceof AST_SymbolConst) {
 | |
|             var def = defun.def_variable(node);
 | |
|             def.constant = node instanceof AST_SymbolConst;
 | |
|             def.init = tw.parent().value;
 | |
|         }
 | |
|         else if (node instanceof AST_SymbolCatch) {
 | |
|             (options.screw_ie8 ? scope : defun)
 | |
|                 .def_variable(node);
 | |
|         }
 | |
|     });
 | |
|     self.walk(tw);
 | |
| 
 | |
|     // pass 2: find back references and eval
 | |
|     var func = null;
 | |
|     var globals = self.globals = new Dictionary();
 | |
|     var tw = new TreeWalker(function(node, descend){
 | |
|         if (node instanceof AST_Lambda) {
 | |
|             var prev_func = func;
 | |
|             func = node;
 | |
|             descend();
 | |
|             func = prev_func;
 | |
|             return true;
 | |
|         }
 | |
|         if (node instanceof AST_SymbolRef) {
 | |
|             var name = node.name;
 | |
|             var sym = node.scope.find_variable(name);
 | |
|             if (!sym) {
 | |
|                 var g;
 | |
|                 if (globals.has(name)) {
 | |
|                     g = globals.get(name);
 | |
|                 } else {
 | |
|                     g = new SymbolDef(self, globals.size(), node);
 | |
|                     g.undeclared = true;
 | |
|                     g.global = true;
 | |
|                     globals.set(name, g);
 | |
|                 }
 | |
|                 node.thedef = g;
 | |
|                 if (name == "eval" && tw.parent() instanceof AST_Call) {
 | |
|                     for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)
 | |
|                         s.uses_eval = true;
 | |
|                 }
 | |
|                 if (func && name == "arguments") {
 | |
|                     func.uses_arguments = true;
 | |
|                 }
 | |
|             } else {
 | |
|                 node.thedef = sym;
 | |
|             }
 | |
|             node.reference();
 | |
|             return true;
 | |
|         }
 | |
|     });
 | |
|     self.walk(tw);
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
 | |
|     this.directives = [];     // contains the directives defined in this scope, i.e. "use strict"
 | |
|     this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
 | |
|     this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
 | |
|     this.uses_with = false;   // will be set to true if this or some nested scope uses the `with` statement
 | |
|     this.uses_eval = false;   // will be set to true if this or nested scope uses the global `eval`
 | |
|     this.parent_scope = null; // the parent scope
 | |
|     this.enclosed = [];       // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
 | |
|     this.cname = -1;          // the current index for mangling functions/variables
 | |
|     this.nesting = nesting;   // the nesting level of this scope (0 means toplevel)
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("strict", function(){
 | |
|     return this.has_directive("use strict");
 | |
| });
 | |
| 
 | |
| AST_Lambda.DEFMETHOD("init_scope_vars", function(){
 | |
|     AST_Scope.prototype.init_scope_vars.apply(this, arguments);
 | |
|     this.uses_arguments = false;
 | |
| });
 | |
| 
 | |
| AST_SymbolRef.DEFMETHOD("reference", function() {
 | |
|     var def = this.definition();
 | |
|     def.references.push(this);
 | |
|     var s = this.scope;
 | |
|     while (s) {
 | |
|         push_uniq(s.enclosed, def);
 | |
|         if (s === def.scope) break;
 | |
|         s = s.parent_scope;
 | |
|     }
 | |
|     this.frame = this.scope.nesting - def.scope.nesting;
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("find_variable", function(name){
 | |
|     if (name instanceof AST_Symbol) name = name.name;
 | |
|     return this.variables.get(name)
 | |
|         || (this.parent_scope && this.parent_scope.find_variable(name));
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("has_directive", function(value){
 | |
|     return this.parent_scope && this.parent_scope.has_directive(value)
 | |
|         || (this.directives.indexOf(value) >= 0 ? this : null);
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("def_function", function(symbol){
 | |
|     this.functions.set(symbol.name, this.def_variable(symbol));
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("def_variable", function(symbol){
 | |
|     var def;
 | |
|     if (!this.variables.has(symbol.name)) {
 | |
|         def = new SymbolDef(this, this.variables.size(), symbol);
 | |
|         this.variables.set(symbol.name, def);
 | |
|         def.global = !this.parent_scope;
 | |
|     } else {
 | |
|         def = this.variables.get(symbol.name);
 | |
|         def.orig.push(symbol);
 | |
|     }
 | |
|     return symbol.thedef = def;
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("next_mangled", function(options){
 | |
|     var ext = this.enclosed;
 | |
|     out: while (true) {
 | |
|         var m = base54(++this.cname);
 | |
|         if (!is_identifier(m)) continue; // skip over "do"
 | |
| 
 | |
|         // https://github.com/mishoo/UglifyJS2/issues/242 -- do not
 | |
|         // shadow a name excepted from mangling.
 | |
|         if (options.except.indexOf(m) >= 0) continue;
 | |
| 
 | |
|         // we must ensure that the mangled name does not shadow a name
 | |
|         // from some parent scope that is referenced in this or in
 | |
|         // inner scopes.
 | |
|         for (var i = ext.length; --i >= 0;) {
 | |
|             var sym = ext[i];
 | |
|             var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
 | |
|             if (m == name) continue out;
 | |
|         }
 | |
|         return m;
 | |
|     }
 | |
| });
 | |
| 
 | |
| AST_Function.DEFMETHOD("next_mangled", function(options, def){
 | |
|     // #179, #326
 | |
|     // in Safari strict mode, something like (function x(x){...}) is a syntax error;
 | |
|     // a function expression's argument cannot shadow the function expression's name
 | |
| 
 | |
|     var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
 | |
|     while (true) {
 | |
|         var name = AST_Lambda.prototype.next_mangled.call(this, options, def);
 | |
|         if (!(tricky_def && tricky_def.mangled_name == name))
 | |
|             return name;
 | |
|     }
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("references", function(sym){
 | |
|     if (sym instanceof AST_Symbol) sym = sym.definition();
 | |
|     return this.enclosed.indexOf(sym) < 0 ? null : sym;
 | |
| });
 | |
| 
 | |
| AST_Symbol.DEFMETHOD("unmangleable", function(options){
 | |
|     return this.definition().unmangleable(options);
 | |
| });
 | |
| 
 | |
| // property accessors are not mangleable
 | |
| AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){
 | |
|     return true;
 | |
| });
 | |
| 
 | |
| // labels are always mangleable
 | |
| AST_Label.DEFMETHOD("unmangleable", function(){
 | |
|     return false;
 | |
| });
 | |
| 
 | |
| AST_Symbol.DEFMETHOD("unreferenced", function(){
 | |
|     return this.definition().references.length == 0
 | |
|         && !(this.scope.uses_eval || this.scope.uses_with);
 | |
| });
 | |
| 
 | |
| AST_Symbol.DEFMETHOD("undeclared", function(){
 | |
|     return this.definition().undeclared;
 | |
| });
 | |
| 
 | |
| AST_LabelRef.DEFMETHOD("undeclared", function(){
 | |
|     return false;
 | |
| });
 | |
| 
 | |
| AST_Label.DEFMETHOD("undeclared", function(){
 | |
|     return false;
 | |
| });
 | |
| 
 | |
| AST_Symbol.DEFMETHOD("definition", function(){
 | |
|     return this.thedef;
 | |
| });
 | |
| 
 | |
| AST_Symbol.DEFMETHOD("global", function(){
 | |
|     return this.definition().global;
 | |
| });
 | |
| 
 | |
| AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
 | |
|     return defaults(options, {
 | |
|         except   : [],
 | |
|         eval     : false,
 | |
|         sort     : false,
 | |
|         toplevel : false,
 | |
|         screw_ie8 : false
 | |
|     });
 | |
| });
 | |
| 
 | |
| AST_Toplevel.DEFMETHOD("mangle_names", function(options){
 | |
|     options = this._default_mangler_options(options);
 | |
|     // We only need to mangle declaration nodes.  Special logic wired
 | |
|     // into the code generator will display the mangled name if it's
 | |
|     // present (and for AST_SymbolRef-s it'll use the mangled name of
 | |
|     // the AST_SymbolDeclaration that it points to).
 | |
|     var lname = -1;
 | |
|     var to_mangle = [];
 | |
|     var tw = new TreeWalker(function(node, descend){
 | |
|         if (node instanceof AST_LabeledStatement) {
 | |
|             // lname is incremented when we get to the AST_Label
 | |
|             var save_nesting = lname;
 | |
|             descend();
 | |
|             lname = save_nesting;
 | |
|             return true;        // don't descend again in TreeWalker
 | |
|         }
 | |
|         if (node instanceof AST_Scope) {
 | |
|             var p = tw.parent(), a = [];
 | |
|             node.variables.each(function(symbol){
 | |
|                 if (options.except.indexOf(symbol.name) < 0) {
 | |
|                     a.push(symbol);
 | |
|                 }
 | |
|             });
 | |
|             if (options.sort) a.sort(function(a, b){
 | |
|                 return b.references.length - a.references.length;
 | |
|             });
 | |
|             to_mangle.push.apply(to_mangle, a);
 | |
|             return;
 | |
|         }
 | |
|         if (node instanceof AST_Label) {
 | |
|             var name;
 | |
|             do name = base54(++lname); while (!is_identifier(name));
 | |
|             node.mangled_name = name;
 | |
|             return true;
 | |
|         }
 | |
|     });
 | |
|     this.walk(tw);
 | |
|     to_mangle.forEach(function(def){ def.mangle(options) });
 | |
| });
 | |
| 
 | |
| AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
 | |
|     options = this._default_mangler_options(options);
 | |
|     var tw = new TreeWalker(function(node){
 | |
|         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) {
 | |
|             base54.consider("if");
 | |
|             if (node.alternative) base54.consider("else");
 | |
|         }
 | |
|         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);
 | |
|     });
 | |
|     this.walk(tw);
 | |
|     base54.sort();
 | |
| });
 | |
| 
 | |
| var base54 = (function() {
 | |
|     var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
 | |
|     var chars, frequency;
 | |
|     function reset() {
 | |
|         frequency = Object.create(null);
 | |
|         chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });
 | |
|         chars.forEach(function(ch){ frequency[ch] = 0 });
 | |
|     }
 | |
|     base54.consider = function(str){
 | |
|         for (var i = str.length; --i >= 0;) {
 | |
|             var code = str.charCodeAt(i);
 | |
|             if (code in frequency) ++frequency[code];
 | |
|         }
 | |
|     };
 | |
|     base54.sort = function() {
 | |
|         chars = mergeSort(chars, function(a, b){
 | |
|             if (is_digit(a) && !is_digit(b)) return 1;
 | |
|             if (is_digit(b) && !is_digit(a)) return -1;
 | |
|             return frequency[b] - frequency[a];
 | |
|         });
 | |
|     };
 | |
|     base54.reset = reset;
 | |
|     reset();
 | |
|     base54.get = function(){ return chars };
 | |
|     base54.freq = function(){ return frequency };
 | |
|     function base54(num) {
 | |
|         var ret = "", base = 54;
 | |
|         do {
 | |
|             ret += String.fromCharCode(chars[num % base]);
 | |
|             num = Math.floor(num / base);
 | |
|             base = 64;
 | |
|         } while (num > 0);
 | |
|         return ret;
 | |
|     };
 | |
|     return base54;
 | |
| })();
 | |
| 
 | |
| AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
 | |
|     options = defaults(options, {
 | |
|         undeclared       : false, // this makes a lot of noise
 | |
|         unreferenced     : true,
 | |
|         assign_to_global : true,
 | |
|         func_arguments   : true,
 | |
|         nested_defuns    : true,
 | |
|         eval             : true
 | |
|     });
 | |
|     var tw = new TreeWalker(function(node){
 | |
|         if (options.undeclared
 | |
|             && node instanceof AST_SymbolRef
 | |
|             && node.undeclared())
 | |
|         {
 | |
|             // XXX: this also warns about JS standard names,
 | |
|             // i.e. Object, Array, parseInt etc.  Should add a list of
 | |
|             // exceptions.
 | |
|             AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {
 | |
|                 name: node.name,
 | |
|                 file: node.start.file,
 | |
|                 line: node.start.line,
 | |
|                 col: node.start.col
 | |
|             });
 | |
|         }
 | |
|         if (options.assign_to_global)
 | |
|         {
 | |
|             var sym = null;
 | |
|             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;
 | |
|             if (sym
 | |
|                 && (sym.undeclared()
 | |
|                     || (sym.global() && sym.scope !== sym.definition().scope))) {
 | |
|                 AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {
 | |
|                     msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
 | |
|                     name: sym.name,
 | |
|                     file: sym.start.file,
 | |
|                     line: sym.start.line,
 | |
|                     col: sym.start.col
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|         if (options.eval
 | |
|             && node instanceof AST_SymbolRef
 | |
|             && node.undeclared()
 | |
|             && node.name == "eval") {
 | |
|             AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);
 | |
|         }
 | |
|         if (options.unreferenced
 | |
|             && (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)
 | |
|             && node.unreferenced()) {
 | |
|             AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
 | |
|                 type: node instanceof AST_Label ? "Label" : "Symbol",
 | |
|                 name: node.name,
 | |
|                 file: node.start.file,
 | |
|                 line: node.start.line,
 | |
|                 col: node.start.col
 | |
|             });
 | |
|         }
 | |
|         if (options.func_arguments
 | |
|             && node instanceof AST_Lambda
 | |
|             && node.uses_arguments) {
 | |
|             AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {
 | |
|                 name: node.name ? node.name.name : "anonymous",
 | |
|                 file: node.start.file,
 | |
|                 line: node.start.line,
 | |
|                 col: node.start.col
 | |
|             });
 | |
|         }
 | |
|         if (options.nested_defuns
 | |
|             && node instanceof AST_Defun
 | |
|             && !(tw.parent() instanceof AST_Scope)) {
 | |
|             AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {
 | |
|                 name: node.name.name,
 | |
|                 type: tw.parent().TYPE,
 | |
|                 file: node.start.file,
 | |
|                 line: node.start.line,
 | |
|                 col: node.start.col
 | |
|             });
 | |
|         }
 | |
|     });
 | |
|     this.walk(tw);
 | |
| });
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| function OutputStream(options) {
 | |
| 
 | |
|     options = defaults(options, {
 | |
|         indent_start  : 0,
 | |
|         indent_level  : 4,
 | |
|         quote_keys    : false,
 | |
|         space_colon   : true,
 | |
|         ascii_only    : false,
 | |
|         inline_script : false,
 | |
|         width         : 80,
 | |
|         max_line_len  : 32000,
 | |
|         beautify      : false,
 | |
|         source_map    : null,
 | |
|         bracketize    : false,
 | |
|         semicolons    : true,
 | |
|         comments      : false,
 | |
|         preserve_line : false,
 | |
|         screw_ie8     : false,
 | |
|         preamble      : null,
 | |
|     }, true);
 | |
| 
 | |
|     var indentation = 0;
 | |
|     var current_col = 0;
 | |
|     var current_line = 1;
 | |
|     var current_pos = 0;
 | |
|     var OUTPUT = "";
 | |
| 
 | |
|     function to_ascii(str, identifier) {
 | |
|         return str.replace(/[\u0080-\uffff]/g, function(ch) {
 | |
|             var code = ch.charCodeAt(0).toString(16);
 | |
|             if (code.length <= 2 && !identifier) {
 | |
|                 while (code.length < 2) code = "0" + code;
 | |
|                 return "\\x" + code;
 | |
|             } else {
 | |
|                 while (code.length < 4) code = "0" + code;
 | |
|                 return "\\u" + code;
 | |
|             }
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function make_string(str) {
 | |
|         var dq = 0, sq = 0;
 | |
|         str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){
 | |
|             switch (s) {
 | |
|               case "\\": return "\\\\";
 | |
|               case "\b": return "\\b";
 | |
|               case "\f": return "\\f";
 | |
|               case "\n": return "\\n";
 | |
|               case "\r": return "\\r";
 | |
|               case "\u2028": return "\\u2028";
 | |
|               case "\u2029": return "\\u2029";
 | |
|               case '"': ++dq; return '"';
 | |
|               case "'": ++sq; return "'";
 | |
|               case "\0": return "\\x00";
 | |
|             }
 | |
|             return s;
 | |
|         });
 | |
|         if (options.ascii_only) str = to_ascii(str);
 | |
|         if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'";
 | |
|         else return '"' + str.replace(/\x22/g, '\\"') + '"';
 | |
|     };
 | |
| 
 | |
|     function encode_string(str) {
 | |
|         var ret = make_string(str);
 | |
|         if (options.inline_script)
 | |
|             ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     function make_name(name) {
 | |
|         name = name.toString();
 | |
|         if (options.ascii_only)
 | |
|             name = to_ascii(name, true);
 | |
|         return name;
 | |
|     };
 | |
| 
 | |
|     function make_indent(back) {
 | |
|         return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
 | |
|     };
 | |
| 
 | |
|     /* -----[ beautification/minification ]----- */
 | |
| 
 | |
|     var might_need_space = false;
 | |
|     var might_need_semicolon = false;
 | |
|     var last = null;
 | |
| 
 | |
|     function last_char() {
 | |
|         return last.charAt(last.length - 1);
 | |
|     };
 | |
| 
 | |
|     function maybe_newline() {
 | |
|         if (options.max_line_len && current_col > options.max_line_len)
 | |
|             print("\n");
 | |
|     };
 | |
| 
 | |
|     var requireSemicolonChars = makePredicate("( [ + * / - , .");
 | |
| 
 | |
|     function print(str) {
 | |
|         str = String(str);
 | |
|         var ch = str.charAt(0);
 | |
|         if (might_need_semicolon) {
 | |
|             if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
 | |
|                 if (options.semicolons || requireSemicolonChars(ch)) {
 | |
|                     OUTPUT += ";";
 | |
|                     current_col++;
 | |
|                     current_pos++;
 | |
|                 } else {
 | |
|                     OUTPUT += "\n";
 | |
|                     current_pos++;
 | |
|                     current_line++;
 | |
|                     current_col = 0;
 | |
|                 }
 | |
|                 if (!options.beautify)
 | |
|                     might_need_space = false;
 | |
|             }
 | |
|             might_need_semicolon = false;
 | |
|             maybe_newline();
 | |
|         }
 | |
| 
 | |
|         if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
 | |
|             var target_line = stack[stack.length - 1].start.line;
 | |
|             while (current_line < target_line) {
 | |
|                 OUTPUT += "\n";
 | |
|                 current_pos++;
 | |
|                 current_line++;
 | |
|                 current_col = 0;
 | |
|                 might_need_space = false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (might_need_space) {
 | |
|             var prev = last_char();
 | |
|             if ((is_identifier_char(prev)
 | |
|                  && (is_identifier_char(ch) || ch == "\\"))
 | |
|                 || (/^[\+\-\/]$/.test(ch) && ch == prev))
 | |
|             {
 | |
|                 OUTPUT += " ";
 | |
|                 current_col++;
 | |
|                 current_pos++;
 | |
|             }
 | |
|             might_need_space = false;
 | |
|         }
 | |
|         var a = str.split(/\r?\n/), n = a.length - 1;
 | |
|         current_line += n;
 | |
|         if (n == 0) {
 | |
|             current_col += a[n].length;
 | |
|         } else {
 | |
|             current_col = a[n].length;
 | |
|         }
 | |
|         current_pos += str.length;
 | |
|         last = str;
 | |
|         OUTPUT += str;
 | |
|     };
 | |
| 
 | |
|     var space = options.beautify ? function() {
 | |
|         print(" ");
 | |
|     } : function() {
 | |
|         might_need_space = true;
 | |
|     };
 | |
| 
 | |
|     var indent = options.beautify ? function(half) {
 | |
|         if (options.beautify) {
 | |
|             print(make_indent(half ? 0.5 : 0));
 | |
|         }
 | |
|     } : noop;
 | |
| 
 | |
|     var with_indent = options.beautify ? function(col, cont) {
 | |
|         if (col === true) col = next_indent();
 | |
|         var save_indentation = indentation;
 | |
|         indentation = col;
 | |
|         var ret = cont();
 | |
|         indentation = save_indentation;
 | |
|         return ret;
 | |
|     } : function(col, cont) { return cont() };
 | |
| 
 | |
|     var newline = options.beautify ? function() {
 | |
|         print("\n");
 | |
|     } : noop;
 | |
| 
 | |
|     var semicolon = options.beautify ? function() {
 | |
|         print(";");
 | |
|     } : function() {
 | |
|         might_need_semicolon = true;
 | |
|     };
 | |
| 
 | |
|     function force_semicolon() {
 | |
|         might_need_semicolon = false;
 | |
|         print(";");
 | |
|     };
 | |
| 
 | |
|     function next_indent() {
 | |
|         return indentation + options.indent_level;
 | |
|     };
 | |
| 
 | |
|     function with_block(cont) {
 | |
|         var ret;
 | |
|         print("{");
 | |
|         newline();
 | |
|         with_indent(next_indent(), function(){
 | |
|             ret = cont();
 | |
|         });
 | |
|         indent();
 | |
|         print("}");
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     function with_parens(cont) {
 | |
|         print("(");
 | |
|         //XXX: still nice to have that for argument lists
 | |
|         //var ret = with_indent(current_col, cont);
 | |
|         var ret = cont();
 | |
|         print(")");
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     function with_square(cont) {
 | |
|         print("[");
 | |
|         //var ret = with_indent(current_col, cont);
 | |
|         var ret = cont();
 | |
|         print("]");
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     function comma() {
 | |
|         print(",");
 | |
|         space();
 | |
|     };
 | |
| 
 | |
|     function colon() {
 | |
|         print(":");
 | |
|         if (options.space_colon) space();
 | |
|     };
 | |
| 
 | |
|     var add_mapping = options.source_map ? function(token, name) {
 | |
|         try {
 | |
|             if (token) options.source_map.add(
 | |
|                 token.file || "?",
 | |
|                 current_line, current_col,
 | |
|                 token.line, token.col,
 | |
|                 (!name && token.type == "name") ? token.value : name
 | |
|             );
 | |
|         } catch(ex) {
 | |
|             AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
 | |
|                 file: token.file,
 | |
|                 line: token.line,
 | |
|                 col: token.col,
 | |
|                 cline: current_line,
 | |
|                 ccol: current_col,
 | |
|                 name: name || ""
 | |
|             })
 | |
|         }
 | |
|     } : noop;
 | |
| 
 | |
|     function get() {
 | |
|         return OUTPUT;
 | |
|     };
 | |
| 
 | |
|     if (options.preamble) {
 | |
|         print(options.preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
 | |
|     }
 | |
| 
 | |
|     var stack = [];
 | |
|     return {
 | |
|         get             : get,
 | |
|         toString        : get,
 | |
|         indent          : indent,
 | |
|         indentation     : function() { return indentation },
 | |
|         current_width   : function() { return current_col - indentation },
 | |
|         should_break    : function() { return options.width && this.current_width() >= options.width },
 | |
|         newline         : newline,
 | |
|         print           : print,
 | |
|         space           : space,
 | |
|         comma           : comma,
 | |
|         colon           : colon,
 | |
|         last            : function() { return last },
 | |
|         semicolon       : semicolon,
 | |
|         force_semicolon : force_semicolon,
 | |
|         to_ascii        : to_ascii,
 | |
|         print_name      : function(name) { print(make_name(name)) },
 | |
|         print_string    : function(str) { print(encode_string(str)) },
 | |
|         next_indent     : next_indent,
 | |
|         with_indent     : with_indent,
 | |
|         with_block      : with_block,
 | |
|         with_parens     : with_parens,
 | |
|         with_square     : with_square,
 | |
|         add_mapping     : add_mapping,
 | |
|         option          : function(opt) { return options[opt] },
 | |
|         line            : function() { return current_line },
 | |
|         col             : function() { return current_col },
 | |
|         pos             : function() { return current_pos },
 | |
|         push_node       : function(node) { stack.push(node) },
 | |
|         pop_node        : function() { return stack.pop() },
 | |
|         stack           : function() { return stack },
 | |
|         parent          : function(n) {
 | |
|             return stack[stack.length - 2 - (n || 0)];
 | |
|         }
 | |
|     };
 | |
| 
 | |
| };
 | |
| 
 | |
| /* -----[ code generators ]----- */
 | |
| 
 | |
| (function(){
 | |
| 
 | |
|     /* -----[ utils ]----- */
 | |
| 
 | |
|     function DEFPRINT(nodetype, generator) {
 | |
|         nodetype.DEFMETHOD("_codegen", generator);
 | |
|     };
 | |
| 
 | |
|     AST_Node.DEFMETHOD("print", function(stream, force_parens){
 | |
|         var self = this, generator = self._codegen;
 | |
|         function doit() {
 | |
|             self.add_comments(stream);
 | |
|             self.add_source_map(stream);
 | |
|             generator(self, stream);
 | |
|         }
 | |
|         stream.push_node(self);
 | |
|         if (force_parens || self.needs_parens(stream)) {
 | |
|             stream.with_parens(doit);
 | |
|         } else {
 | |
|             doit();
 | |
|         }
 | |
|         stream.pop_node();
 | |
|     });
 | |
| 
 | |
|     AST_Node.DEFMETHOD("print_to_string", function(options){
 | |
|         var s = OutputStream(options);
 | |
|         this.print(s);
 | |
|         return s.get();
 | |
|     });
 | |
| 
 | |
|     /* -----[ comments ]----- */
 | |
| 
 | |
|     AST_Node.DEFMETHOD("add_comments", function(output){
 | |
|         var c = output.option("comments"), self = this;
 | |
|         if (c) {
 | |
|             var start = self.start;
 | |
|             if (start && !start._comments_dumped) {
 | |
|                 start._comments_dumped = true;
 | |
|                 var comments = start.comments_before || [];
 | |
| 
 | |
|                 // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
 | |
|                 //      if this node is `return` or `throw`, we cannot allow comments before
 | |
|                 //      the returned or thrown value.
 | |
|                 if (self instanceof AST_Exit && self.value
 | |
|                     && self.value.start.comments_before
 | |
|                     && self.value.start.comments_before.length > 0) {
 | |
|                     comments = comments.concat(self.value.start.comments_before);
 | |
|                     self.value.start.comments_before = [];
 | |
|                 }
 | |
| 
 | |
|                 if (c.test) {
 | |
|                     comments = comments.filter(function(comment){
 | |
|                         return c.test(comment.value);
 | |
|                     });
 | |
|                 } else if (typeof c == "function") {
 | |
|                     comments = comments.filter(function(comment){
 | |
|                         return c(self, comment);
 | |
|                     });
 | |
|                 }
 | |
|                 comments.forEach(function(c){
 | |
|                     if (/comment[134]/.test(c.type)) {
 | |
|                         output.print("//" + c.value + "\n");
 | |
|                         output.indent();
 | |
|                     }
 | |
|                     else if (c.type == "comment2") {
 | |
|                         output.print("/*" + c.value + "*/");
 | |
|                         if (start.nlb) {
 | |
|                             output.print("\n");
 | |
|                             output.indent();
 | |
|                         } else {
 | |
|                             output.space();
 | |
|                         }
 | |
|                     }
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     /* -----[ PARENTHESES ]----- */
 | |
| 
 | |
|     function PARENS(nodetype, func) {
 | |
|         nodetype.DEFMETHOD("needs_parens", func);
 | |
|     };
 | |
| 
 | |
|     PARENS(AST_Node, function(){
 | |
|         return false;
 | |
|     });
 | |
| 
 | |
|     // a function expression needs parens around it when it's provably
 | |
|     // the first token to appear in a statement.
 | |
|     PARENS(AST_Function, function(output){
 | |
|         return first_in_statement(output);
 | |
|     });
 | |
| 
 | |
|     // same goes for an object literal, because otherwise it would be
 | |
|     // interpreted as a block of code.
 | |
|     PARENS(AST_Object, function(output){
 | |
|         return first_in_statement(output);
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_Unary, function(output){
 | |
|         var p = output.parent();
 | |
|         return p instanceof AST_PropAccess && p.expression === this;
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_Seq, function(output){
 | |
|         var p = output.parent();
 | |
|         return p instanceof AST_Call             // (foo, bar)() or foo(1, (2, 3), 4)
 | |
|             || p instanceof AST_Unary            // !(foo, bar, baz)
 | |
|             || p instanceof AST_Binary           // 1 + (2, 3) + 4 ==> 8
 | |
|             || p instanceof AST_VarDef           // var a = (1, 2), b = a + a; ==> b == 4
 | |
|             || p instanceof AST_Dot              // (1, {foo:2}).foo ==> 2
 | |
|             || p instanceof AST_Array            // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
 | |
|             || p instanceof AST_ObjectProperty   // { foo: (1, 2) }.foo ==> 2
 | |
|             || p instanceof AST_Conditional      /* (false, true) ? (a = 10, b = 20) : (c = 30)
 | |
|                                                   * ==> 20 (side effect, set a := 10 and b := 20) */
 | |
|         ;
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_Binary, function(output){
 | |
|         var p = output.parent();
 | |
|         // (foo && bar)()
 | |
|         if (p instanceof AST_Call && p.expression === this)
 | |
|             return true;
 | |
|         // typeof (foo && bar)
 | |
|         if (p instanceof AST_Unary)
 | |
|             return true;
 | |
|         // (foo && bar)["prop"], (foo && bar).prop
 | |
|         if (p instanceof AST_PropAccess && p.expression === this)
 | |
|             return true;
 | |
|         // this deals with precedence: 3 * (2 + 1)
 | |
|         if (p instanceof AST_Binary) {
 | |
|             var po = p.operator, pp = PRECEDENCE[po];
 | |
|             var so = this.operator, sp = PRECEDENCE[so];
 | |
|             if (pp > sp
 | |
|                 || (pp == sp
 | |
|                     && this === p.right)) {
 | |
|                 return true;
 | |
|             }
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_PropAccess, function(output){
 | |
|         var p = output.parent();
 | |
|         if (p instanceof AST_New && p.expression === this) {
 | |
|             // i.e. new (foo.bar().baz)
 | |
|             //
 | |
|             // if there's one call into this subtree, then we need
 | |
|             // parens around it too, otherwise the call will be
 | |
|             // interpreted as passing the arguments to the upper New
 | |
|             // expression.
 | |
|             try {
 | |
|                 this.walk(new TreeWalker(function(node){
 | |
|                     if (node instanceof AST_Call) throw p;
 | |
|                 }));
 | |
|             } catch(ex) {
 | |
|                 if (ex !== p) throw ex;
 | |
|                 return true;
 | |
|             }
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_Call, function(output){
 | |
|         var p = output.parent(), p1;
 | |
|         if (p instanceof AST_New && p.expression === this)
 | |
|             return true;
 | |
| 
 | |
|         // workaround for Safari bug.
 | |
|         // https://bugs.webkit.org/show_bug.cgi?id=123506
 | |
|         return this.expression instanceof AST_Function
 | |
|             && p instanceof AST_PropAccess
 | |
|             && p.expression === this
 | |
|             && (p1 = output.parent(1)) instanceof AST_Assign
 | |
|             && p1.left === p;
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_New, function(output){
 | |
|         var p = output.parent();
 | |
|         if (no_constructor_parens(this, output)
 | |
|             && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
 | |
|                 || p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
 | |
|             return true;
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_Number, function(output){
 | |
|         var p = output.parent();
 | |
|         if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this)
 | |
|             return true;
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_NaN, function(output){
 | |
|         var p = output.parent();
 | |
|         if (p instanceof AST_PropAccess && p.expression === this)
 | |
|             return true;
 | |
|     });
 | |
| 
 | |
|     function assign_and_conditional_paren_rules(output) {
 | |
|         var p = output.parent();
 | |
|         // !(a = false) → true
 | |
|         if (p instanceof AST_Unary)
 | |
|             return true;
 | |
|         // 1 + (a = 2) + 3 → 6, side effect setting a = 2
 | |
|         if (p instanceof AST_Binary && !(p instanceof AST_Assign))
 | |
|             return true;
 | |
|         // (a = func)() —or— new (a = Object)()
 | |
|         if (p instanceof AST_Call && p.expression === this)
 | |
|             return true;
 | |
|         // (a = foo) ? bar : baz
 | |
|         if (p instanceof AST_Conditional && p.condition === this)
 | |
|             return true;
 | |
|         // (a = foo)["prop"] —or— (a = foo).prop
 | |
|         if (p instanceof AST_PropAccess && p.expression === this)
 | |
|             return true;
 | |
|     };
 | |
| 
 | |
|     PARENS(AST_Assign, assign_and_conditional_paren_rules);
 | |
|     PARENS(AST_Conditional, assign_and_conditional_paren_rules);
 | |
| 
 | |
|     /* -----[ PRINTERS ]----- */
 | |
| 
 | |
|     DEFPRINT(AST_Directive, function(self, output){
 | |
|         output.print_string(self.value);
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Debugger, function(self, output){
 | |
|         output.print("debugger");
 | |
|         output.semicolon();
 | |
|     });
 | |
| 
 | |
|     /* -----[ statements ]----- */
 | |
| 
 | |
|     function display_body(body, is_toplevel, output) {
 | |
|         var last = body.length - 1;
 | |
|         body.forEach(function(stmt, i){
 | |
|             if (!(stmt instanceof AST_EmptyStatement)) {
 | |
|                 output.indent();
 | |
|                 stmt.print(output);
 | |
|                 if (!(i == last && is_toplevel)) {
 | |
|                     output.newline();
 | |
|                     if (is_toplevel) output.newline();
 | |
|                 }
 | |
|             }
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){
 | |
|         force_statement(this.body, output);
 | |
|     });
 | |
| 
 | |
|     DEFPRINT(AST_Statement, function(self, output){
 | |
|         self.body.print(output);
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Toplevel, function(self, output){
 | |
|         display_body(self.body, true, output);
 | |
|         output.print("");
 | |
|     });
 | |
|     DEFPRINT(AST_LabeledStatement, function(self, output){
 | |
|         self.label.print(output);
 | |
|         output.colon();
 | |
|         self.body.print(output);
 | |
|     });
 | |
|     DEFPRINT(AST_SimpleStatement, function(self, output){
 | |
|         self.body.print(output);
 | |
|         output.semicolon();
 | |
|     });
 | |
|     function print_bracketed(body, output) {
 | |
|         if (body.length > 0) output.with_block(function(){
 | |
|             display_body(body, false, output);
 | |
|         });
 | |
|         else output.print("{}");
 | |
|     };
 | |
|     DEFPRINT(AST_BlockStatement, function(self, output){
 | |
|         print_bracketed(self.body, output);
 | |
|     });
 | |
|     DEFPRINT(AST_EmptyStatement, function(self, output){
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Do, function(self, output){
 | |
|         output.print("do");
 | |
|         output.space();
 | |
|         self._do_print_body(output);
 | |
|         output.space();
 | |
|         output.print("while");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.condition.print(output);
 | |
|         });
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_While, function(self, output){
 | |
|         output.print("while");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.condition.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
|     DEFPRINT(AST_For, function(self, output){
 | |
|         output.print("for");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             if (self.init) {
 | |
|                 if (self.init instanceof AST_Definitions) {
 | |
|                     self.init.print(output);
 | |
|                 } else {
 | |
|                     parenthesize_for_noin(self.init, output, true);
 | |
|                 }
 | |
|                 output.print(";");
 | |
|                 output.space();
 | |
|             } else {
 | |
|                 output.print(";");
 | |
|             }
 | |
|             if (self.condition) {
 | |
|                 self.condition.print(output);
 | |
|                 output.print(";");
 | |
|                 output.space();
 | |
|             } else {
 | |
|                 output.print(";");
 | |
|             }
 | |
|             if (self.step) {
 | |
|                 self.step.print(output);
 | |
|             }
 | |
|         });
 | |
|         output.space();
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
|     DEFPRINT(AST_ForIn, function(self, output){
 | |
|         output.print("for");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.init.print(output);
 | |
|             output.space();
 | |
|             output.print("in");
 | |
|             output.space();
 | |
|             self.object.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
|     DEFPRINT(AST_With, function(self, output){
 | |
|         output.print("with");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.expression.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
| 
 | |
|     /* -----[ functions ]----- */
 | |
|     AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
 | |
|         var self = this;
 | |
|         if (!nokeyword) {
 | |
|             output.print("function");
 | |
|         }
 | |
|         if (self.name) {
 | |
|             output.space();
 | |
|             self.name.print(output);
 | |
|         }
 | |
|         output.with_parens(function(){
 | |
|             self.argnames.forEach(function(arg, i){
 | |
|                 if (i) output.comma();
 | |
|                 arg.print(output);
 | |
|             });
 | |
|         });
 | |
|         output.space();
 | |
|         print_bracketed(self.body, output);
 | |
|     });
 | |
|     DEFPRINT(AST_Lambda, function(self, output){
 | |
|         self._do_print(output);
 | |
|     });
 | |
| 
 | |
|     /* -----[ exits ]----- */
 | |
|     AST_Exit.DEFMETHOD("_do_print", function(output, kind){
 | |
|         output.print(kind);
 | |
|         if (this.value) {
 | |
|             output.space();
 | |
|             this.value.print(output);
 | |
|         }
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Return, function(self, output){
 | |
|         self._do_print(output, "return");
 | |
|     });
 | |
|     DEFPRINT(AST_Throw, function(self, output){
 | |
|         self._do_print(output, "throw");
 | |
|     });
 | |
| 
 | |
|     /* -----[ loop control ]----- */
 | |
|     AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
 | |
|         output.print(kind);
 | |
|         if (this.label) {
 | |
|             output.space();
 | |
|             this.label.print(output);
 | |
|         }
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Break, function(self, output){
 | |
|         self._do_print(output, "break");
 | |
|     });
 | |
|     DEFPRINT(AST_Continue, function(self, output){
 | |
|         self._do_print(output, "continue");
 | |
|     });
 | |
| 
 | |
|     /* -----[ if ]----- */
 | |
|     function make_then(self, output) {
 | |
|         if (output.option("bracketize")) {
 | |
|             make_block(self.body, output);
 | |
|             return;
 | |
|         }
 | |
|         // The squeezer replaces "block"-s that contain only a single
 | |
|         // statement with the statement itself; technically, the AST
 | |
|         // is correct, but this can create problems when we output an
 | |
|         // IF having an ELSE clause where the THEN clause ends in an
 | |
|         // IF *without* an ELSE block (then the outer ELSE would refer
 | |
|         // to the inner IF).  This function checks for this case and
 | |
|         // adds the block brackets if needed.
 | |
|         if (!self.body)
 | |
|             return output.force_semicolon();
 | |
|         if (self.body instanceof AST_Do
 | |
|             && !output.option("screw_ie8")) {
 | |
|             // https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
 | |
|             // croaks with "syntax error" on code like this: if (foo)
 | |
|             // do ... while(cond); else ...  we need block brackets
 | |
|             // around do/while
 | |
|             make_block(self.body, output);
 | |
|             return;
 | |
|         }
 | |
|         var b = self.body;
 | |
|         while (true) {
 | |
|             if (b instanceof AST_If) {
 | |
|                 if (!b.alternative) {
 | |
|                     make_block(self.body, output);
 | |
|                     return;
 | |
|                 }
 | |
|                 b = b.alternative;
 | |
|             }
 | |
|             else if (b instanceof AST_StatementWithBody) {
 | |
|                 b = b.body;
 | |
|             }
 | |
|             else break;
 | |
|         }
 | |
|         force_statement(self.body, output);
 | |
|     };
 | |
|     DEFPRINT(AST_If, function(self, output){
 | |
|         output.print("if");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.condition.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         if (self.alternative) {
 | |
|             make_then(self, output);
 | |
|             output.space();
 | |
|             output.print("else");
 | |
|             output.space();
 | |
|             force_statement(self.alternative, output);
 | |
|         } else {
 | |
|             self._do_print_body(output);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     /* -----[ switch ]----- */
 | |
|     DEFPRINT(AST_Switch, function(self, output){
 | |
|         output.print("switch");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.expression.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         if (self.body.length > 0) output.with_block(function(){
 | |
|             self.body.forEach(function(stmt, i){
 | |
|                 if (i) output.newline();
 | |
|                 output.indent(true);
 | |
|                 stmt.print(output);
 | |
|             });
 | |
|         });
 | |
|         else output.print("{}");
 | |
|     });
 | |
|     AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){
 | |
|         if (this.body.length > 0) {
 | |
|             output.newline();
 | |
|             this.body.forEach(function(stmt){
 | |
|                 output.indent();
 | |
|                 stmt.print(output);
 | |
|                 output.newline();
 | |
|             });
 | |
|         }
 | |
|     });
 | |
|     DEFPRINT(AST_Default, function(self, output){
 | |
|         output.print("default:");
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
|     DEFPRINT(AST_Case, function(self, output){
 | |
|         output.print("case");
 | |
|         output.space();
 | |
|         self.expression.print(output);
 | |
|         output.print(":");
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
| 
 | |
|     /* -----[ exceptions ]----- */
 | |
|     DEFPRINT(AST_Try, function(self, output){
 | |
|         output.print("try");
 | |
|         output.space();
 | |
|         print_bracketed(self.body, output);
 | |
|         if (self.bcatch) {
 | |
|             output.space();
 | |
|             self.bcatch.print(output);
 | |
|         }
 | |
|         if (self.bfinally) {
 | |
|             output.space();
 | |
|             self.bfinally.print(output);
 | |
|         }
 | |
|     });
 | |
|     DEFPRINT(AST_Catch, function(self, output){
 | |
|         output.print("catch");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.argname.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         print_bracketed(self.body, output);
 | |
|     });
 | |
|     DEFPRINT(AST_Finally, function(self, output){
 | |
|         output.print("finally");
 | |
|         output.space();
 | |
|         print_bracketed(self.body, output);
 | |
|     });
 | |
| 
 | |
|     /* -----[ var/const ]----- */
 | |
|     AST_Definitions.DEFMETHOD("_do_print", function(output, kind){
 | |
|         output.print(kind);
 | |
|         output.space();
 | |
|         this.definitions.forEach(function(def, i){
 | |
|             if (i) output.comma();
 | |
|             def.print(output);
 | |
|         });
 | |
|         var p = output.parent();
 | |
|         var in_for = p instanceof AST_For || p instanceof AST_ForIn;
 | |
|         var avoid_semicolon = in_for && p.init === this;
 | |
|         if (!avoid_semicolon)
 | |
|             output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Var, function(self, output){
 | |
|         self._do_print(output, "var");
 | |
|     });
 | |
|     DEFPRINT(AST_Const, function(self, output){
 | |
|         self._do_print(output, "const");
 | |
|     });
 | |
| 
 | |
|     function parenthesize_for_noin(node, output, noin) {
 | |
|         if (!noin) node.print(output);
 | |
|         else try {
 | |
|             // need to take some precautions here:
 | |
|             //    https://github.com/mishoo/UglifyJS2/issues/60
 | |
|             node.walk(new TreeWalker(function(node){
 | |
|                 if (node instanceof AST_Binary && node.operator == "in")
 | |
|                     throw output;
 | |
|             }));
 | |
|             node.print(output);
 | |
|         } catch(ex) {
 | |
|             if (ex !== output) throw ex;
 | |
|             node.print(output, true);
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     DEFPRINT(AST_VarDef, function(self, output){
 | |
|         self.name.print(output);
 | |
|         if (self.value) {
 | |
|             output.space();
 | |
|             output.print("=");
 | |
|             output.space();
 | |
|             var p = output.parent(1);
 | |
|             var noin = p instanceof AST_For || p instanceof AST_ForIn;
 | |
|             parenthesize_for_noin(self.value, output, noin);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     /* -----[ other expressions ]----- */
 | |
|     DEFPRINT(AST_Call, function(self, output){
 | |
|         self.expression.print(output);
 | |
|         if (self instanceof AST_New && no_constructor_parens(self, output))
 | |
|             return;
 | |
|         output.with_parens(function(){
 | |
|             self.args.forEach(function(expr, i){
 | |
|                 if (i) output.comma();
 | |
|                 expr.print(output);
 | |
|             });
 | |
|         });
 | |
|     });
 | |
|     DEFPRINT(AST_New, function(self, output){
 | |
|         output.print("new");
 | |
|         output.space();
 | |
|         AST_Call.prototype._codegen(self, output);
 | |
|     });
 | |
| 
 | |
|     AST_Seq.DEFMETHOD("_do_print", function(output){
 | |
|         this.car.print(output);
 | |
|         if (this.cdr) {
 | |
|             output.comma();
 | |
|             if (output.should_break()) {
 | |
|                 output.newline();
 | |
|                 output.indent();
 | |
|             }
 | |
|             this.cdr.print(output);
 | |
|         }
 | |
|     });
 | |
|     DEFPRINT(AST_Seq, function(self, output){
 | |
|         self._do_print(output);
 | |
|         // var p = output.parent();
 | |
|         // if (p instanceof AST_Statement) {
 | |
|         //     output.with_indent(output.next_indent(), function(){
 | |
|         //         self._do_print(output);
 | |
|         //     });
 | |
|         // } else {
 | |
|         //     self._do_print(output);
 | |
|         // }
 | |
|     });
 | |
|     DEFPRINT(AST_Dot, function(self, output){
 | |
|         var expr = self.expression;
 | |
|         expr.print(output);
 | |
|         if (expr instanceof AST_Number && expr.getValue() >= 0) {
 | |
|             if (!/[xa-f.]/i.test(output.last())) {
 | |
|                 output.print(".");
 | |
|             }
 | |
|         }
 | |
|         output.print(".");
 | |
|         // the name after dot would be mapped about here.
 | |
|         output.add_mapping(self.end);
 | |
|         output.print_name(self.property);
 | |
|     });
 | |
|     DEFPRINT(AST_Sub, function(self, output){
 | |
|         self.expression.print(output);
 | |
|         output.print("[");
 | |
|         self.property.print(output);
 | |
|         output.print("]");
 | |
|     });
 | |
|     DEFPRINT(AST_UnaryPrefix, function(self, output){
 | |
|         var op = self.operator;
 | |
|         output.print(op);
 | |
|         if (/^[a-z]/i.test(op))
 | |
|             output.space();
 | |
|         self.expression.print(output);
 | |
|     });
 | |
|     DEFPRINT(AST_UnaryPostfix, function(self, output){
 | |
|         self.expression.print(output);
 | |
|         output.print(self.operator);
 | |
|     });
 | |
|     DEFPRINT(AST_Binary, function(self, output){
 | |
|         self.left.print(output);
 | |
|         output.space();
 | |
|         output.print(self.operator);
 | |
|         if (self.operator == "<"
 | |
|             && self.right instanceof AST_UnaryPrefix
 | |
|             && self.right.operator == "!"
 | |
|             && self.right.expression instanceof AST_UnaryPrefix
 | |
|             && self.right.expression.operator == "--") {
 | |
|             // space is mandatory to avoid outputting <!--
 | |
|             // http://javascript.spec.whatwg.org/#comment-syntax
 | |
|             output.print(" ");
 | |
|         } else {
 | |
|             // the space is optional depending on "beautify"
 | |
|             output.space();
 | |
|         }
 | |
|         self.right.print(output);
 | |
|     });
 | |
|     DEFPRINT(AST_Conditional, function(self, output){
 | |
|         self.condition.print(output);
 | |
|         output.space();
 | |
|         output.print("?");
 | |
|         output.space();
 | |
|         self.consequent.print(output);
 | |
|         output.space();
 | |
|         output.colon();
 | |
|         self.alternative.print(output);
 | |
|     });
 | |
| 
 | |
|     /* -----[ literals ]----- */
 | |
|     DEFPRINT(AST_Array, function(self, output){
 | |
|         output.with_square(function(){
 | |
|             var a = self.elements, len = a.length;
 | |
|             if (len > 0) output.space();
 | |
|             a.forEach(function(exp, i){
 | |
|                 if (i) output.comma();
 | |
|                 exp.print(output);
 | |
|                 // If the final element is a hole, we need to make sure it
 | |
|                 // doesn't look like a trailing comma, by inserting an actual
 | |
|                 // trailing comma.
 | |
|                 if (i === len - 1 && exp instanceof AST_Hole)
 | |
|                   output.comma();
 | |
|             });
 | |
|             if (len > 0) output.space();
 | |
|         });
 | |
|     });
 | |
|     DEFPRINT(AST_Object, function(self, output){
 | |
|         if (self.properties.length > 0) output.with_block(function(){
 | |
|             self.properties.forEach(function(prop, i){
 | |
|                 if (i) {
 | |
|                     output.print(",");
 | |
|                     output.newline();
 | |
|                 }
 | |
|                 output.indent();
 | |
|                 prop.print(output);
 | |
|             });
 | |
|             output.newline();
 | |
|         });
 | |
|         else output.print("{}");
 | |
|     });
 | |
|     DEFPRINT(AST_ObjectKeyVal, function(self, output){
 | |
|         var key = self.key;
 | |
|         if (output.option("quote_keys")) {
 | |
|             output.print_string(key + "");
 | |
|         } else if ((typeof key == "number"
 | |
|                     || !output.option("beautify")
 | |
|                     && +key + "" == key)
 | |
|                    && parseFloat(key) >= 0) {
 | |
|             output.print(make_num(key));
 | |
|         } else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
 | |
|             output.print_name(key);
 | |
|         } else {
 | |
|             output.print_string(key);
 | |
|         }
 | |
|         output.colon();
 | |
|         self.value.print(output);
 | |
|     });
 | |
|     DEFPRINT(AST_ObjectSetter, function(self, output){
 | |
|         output.print("set");
 | |
|         output.space();
 | |
|         self.key.print(output);
 | |
|         self.value._do_print(output, true);
 | |
|     });
 | |
|     DEFPRINT(AST_ObjectGetter, function(self, output){
 | |
|         output.print("get");
 | |
|         output.space();
 | |
|         self.key.print(output);
 | |
|         self.value._do_print(output, true);
 | |
|     });
 | |
|     DEFPRINT(AST_Symbol, function(self, output){
 | |
|         var def = self.definition();
 | |
|         output.print_name(def ? def.mangled_name || def.name : self.name);
 | |
|     });
 | |
|     DEFPRINT(AST_Undefined, function(self, output){
 | |
|         output.print("void 0");
 | |
|     });
 | |
|     DEFPRINT(AST_Hole, noop);
 | |
|     DEFPRINT(AST_Infinity, function(self, output){
 | |
|         output.print("1/0");
 | |
|     });
 | |
|     DEFPRINT(AST_NaN, function(self, output){
 | |
|         output.print("0/0");
 | |
|     });
 | |
|     DEFPRINT(AST_This, function(self, output){
 | |
|         output.print("this");
 | |
|     });
 | |
|     DEFPRINT(AST_Constant, function(self, output){
 | |
|         output.print(self.getValue());
 | |
|     });
 | |
|     DEFPRINT(AST_String, function(self, output){
 | |
|         output.print_string(self.getValue());
 | |
|     });
 | |
|     DEFPRINT(AST_Number, function(self, output){
 | |
|         output.print(make_num(self.getValue()));
 | |
|     });
 | |
|     DEFPRINT(AST_RegExp, function(self, output){
 | |
|         var str = self.getValue().toString();
 | |
|         if (output.option("ascii_only"))
 | |
|             str = output.to_ascii(str);
 | |
|         output.print(str);
 | |
|         var p = output.parent();
 | |
|         if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
 | |
|             output.print(" ");
 | |
|     });
 | |
| 
 | |
|     function force_statement(stat, output) {
 | |
|         if (output.option("bracketize")) {
 | |
|             if (!stat || stat instanceof AST_EmptyStatement)
 | |
|                 output.print("{}");
 | |
|             else if (stat instanceof AST_BlockStatement)
 | |
|                 stat.print(output);
 | |
|             else output.with_block(function(){
 | |
|                 output.indent();
 | |
|                 stat.print(output);
 | |
|                 output.newline();
 | |
|             });
 | |
|         } else {
 | |
|             if (!stat || stat instanceof AST_EmptyStatement)
 | |
|                 output.force_semicolon();
 | |
|             else
 | |
|                 stat.print(output);
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     // return true if the node at the top of the stack (that means the
 | |
|     // innermost node in the current output) is lexically the first in
 | |
|     // a statement.
 | |
|     function first_in_statement(output) {
 | |
|         var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
 | |
|         while (i > 0) {
 | |
|             if (p instanceof AST_Statement && p.body === node)
 | |
|                 return true;
 | |
|             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 ))
 | |
|             {
 | |
|                 node = p;
 | |
|                 p = a[--i];
 | |
|             } else {
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     // self should be AST_New.  decide if we want to show parens or not.
 | |
|     function no_constructor_parens(self, output) {
 | |
|         return self.args.length == 0 && !output.option("beautify");
 | |
|     };
 | |
| 
 | |
|     function best_of(a) {
 | |
|         var best = a[0], len = best.length;
 | |
|         for (var i = 1; i < a.length; ++i) {
 | |
|             if (a[i].length < len) {
 | |
|                 best = a[i];
 | |
|                 len = best.length;
 | |
|             }
 | |
|         }
 | |
|         return best;
 | |
|     };
 | |
| 
 | |
|     function make_num(num) {
 | |
|         var str = num.toString(10), a = [ str.replace(/^0\./, ".").replace('e+', 'e') ], m;
 | |
|         if (Math.floor(num) === num) {
 | |
|             if (num >= 0) {
 | |
|                 a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
 | |
|                        "0" + num.toString(8)); // same.
 | |
|             } else {
 | |
|                 a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
 | |
|                        "-0" + (-num).toString(8)); // same.
 | |
|             }
 | |
|             if ((m = /^(.*?)(0+)$/.exec(num))) {
 | |
|                 a.push(m[1] + "e" + m[2].length);
 | |
|             }
 | |
|         } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
 | |
|             a.push(m[2] + "e-" + (m[1].length + m[2].length),
 | |
|                    str.substr(str.indexOf(".")));
 | |
|         }
 | |
|         return best_of(a);
 | |
|     };
 | |
| 
 | |
|     function make_block(stmt, output) {
 | |
|         if (stmt instanceof AST_BlockStatement) {
 | |
|             stmt.print(output);
 | |
|             return;
 | |
|         }
 | |
|         output.with_block(function(){
 | |
|             output.indent();
 | |
|             stmt.print(output);
 | |
|             output.newline();
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     /* -----[ source map generators ]----- */
 | |
| 
 | |
|     function DEFMAP(nodetype, generator) {
 | |
|         nodetype.DEFMETHOD("add_source_map", function(stream){
 | |
|             generator(this, stream);
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     // We could easily add info for ALL nodes, but it seems to me that
 | |
|     // would be quite wasteful, hence this noop in the base class.
 | |
|     DEFMAP(AST_Node, noop);
 | |
| 
 | |
|     function basic_sourcemap_gen(self, output) {
 | |
|         output.add_mapping(self.start);
 | |
|     };
 | |
| 
 | |
|     // XXX: I'm not exactly sure if we need it for all of these nodes,
 | |
|     // or if we should add even more.
 | |
| 
 | |
|     DEFMAP(AST_Directive, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Debugger, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Symbol, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Jump, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_StatementWithBody, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_LabeledStatement, noop); // since the label symbol will mark it
 | |
|     DEFMAP(AST_Lambda, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Switch, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_SwitchBranch, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_BlockStatement, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Toplevel, noop);
 | |
|     DEFMAP(AST_New, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Try, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Catch, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Finally, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Definitions, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Constant, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_ObjectProperty, function(self, output){
 | |
|         output.add_mapping(self.start, self.key);
 | |
|     });
 | |
| 
 | |
| })();
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| function Compressor(options, false_by_default) {
 | |
|     if (!(this instanceof Compressor))
 | |
|         return new Compressor(options, false_by_default);
 | |
|     TreeTransformer.call(this, this.before, this.after);
 | |
|     this.options = defaults(options, {
 | |
|         sequences     : !false_by_default,
 | |
|         properties    : !false_by_default,
 | |
|         dead_code     : !false_by_default,
 | |
|         drop_debugger : !false_by_default,
 | |
|         unsafe        : false,
 | |
|         unsafe_comps  : false,
 | |
|         conditionals  : !false_by_default,
 | |
|         comparisons   : !false_by_default,
 | |
|         evaluate      : !false_by_default,
 | |
|         booleans      : !false_by_default,
 | |
|         loops         : !false_by_default,
 | |
|         unused        : !false_by_default,
 | |
|         hoist_funs    : !false_by_default,
 | |
|         hoist_vars    : false,
 | |
|         if_return     : !false_by_default,
 | |
|         join_vars     : !false_by_default,
 | |
|         cascade       : !false_by_default,
 | |
|         side_effects  : !false_by_default,
 | |
|         pure_getters  : false,
 | |
|         pure_funcs    : null,
 | |
|         negate_iife   : !false_by_default,
 | |
|         screw_ie8     : false,
 | |
|         drop_console  : false,
 | |
| 
 | |
|         warnings      : true,
 | |
|         global_defs   : {}
 | |
|     }, true);
 | |
| };
 | |
| 
 | |
| Compressor.prototype = new TreeTransformer;
 | |
| merge(Compressor.prototype, {
 | |
|     option: function(key) { return this.options[key] },
 | |
|     warn: function() {
 | |
|         if (this.options.warnings)
 | |
|             AST_Node.warn.apply(AST_Node, arguments);
 | |
|     },
 | |
|     before: function(node, descend, in_list) {
 | |
|         if (node._squeezed) return node;
 | |
|         var was_scope = false;
 | |
|         if (node instanceof AST_Scope) {
 | |
|             node = node.hoist_declarations(this);
 | |
|             was_scope = true;
 | |
|         }
 | |
|         descend(node, this);
 | |
|         node = node.optimize(this);
 | |
|         if (was_scope && node instanceof AST_Scope) {
 | |
|             node.drop_unused(this);
 | |
|             descend(node, this);
 | |
|         }
 | |
|         node._squeezed = true;
 | |
|         return node;
 | |
|     }
 | |
| });
 | |
| 
 | |
| (function(){
 | |
| 
 | |
|     function OPT(node, optimizer) {
 | |
|         node.DEFMETHOD("optimize", function(compressor){
 | |
|             var self = this;
 | |
|             if (self._optimized) return self;
 | |
|             var opt = optimizer(self, compressor);
 | |
|             opt._optimized = true;
 | |
|             if (opt === self) return opt;
 | |
|             return opt.transform(compressor);
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     OPT(AST_Node, function(self, compressor){
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     AST_Node.DEFMETHOD("equivalent_to", function(node){
 | |
|         // XXX: this is a rather expensive way to test two node's equivalence:
 | |
|         return this.print_to_string() == node.print_to_string();
 | |
|     });
 | |
| 
 | |
|     function make_node(ctor, orig, props) {
 | |
|         if (!props) props = {};
 | |
|         if (orig) {
 | |
|             if (!props.start) props.start = orig.start;
 | |
|             if (!props.end) props.end = orig.end;
 | |
|         }
 | |
|         return new ctor(props);
 | |
|     };
 | |
| 
 | |
|     function make_node_from_constant(compressor, val, orig) {
 | |
|         // XXX: WIP.
 | |
|         // if (val instanceof AST_Node) return val.transform(new TreeTransformer(null, function(node){
 | |
|         //     if (node instanceof AST_SymbolRef) {
 | |
|         //         var scope = compressor.find_parent(AST_Scope);
 | |
|         //         var def = scope.find_variable(node);
 | |
|         //         node.thedef = def;
 | |
|         //         return node;
 | |
|         //     }
 | |
|         // })).transform(compressor);
 | |
| 
 | |
|         if (val instanceof AST_Node) return val.transform(compressor);
 | |
|         switch (typeof val) {
 | |
|           case "string":
 | |
|             return make_node(AST_String, orig, {
 | |
|                 value: val
 | |
|             }).optimize(compressor);
 | |
|           case "number":
 | |
|             return make_node(isNaN(val) ? AST_NaN : AST_Number, orig, {
 | |
|                 value: val
 | |
|             }).optimize(compressor);
 | |
|           case "boolean":
 | |
|             return make_node(val ? AST_True : AST_False, orig).optimize(compressor);
 | |
|           case "undefined":
 | |
|             return make_node(AST_Undefined, orig).optimize(compressor);
 | |
|           default:
 | |
|             if (val === null) {
 | |
|                 return make_node(AST_Null, orig).optimize(compressor);
 | |
|             }
 | |
|             if (val instanceof RegExp) {
 | |
|                 return make_node(AST_RegExp, orig).optimize(compressor);
 | |
|             }
 | |
|             throw new Error(string_template("Can't handle constant of type: {type}", {
 | |
|                 type: typeof val
 | |
|             }));
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function as_statement_array(thing) {
 | |
|         if (thing === null) return [];
 | |
|         if (thing instanceof AST_BlockStatement) return thing.body;
 | |
|         if (thing instanceof AST_EmptyStatement) return [];
 | |
|         if (thing instanceof AST_Statement) return [ thing ];
 | |
|         throw new Error("Can't convert thing to statement array");
 | |
|     };
 | |
| 
 | |
|     function is_empty(thing) {
 | |
|         if (thing === null) return true;
 | |
|         if (thing instanceof AST_EmptyStatement) return true;
 | |
|         if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
 | |
|         return false;
 | |
|     };
 | |
| 
 | |
|     function loop_body(x) {
 | |
|         if (x instanceof AST_Switch) return x;
 | |
|         if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
 | |
|             return (x.body instanceof AST_BlockStatement ? x.body : x);
 | |
|         }
 | |
|         return x;
 | |
|     };
 | |
| 
 | |
|     function tighten_body(statements, compressor) {
 | |
|         var CHANGED;
 | |
|         do {
 | |
|             CHANGED = false;
 | |
|             statements = eliminate_spurious_blocks(statements);
 | |
|             if (compressor.option("dead_code")) {
 | |
|                 statements = eliminate_dead_code(statements, compressor);
 | |
|             }
 | |
|             if (compressor.option("if_return")) {
 | |
|                 statements = handle_if_return(statements, compressor);
 | |
|             }
 | |
|             if (compressor.option("sequences")) {
 | |
|                 statements = sequencesize(statements, compressor);
 | |
|             }
 | |
|             if (compressor.option("join_vars")) {
 | |
|                 statements = join_consecutive_vars(statements, compressor);
 | |
|             }
 | |
|         } while (CHANGED);
 | |
| 
 | |
|         if (compressor.option("negate_iife")) {
 | |
|             negate_iifes(statements, compressor);
 | |
|         }
 | |
| 
 | |
|         return statements;
 | |
| 
 | |
|         function eliminate_spurious_blocks(statements) {
 | |
|             var seen_dirs = [];
 | |
|             return statements.reduce(function(a, stat){
 | |
|                 if (stat instanceof AST_BlockStatement) {
 | |
|                     CHANGED = true;
 | |
|                     a.push.apply(a, eliminate_spurious_blocks(stat.body));
 | |
|                 } else if (stat instanceof AST_EmptyStatement) {
 | |
|                     CHANGED = true;
 | |
|                 } else if (stat instanceof AST_Directive) {
 | |
|                     if (seen_dirs.indexOf(stat.value) < 0) {
 | |
|                         a.push(stat);
 | |
|                         seen_dirs.push(stat.value);
 | |
|                     } else {
 | |
|                         CHANGED = true;
 | |
|                     }
 | |
|                 } else {
 | |
|                     a.push(stat);
 | |
|                 }
 | |
|                 return a;
 | |
|             }, []);
 | |
|         };
 | |
| 
 | |
|         function handle_if_return(statements, compressor) {
 | |
|             var self = compressor.self();
 | |
|             var in_lambda = self instanceof AST_Lambda;
 | |
|             var ret = [];
 | |
|             loop: for (var i = statements.length; --i >= 0;) {
 | |
|                 var stat = statements[i];
 | |
|                 switch (true) {
 | |
|                   case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
 | |
|                     CHANGED = true;
 | |
|                     // note, ret.length is probably always zero
 | |
|                     // because we drop unreachable code before this
 | |
|                     // step.  nevertheless, it's good to check.
 | |
|                     continue loop;
 | |
|                   case stat instanceof AST_If:
 | |
|                     if (stat.body instanceof AST_Return) {
 | |
|                         //---
 | |
|                         // pretty silly case, but:
 | |
|                         // if (foo()) return; return; ==> foo(); return;
 | |
|                         if (((in_lambda && ret.length == 0)
 | |
|                              || (ret[0] instanceof AST_Return && !ret[0].value))
 | |
|                             && !stat.body.value && !stat.alternative) {
 | |
|                             CHANGED = true;
 | |
|                             var cond = make_node(AST_SimpleStatement, stat.condition, {
 | |
|                                 body: stat.condition
 | |
|                             });
 | |
|                             ret.unshift(cond);
 | |
|                             continue loop;
 | |
|                         }
 | |
|                         //---
 | |
|                         // if (foo()) return x; return y; ==> return foo() ? x : y;
 | |
|                         if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) {
 | |
|                             CHANGED = true;
 | |
|                             stat = stat.clone();
 | |
|                             stat.alternative = ret[0];
 | |
|                             ret[0] = stat.transform(compressor);
 | |
|                             continue loop;
 | |
|                         }
 | |
|                         //---
 | |
|                         // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
 | |
|                         if ((ret.length == 0 || ret[0] instanceof AST_Return) && stat.body.value && !stat.alternative && in_lambda) {
 | |
|                             CHANGED = true;
 | |
|                             stat = stat.clone();
 | |
|                             stat.alternative = ret[0] || make_node(AST_Return, stat, {
 | |
|                                 value: make_node(AST_Undefined, stat)
 | |
|                             });
 | |
|                             ret[0] = stat.transform(compressor);
 | |
|                             continue loop;
 | |
|                         }
 | |
|                         //---
 | |
|                         // if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
 | |
|                         if (!stat.body.value && in_lambda) {
 | |
|                             CHANGED = true;
 | |
|                             stat = stat.clone();
 | |
|                             stat.condition = stat.condition.negate(compressor);
 | |
|                             stat.body = make_node(AST_BlockStatement, stat, {
 | |
|                                 body: as_statement_array(stat.alternative).concat(ret)
 | |
|                             });
 | |
|                             stat.alternative = null;
 | |
|                             ret = [ stat.transform(compressor) ];
 | |
|                             continue loop;
 | |
|                         }
 | |
|                         //---
 | |
|                         if (ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
 | |
|                             && (!stat.alternative || stat.alternative instanceof AST_SimpleStatement)) {
 | |
|                             CHANGED = true;
 | |
|                             ret.push(make_node(AST_Return, ret[0], {
 | |
|                                 value: make_node(AST_Undefined, ret[0])
 | |
|                             }).transform(compressor));
 | |
|                             ret = as_statement_array(stat.alternative).concat(ret);
 | |
|                             ret.unshift(stat);
 | |
|                             continue loop;
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     var ab = aborts(stat.body);
 | |
|                     var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
 | |
|                     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))) {
 | |
|                         if (ab.label) {
 | |
|                             remove(ab.label.thedef.references, ab);
 | |
|                         }
 | |
|                         CHANGED = true;
 | |
|                         var body = as_statement_array(stat.body).slice(0, -1);
 | |
|                         stat = stat.clone();
 | |
|                         stat.condition = stat.condition.negate(compressor);
 | |
|                         stat.body = make_node(AST_BlockStatement, stat, {
 | |
|                             body: ret
 | |
|                         });
 | |
|                         stat.alternative = make_node(AST_BlockStatement, stat, {
 | |
|                             body: body
 | |
|                         });
 | |
|                         ret = [ stat.transform(compressor) ];
 | |
|                         continue loop;
 | |
|                     }
 | |
| 
 | |
|                     var ab = aborts(stat.alternative);
 | |
|                     var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
 | |
|                     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))) {
 | |
|                         if (ab.label) {
 | |
|                             remove(ab.label.thedef.references, ab);
 | |
|                         }
 | |
|                         CHANGED = true;
 | |
|                         stat = stat.clone();
 | |
|                         stat.body = make_node(AST_BlockStatement, stat.body, {
 | |
|                             body: as_statement_array(stat.body).concat(ret)
 | |
|                         });
 | |
|                         stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
 | |
|                             body: as_statement_array(stat.alternative).slice(0, -1)
 | |
|                         });
 | |
|                         ret = [ stat.transform(compressor) ];
 | |
|                         continue loop;
 | |
|                     }
 | |
| 
 | |
|                     ret.unshift(stat);
 | |
|                     break;
 | |
|                   default:
 | |
|                     ret.unshift(stat);
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             return ret;
 | |
|         };
 | |
| 
 | |
|         function eliminate_dead_code(statements, compressor) {
 | |
|             var has_quit = false;
 | |
|             var orig = statements.length;
 | |
|             var self = compressor.self();
 | |
|             statements = statements.reduce(function(a, stat){
 | |
|                 if (has_quit) {
 | |
|                     extract_declarations_from_unreachable_code(compressor, stat, a);
 | |
|                 } else {
 | |
|                     if (stat instanceof AST_LoopControl) {
 | |
|                         var lct = compressor.loopcontrol_target(stat.label);
 | |
|                         if ((stat instanceof AST_Break
 | |
|                              && lct instanceof AST_BlockStatement
 | |
|                              && loop_body(lct) === self) || (stat instanceof AST_Continue
 | |
|                                                              && loop_body(lct) === self)) {
 | |
|                             if (stat.label) {
 | |
|                                 remove(stat.label.thedef.references, stat);
 | |
|                             }
 | |
|                         } else {
 | |
|                             a.push(stat);
 | |
|                         }
 | |
|                     } else {
 | |
|                         a.push(stat);
 | |
|                     }
 | |
|                     if (aborts(stat)) has_quit = true;
 | |
|                 }
 | |
|                 return a;
 | |
|             }, []);
 | |
|             CHANGED = statements.length != orig;
 | |
|             return statements;
 | |
|         };
 | |
| 
 | |
|         function sequencesize(statements, compressor) {
 | |
|             if (statements.length < 2) return statements;
 | |
|             var seq = [], ret = [];
 | |
|             function push_seq() {
 | |
|                 seq = AST_Seq.from_array(seq);
 | |
|                 if (seq) ret.push(make_node(AST_SimpleStatement, seq, {
 | |
|                     body: seq
 | |
|                 }));
 | |
|                 seq = [];
 | |
|             };
 | |
|             statements.forEach(function(stat){
 | |
|                 if (stat instanceof AST_SimpleStatement) seq.push(stat.body);
 | |
|                 else push_seq(), ret.push(stat);
 | |
|             });
 | |
|             push_seq();
 | |
|             ret = sequencesize_2(ret, compressor);
 | |
|             CHANGED = ret.length != statements.length;
 | |
|             return ret;
 | |
|         };
 | |
| 
 | |
|         function sequencesize_2(statements, compressor) {
 | |
|             function cons_seq(right) {
 | |
|                 ret.pop();
 | |
|                 var left = prev.body;
 | |
|                 if (left instanceof AST_Seq) {
 | |
|                     left.add(right);
 | |
|                 } else {
 | |
|                     left = AST_Seq.cons(left, right);
 | |
|                 }
 | |
|                 return left.transform(compressor);
 | |
|             };
 | |
|             var ret = [], prev = null;
 | |
|             statements.forEach(function(stat){
 | |
|                 if (prev) {
 | |
|                     if (stat instanceof AST_For) {
 | |
|                         var opera = {};
 | |
|                         try {
 | |
|                             prev.body.walk(new TreeWalker(function(node){
 | |
|                                 if (node instanceof AST_Binary && node.operator == "in")
 | |
|                                     throw opera;
 | |
|                             }));
 | |
|                             if (stat.init && !(stat.init instanceof AST_Definitions)) {
 | |
|                                 stat.init = cons_seq(stat.init);
 | |
|                             }
 | |
|                             else if (!stat.init) {
 | |
|                                 stat.init = prev.body;
 | |
|                                 ret.pop();
 | |
|                             }
 | |
|                         } catch(ex) {
 | |
|                             if (ex !== opera) throw ex;
 | |
|                         }
 | |
|                     }
 | |
|                     else if (stat instanceof AST_If) {
 | |
|                         stat.condition = cons_seq(stat.condition);
 | |
|                     }
 | |
|                     else if (stat instanceof AST_With) {
 | |
|                         stat.expression = cons_seq(stat.expression);
 | |
|                     }
 | |
|                     else if (stat instanceof AST_Exit && stat.value) {
 | |
|                         stat.value = cons_seq(stat.value);
 | |
|                     }
 | |
|                     else if (stat instanceof AST_Exit) {
 | |
|                         stat.value = cons_seq(make_node(AST_Undefined, stat));
 | |
|                     }
 | |
|                     else if (stat instanceof AST_Switch) {
 | |
|                         stat.expression = cons_seq(stat.expression);
 | |
|                     }
 | |
|                 }
 | |
|                 ret.push(stat);
 | |
|                 prev = stat instanceof AST_SimpleStatement ? stat : null;
 | |
|             });
 | |
|             return ret;
 | |
|         };
 | |
| 
 | |
|         function join_consecutive_vars(statements, compressor) {
 | |
|             var prev = null;
 | |
|             return statements.reduce(function(a, stat){
 | |
|                 if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {
 | |
|                     prev.definitions = prev.definitions.concat(stat.definitions);
 | |
|                     CHANGED = true;
 | |
|                 }
 | |
|                 else if (stat instanceof AST_For
 | |
|                          && prev instanceof AST_Definitions
 | |
|                          && (!stat.init || stat.init.TYPE == prev.TYPE)) {
 | |
|                     CHANGED = true;
 | |
|                     a.pop();
 | |
|                     if (stat.init) {
 | |
|                         stat.init.definitions = prev.definitions.concat(stat.init.definitions);
 | |
|                     } else {
 | |
|                         stat.init = prev;
 | |
|                     }
 | |
|                     a.push(stat);
 | |
|                     prev = stat;
 | |
|                 }
 | |
|                 else {
 | |
|                     prev = stat;
 | |
|                     a.push(stat);
 | |
|                 }
 | |
|                 return a;
 | |
|             }, []);
 | |
|         };
 | |
| 
 | |
|         function negate_iifes(statements, compressor) {
 | |
|             statements.forEach(function(stat){
 | |
|                 if (stat instanceof AST_SimpleStatement) {
 | |
|                     stat.body = (function transform(thing) {
 | |
|                         return thing.transform(new TreeTransformer(function(node){
 | |
|                             if (node instanceof AST_Call && node.expression instanceof AST_Function) {
 | |
|                                 return make_node(AST_UnaryPrefix, node, {
 | |
|                                     operator: "!",
 | |
|                                     expression: node
 | |
|                                 });
 | |
|                             }
 | |
|                             else if (node instanceof AST_Call) {
 | |
|                                 node.expression = transform(node.expression);
 | |
|                             }
 | |
|                             else if (node instanceof AST_Seq) {
 | |
|                                 node.car = transform(node.car);
 | |
|                             }
 | |
|                             else if (node instanceof AST_Conditional) {
 | |
|                                 var expr = transform(node.condition);
 | |
|                                 if (expr !== node.condition) {
 | |
|                                     // it has been negated, reverse
 | |
|                                     node.condition = expr;
 | |
|                                     var tmp = node.consequent;
 | |
|                                     node.consequent = node.alternative;
 | |
|                                     node.alternative = tmp;
 | |
|                                 }
 | |
|                             }
 | |
|                             return node;
 | |
|                         }));
 | |
|                     })(stat.body);
 | |
|                 }
 | |
|             });
 | |
|         };
 | |
| 
 | |
|     };
 | |
| 
 | |
|     function extract_declarations_from_unreachable_code(compressor, stat, target) {
 | |
|         compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
 | |
|         stat.walk(new TreeWalker(function(node){
 | |
|             if (node instanceof AST_Definitions) {
 | |
|                 compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
 | |
|                 node.remove_initializers();
 | |
|                 target.push(node);
 | |
|                 return true;
 | |
|             }
 | |
|             if (node instanceof AST_Defun) {
 | |
|                 target.push(node);
 | |
|                 return true;
 | |
|             }
 | |
|             if (node instanceof AST_Scope) {
 | |
|                 return true;
 | |
|             }
 | |
|         }));
 | |
|     };
 | |
| 
 | |
|     /* -----[ boolean/negation helpers ]----- */
 | |
| 
 | |
|     // methods to determine whether an expression has a boolean result type
 | |
|     (function (def){
 | |
|         var unary_bool = [ "!", "delete" ];
 | |
|         var binary_bool = [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ];
 | |
|         def(AST_Node, function(){ return false });
 | |
|         def(AST_UnaryPrefix, function(){
 | |
|             return member(this.operator, unary_bool);
 | |
|         });
 | |
|         def(AST_Binary, function(){
 | |
|             return member(this.operator, binary_bool) ||
 | |
|                 ( (this.operator == "&&" || this.operator == "||") &&
 | |
|                   this.left.is_boolean() && this.right.is_boolean() );
 | |
|         });
 | |
|         def(AST_Conditional, function(){
 | |
|             return this.consequent.is_boolean() && this.alternative.is_boolean();
 | |
|         });
 | |
|         def(AST_Assign, function(){
 | |
|             return this.operator == "=" && this.right.is_boolean();
 | |
|         });
 | |
|         def(AST_Seq, function(){
 | |
|             return this.cdr.is_boolean();
 | |
|         });
 | |
|         def(AST_True, function(){ return true });
 | |
|         def(AST_False, function(){ return true });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("is_boolean", func);
 | |
|     });
 | |
| 
 | |
|     // methods to determine if an expression has a string result type
 | |
|     (function (def){
 | |
|         def(AST_Node, function(){ return false });
 | |
|         def(AST_String, function(){ return true });
 | |
|         def(AST_UnaryPrefix, function(){
 | |
|             return this.operator == "typeof";
 | |
|         });
 | |
|         def(AST_Binary, function(compressor){
 | |
|             return this.operator == "+" &&
 | |
|                 (this.left.is_string(compressor) || this.right.is_string(compressor));
 | |
|         });
 | |
|         def(AST_Assign, function(compressor){
 | |
|             return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
 | |
|         });
 | |
|         def(AST_Seq, function(compressor){
 | |
|             return this.cdr.is_string(compressor);
 | |
|         });
 | |
|         def(AST_Conditional, function(compressor){
 | |
|             return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
 | |
|         });
 | |
|         def(AST_Call, function(compressor){
 | |
|             return compressor.option("unsafe")
 | |
|                 && this.expression instanceof AST_SymbolRef
 | |
|                 && this.expression.name == "String"
 | |
|                 && this.expression.undeclared();
 | |
|         });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("is_string", func);
 | |
|     });
 | |
| 
 | |
|     function best_of(ast1, ast2) {
 | |
|         return ast1.print_to_string().length >
 | |
|             ast2.print_to_string().length
 | |
|             ? ast2 : ast1;
 | |
|     };
 | |
| 
 | |
|     // methods to evaluate a constant expression
 | |
|     (function (def){
 | |
|         // The evaluate method returns an array with one or two
 | |
|         // elements.  If the node has been successfully reduced to a
 | |
|         // constant, then the second element tells us the value;
 | |
|         // otherwise the second element is missing.  The first element
 | |
|         // of the array is always an AST_Node descendant; if
 | |
|         // evaluation was successful it's a node that represents the
 | |
|         // constant; otherwise it's the original or a replacement node.
 | |
|         AST_Node.DEFMETHOD("evaluate", function(compressor){
 | |
|             if (!compressor.option("evaluate")) return [ this ];
 | |
|             try {
 | |
|                 var val = this._eval(compressor);
 | |
|                 return [ best_of(make_node_from_constant(compressor, val, this), this), val ];
 | |
|             } catch(ex) {
 | |
|                 if (ex !== def) throw ex;
 | |
|                 return [ this ];
 | |
|             }
 | |
|         });
 | |
|         def(AST_Statement, function(){
 | |
|             throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
 | |
|         });
 | |
|         def(AST_Function, function(){
 | |
|             // XXX: AST_Function inherits from AST_Scope, which itself
 | |
|             // inherits from AST_Statement; however, an AST_Function
 | |
|             // isn't really a statement.  This could byte in other
 | |
|             // places too. :-( Wish JS had multiple inheritance.
 | |
|             throw def;
 | |
|         });
 | |
|         function ev(node, compressor) {
 | |
|             if (!compressor) throw new Error("Compressor must be passed");
 | |
| 
 | |
|             return node._eval(compressor);
 | |
|         };
 | |
|         def(AST_Node, function(){
 | |
|             throw def;          // not constant
 | |
|         });
 | |
|         def(AST_Constant, function(){
 | |
|             return this.getValue();
 | |
|         });
 | |
|         def(AST_UnaryPrefix, function(compressor){
 | |
|             var e = this.expression;
 | |
|             switch (this.operator) {
 | |
|               case "!": return !ev(e, compressor);
 | |
|               case "typeof":
 | |
|                 // Function would be evaluated to an array and so typeof would
 | |
|                 // incorrectly return 'object'. Hence making is a special case.
 | |
|                 if (e instanceof AST_Function) return typeof function(){};
 | |
| 
 | |
|                 e = ev(e, compressor);
 | |
| 
 | |
|                 // typeof <RegExp> returns "object" or "function" on different platforms
 | |
|                 // so cannot evaluate reliably
 | |
|                 if (e instanceof RegExp) throw def;
 | |
| 
 | |
|                 return typeof e;
 | |
|               case "void": return void ev(e, compressor);
 | |
|               case "~": return ~ev(e, compressor);
 | |
|               case "-":
 | |
|                 e = ev(e, compressor);
 | |
|                 if (e === 0) throw def;
 | |
|                 return -e;
 | |
|               case "+": return +ev(e, compressor);
 | |
|             }
 | |
|             throw def;
 | |
|         });
 | |
|         def(AST_Binary, function(c){
 | |
|             var left = this.left, right = this.right;
 | |
|             switch (this.operator) {
 | |
|               case "&&"         : return ev(left, c) &&         ev(right, c);
 | |
|               case "||"         : return ev(left, c) ||         ev(right, c);
 | |
|               case "|"          : return ev(left, c) |          ev(right, c);
 | |
|               case "&"          : return ev(left, c) &          ev(right, c);
 | |
|               case "^"          : return ev(left, c) ^          ev(right, c);
 | |
|               case "+"          : return ev(left, c) +          ev(right, c);
 | |
|               case "*"          : return ev(left, c) *          ev(right, c);
 | |
|               case "/"          : return ev(left, c) /          ev(right, c);
 | |
|               case "%"          : return ev(left, c) %          ev(right, c);
 | |
|               case "-"          : return ev(left, c) -          ev(right, c);
 | |
|               case "<<"         : return ev(left, c) <<         ev(right, c);
 | |
|               case ">>"         : return ev(left, c) >>         ev(right, c);
 | |
|               case ">>>"        : return ev(left, c) >>>        ev(right, c);
 | |
|               case "=="         : return ev(left, c) ==         ev(right, c);
 | |
|               case "==="        : return ev(left, c) ===        ev(right, c);
 | |
|               case "!="         : return ev(left, c) !=         ev(right, c);
 | |
|               case "!=="        : return ev(left, c) !==        ev(right, c);
 | |
|               case "<"          : return ev(left, c) <          ev(right, c);
 | |
|               case "<="         : return ev(left, c) <=         ev(right, c);
 | |
|               case ">"          : return ev(left, c) >          ev(right, c);
 | |
|               case ">="         : return ev(left, c) >=         ev(right, c);
 | |
|               case "in"         : return ev(left, c) in         ev(right, c);
 | |
|               case "instanceof" : return ev(left, c) instanceof ev(right, c);
 | |
|             }
 | |
|             throw def;
 | |
|         });
 | |
|         def(AST_Conditional, function(compressor){
 | |
|             return ev(this.condition, compressor)
 | |
|                 ? ev(this.consequent, compressor)
 | |
|                 : ev(this.alternative, compressor);
 | |
|         });
 | |
|         def(AST_SymbolRef, function(compressor){
 | |
|             var d = this.definition();
 | |
|             if (d && d.constant && d.init) return ev(d.init, compressor);
 | |
|             throw def;
 | |
|         });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("_eval", func);
 | |
|     });
 | |
| 
 | |
|     // method to negate an expression
 | |
|     (function(def){
 | |
|         function basic_negation(exp) {
 | |
|             return make_node(AST_UnaryPrefix, exp, {
 | |
|                 operator: "!",
 | |
|                 expression: exp
 | |
|             });
 | |
|         };
 | |
|         def(AST_Node, function(){
 | |
|             return basic_negation(this);
 | |
|         });
 | |
|         def(AST_Statement, function(){
 | |
|             throw new Error("Cannot negate a statement");
 | |
|         });
 | |
|         def(AST_Function, function(){
 | |
|             return basic_negation(this);
 | |
|         });
 | |
|         def(AST_UnaryPrefix, function(){
 | |
|             if (this.operator == "!")
 | |
|                 return this.expression;
 | |
|             return basic_negation(this);
 | |
|         });
 | |
|         def(AST_Seq, function(compressor){
 | |
|             var self = this.clone();
 | |
|             self.cdr = self.cdr.negate(compressor);
 | |
|             return self;
 | |
|         });
 | |
|         def(AST_Conditional, function(compressor){
 | |
|             var self = this.clone();
 | |
|             self.consequent = self.consequent.negate(compressor);
 | |
|             self.alternative = self.alternative.negate(compressor);
 | |
|             return best_of(basic_negation(this), self);
 | |
|         });
 | |
|         def(AST_Binary, function(compressor){
 | |
|             var self = this.clone(), op = this.operator;
 | |
|             if (compressor.option("unsafe_comps")) {
 | |
|                 switch (op) {
 | |
|                   case "<=" : self.operator = ">"  ; return self;
 | |
|                   case "<"  : self.operator = ">=" ; return self;
 | |
|                   case ">=" : self.operator = "<"  ; return self;
 | |
|                   case ">"  : self.operator = "<=" ; return self;
 | |
|                 }
 | |
|             }
 | |
|             switch (op) {
 | |
|               case "==" : self.operator = "!="; return self;
 | |
|               case "!=" : self.operator = "=="; return self;
 | |
|               case "===": self.operator = "!=="; return self;
 | |
|               case "!==": self.operator = "==="; return self;
 | |
|               case "&&":
 | |
|                 self.operator = "||";
 | |
|                 self.left = self.left.negate(compressor);
 | |
|                 self.right = self.right.negate(compressor);
 | |
|                 return best_of(basic_negation(this), self);
 | |
|               case "||":
 | |
|                 self.operator = "&&";
 | |
|                 self.left = self.left.negate(compressor);
 | |
|                 self.right = self.right.negate(compressor);
 | |
|                 return best_of(basic_negation(this), self);
 | |
|             }
 | |
|             return basic_negation(this);
 | |
|         });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("negate", function(compressor){
 | |
|             return func.call(this, compressor);
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     // determine if expression has side effects
 | |
|     (function(def){
 | |
|         def(AST_Node, function(compressor){ return true });
 | |
| 
 | |
|         def(AST_EmptyStatement, function(compressor){ return false });
 | |
|         def(AST_Constant, function(compressor){ return false });
 | |
|         def(AST_This, function(compressor){ return false });
 | |
| 
 | |
|         def(AST_Call, function(compressor){
 | |
|             var pure = compressor.option("pure_funcs");
 | |
|             if (!pure) return true;
 | |
|             return pure.indexOf(this.expression.print_to_string()) < 0;
 | |
|         });
 | |
| 
 | |
|         def(AST_Block, function(compressor){
 | |
|             for (var i = this.body.length; --i >= 0;) {
 | |
|                 if (this.body[i].has_side_effects(compressor))
 | |
|                     return true;
 | |
|             }
 | |
|             return false;
 | |
|         });
 | |
| 
 | |
|         def(AST_SimpleStatement, function(compressor){
 | |
|             return this.body.has_side_effects(compressor);
 | |
|         });
 | |
|         def(AST_Defun, function(compressor){ return true });
 | |
|         def(AST_Function, function(compressor){ return false });
 | |
|         def(AST_Binary, function(compressor){
 | |
|             return this.left.has_side_effects(compressor)
 | |
|                 || this.right.has_side_effects(compressor);
 | |
|         });
 | |
|         def(AST_Assign, function(compressor){ return true });
 | |
|         def(AST_Conditional, function(compressor){
 | |
|             return this.condition.has_side_effects(compressor)
 | |
|                 || this.consequent.has_side_effects(compressor)
 | |
|                 || this.alternative.has_side_effects(compressor);
 | |
|         });
 | |
|         def(AST_Unary, function(compressor){
 | |
|             return this.operator == "delete"
 | |
|                 || this.operator == "++"
 | |
|                 || this.operator == "--"
 | |
|                 || this.expression.has_side_effects(compressor);
 | |
|         });
 | |
|         def(AST_SymbolRef, function(compressor){ return false });
 | |
|         def(AST_Object, function(compressor){
 | |
|             for (var i = this.properties.length; --i >= 0;)
 | |
|                 if (this.properties[i].has_side_effects(compressor))
 | |
|                     return true;
 | |
|             return false;
 | |
|         });
 | |
|         def(AST_ObjectProperty, function(compressor){
 | |
|             return this.value.has_side_effects(compressor);
 | |
|         });
 | |
|         def(AST_Array, function(compressor){
 | |
|             for (var i = this.elements.length; --i >= 0;)
 | |
|                 if (this.elements[i].has_side_effects(compressor))
 | |
|                     return true;
 | |
|             return false;
 | |
|         });
 | |
|         def(AST_Dot, function(compressor){
 | |
|             if (!compressor.option("pure_getters")) return true;
 | |
|             return this.expression.has_side_effects(compressor);
 | |
|         });
 | |
|         def(AST_Sub, function(compressor){
 | |
|             if (!compressor.option("pure_getters")) return true;
 | |
|             return this.expression.has_side_effects(compressor)
 | |
|                 || this.property.has_side_effects(compressor);
 | |
|         });
 | |
|         def(AST_PropAccess, function(compressor){
 | |
|             return !compressor.option("pure_getters");
 | |
|         });
 | |
|         def(AST_Seq, function(compressor){
 | |
|             return this.car.has_side_effects(compressor)
 | |
|                 || this.cdr.has_side_effects(compressor);
 | |
|         });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("has_side_effects", func);
 | |
|     });
 | |
| 
 | |
|     // tell me if a statement aborts
 | |
|     function aborts(thing) {
 | |
|         return thing && thing.aborts();
 | |
|     };
 | |
|     (function(def){
 | |
|         def(AST_Statement, function(){ return null });
 | |
|         def(AST_Jump, function(){ return this });
 | |
|         function block_aborts(){
 | |
|             var n = this.body.length;
 | |
|             return n > 0 && aborts(this.body[n - 1]);
 | |
|         };
 | |
|         def(AST_BlockStatement, block_aborts);
 | |
|         def(AST_SwitchBranch, block_aborts);
 | |
|         def(AST_If, function(){
 | |
|             return this.alternative && aborts(this.body) && aborts(this.alternative);
 | |
|         });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("aborts", func);
 | |
|     });
 | |
| 
 | |
|     /* -----[ optimizers ]----- */
 | |
| 
 | |
|     OPT(AST_Directive, function(self, compressor){
 | |
|         if (self.scope.has_directive(self.value) !== self.scope) {
 | |
|             return make_node(AST_EmptyStatement, self);
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Debugger, function(self, compressor){
 | |
|         if (compressor.option("drop_debugger"))
 | |
|             return make_node(AST_EmptyStatement, self);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_LabeledStatement, function(self, compressor){
 | |
|         if (self.body instanceof AST_Break
 | |
|             && compressor.loopcontrol_target(self.body.label) === self.body) {
 | |
|             return make_node(AST_EmptyStatement, self);
 | |
|         }
 | |
|         return self.label.references.length == 0 ? self.body : self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Block, function(self, compressor){
 | |
|         self.body = tighten_body(self.body, compressor);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_BlockStatement, function(self, compressor){
 | |
|         self.body = tighten_body(self.body, compressor);
 | |
|         switch (self.body.length) {
 | |
|           case 1: return self.body[0];
 | |
|           case 0: return make_node(AST_EmptyStatement, self);
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     AST_Scope.DEFMETHOD("drop_unused", function(compressor){
 | |
|         var self = this;
 | |
|         if (compressor.option("unused")
 | |
|             && !(self instanceof AST_Toplevel)
 | |
|             && !self.uses_eval
 | |
|            ) {
 | |
|             var in_use = [];
 | |
|             var initializations = new Dictionary();
 | |
|             // pass 1: find out which symbols are directly used in
 | |
|             // this scope (not in nested scopes).
 | |
|             var scope = this;
 | |
|             var tw = new TreeWalker(function(node, descend){
 | |
|                 if (node !== self) {
 | |
|                     if (node instanceof AST_Defun) {
 | |
|                         initializations.add(node.name.name, node);
 | |
|                         return true; // don't go in nested scopes
 | |
|                     }
 | |
|                     if (node instanceof AST_Definitions && scope === self) {
 | |
|                         node.definitions.forEach(function(def){
 | |
|                             if (def.value) {
 | |
|                                 initializations.add(def.name.name, def.value);
 | |
|                                 if (def.value.has_side_effects(compressor)) {
 | |
|                                     def.value.walk(tw);
 | |
|                                 }
 | |
|                             }
 | |
|                         });
 | |
|                         return true;
 | |
|                     }
 | |
|                     if (node instanceof AST_SymbolRef) {
 | |
|                         push_uniq(in_use, node.definition());
 | |
|                         return true;
 | |
|                     }
 | |
|                     if (node instanceof AST_Scope) {
 | |
|                         var save_scope = scope;
 | |
|                         scope = node;
 | |
|                         descend();
 | |
|                         scope = save_scope;
 | |
|                         return true;
 | |
|                     }
 | |
|                 }
 | |
|             });
 | |
|             self.walk(tw);
 | |
|             // pass 2: for every used symbol we need to walk its
 | |
|             // initialization code to figure out if it uses other
 | |
|             // symbols (that may not be in_use).
 | |
|             for (var i = 0; i < in_use.length; ++i) {
 | |
|                 in_use[i].orig.forEach(function(decl){
 | |
|                     // undeclared globals will be instanceof AST_SymbolRef
 | |
|                     var init = initializations.get(decl.name);
 | |
|                     if (init) init.forEach(function(init){
 | |
|                         var tw = new TreeWalker(function(node){
 | |
|                             if (node instanceof AST_SymbolRef) {
 | |
|                                 push_uniq(in_use, node.definition());
 | |
|                             }
 | |
|                         });
 | |
|                         init.walk(tw);
 | |
|                     });
 | |
|                 });
 | |
|             }
 | |
|             // pass 3: we should drop declarations not in_use
 | |
|             var tt = new TreeTransformer(
 | |
|                 function before(node, descend, in_list) {
 | |
|                     if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
 | |
|                         for (var a = node.argnames, i = a.length; --i >= 0;) {
 | |
|                             var sym = a[i];
 | |
|                             if (sym.unreferenced()) {
 | |
|                                 a.pop();
 | |
|                                 compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
 | |
|                                     name : sym.name,
 | |
|                                     file : sym.start.file,
 | |
|                                     line : sym.start.line,
 | |
|                                     col  : sym.start.col
 | |
|                                 });
 | |
|                             }
 | |
|                             else break;
 | |
|                         }
 | |
|                     }
 | |
|                     if (node instanceof AST_Defun && node !== self) {
 | |
|                         if (!member(node.name.definition(), in_use)) {
 | |
|                             compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
 | |
|                                 name : node.name.name,
 | |
|                                 file : node.name.start.file,
 | |
|                                 line : node.name.start.line,
 | |
|                                 col  : node.name.start.col
 | |
|                             });
 | |
|                             return make_node(AST_EmptyStatement, node);
 | |
|                         }
 | |
|                         return node;
 | |
|                     }
 | |
|                     if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
 | |
|                         var def = node.definitions.filter(function(def){
 | |
|                             if (member(def.name.definition(), in_use)) return true;
 | |
|                             var w = {
 | |
|                                 name : def.name.name,
 | |
|                                 file : def.name.start.file,
 | |
|                                 line : def.name.start.line,
 | |
|                                 col  : def.name.start.col
 | |
|                             };
 | |
|                             if (def.value && def.value.has_side_effects(compressor)) {
 | |
|                                 def._unused_side_effects = true;
 | |
|                                 compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
 | |
|                                 return true;
 | |
|                             }
 | |
|                             compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
 | |
|                             return false;
 | |
|                         });
 | |
|                         // place uninitialized names at the start
 | |
|                         def = mergeSort(def, function(a, b){
 | |
|                             if (!a.value && b.value) return -1;
 | |
|                             if (!b.value && a.value) return 1;
 | |
|                             return 0;
 | |
|                         });
 | |
|                         // for unused names whose initialization has
 | |
|                         // side effects, we can cascade the init. code
 | |
|                         // into the next one, or next statement.
 | |
|                         var side_effects = [];
 | |
|                         for (var i = 0; i < def.length;) {
 | |
|                             var x = def[i];
 | |
|                             if (x._unused_side_effects) {
 | |
|                                 side_effects.push(x.value);
 | |
|                                 def.splice(i, 1);
 | |
|                             } else {
 | |
|                                 if (side_effects.length > 0) {
 | |
|                                     side_effects.push(x.value);
 | |
|                                     x.value = AST_Seq.from_array(side_effects);
 | |
|                                     side_effects = [];
 | |
|                                 }
 | |
|                                 ++i;
 | |
|                             }
 | |
|                         }
 | |
|                         if (side_effects.length > 0) {
 | |
|                             side_effects = make_node(AST_BlockStatement, node, {
 | |
|                                 body: [ make_node(AST_SimpleStatement, node, {
 | |
|                                     body: AST_Seq.from_array(side_effects)
 | |
|                                 }) ]
 | |
|                             });
 | |
|                         } else {
 | |
|                             side_effects = null;
 | |
|                         }
 | |
|                         if (def.length == 0 && !side_effects) {
 | |
|                             return make_node(AST_EmptyStatement, node);
 | |
|                         }
 | |
|                         if (def.length == 0) {
 | |
|                             return side_effects;
 | |
|                         }
 | |
|                         node.definitions = def;
 | |
|                         if (side_effects) {
 | |
|                             side_effects.body.unshift(node);
 | |
|                             node = side_effects;
 | |
|                         }
 | |
|                         return node;
 | |
|                     }
 | |
|                     if (node instanceof AST_For) {
 | |
|                         descend(node, this);
 | |
| 
 | |
|                         if (node.init instanceof AST_BlockStatement) {
 | |
|                             // certain combination of unused name + side effect leads to:
 | |
|                             //    https://github.com/mishoo/UglifyJS2/issues/44
 | |
|                             // that's an invalid AST.
 | |
|                             // We fix it at this stage by moving the `var` outside the `for`.
 | |
| 
 | |
|                             var body = node.init.body.slice(0, -1);
 | |
|                             node.init = node.init.body.slice(-1)[0].body;
 | |
|                             body.push(node);
 | |
| 
 | |
|                             return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
 | |
|                                 body: body
 | |
|                             });
 | |
|                         }
 | |
|                     }
 | |
|                     if (node instanceof AST_Scope && node !== self)
 | |
|                         return node;
 | |
|                 }
 | |
|             );
 | |
|             self.transform(tt);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
 | |
|         var hoist_funs = compressor.option("hoist_funs");
 | |
|         var hoist_vars = compressor.option("hoist_vars");
 | |
|         var self = this;
 | |
|         if (hoist_funs || hoist_vars) {
 | |
|             var dirs = [];
 | |
|             var hoisted = [];
 | |
|             var vars = new Dictionary(), vars_found = 0, var_decl = 0;
 | |
|             // let's count var_decl first, we seem to waste a lot of
 | |
|             // space if we hoist `var` when there's only one.
 | |
|             self.walk(new TreeWalker(function(node){
 | |
|                 if (node instanceof AST_Scope && node !== self)
 | |
|                     return true;
 | |
|                 if (node instanceof AST_Var) {
 | |
|                     ++var_decl;
 | |
|                     return true;
 | |
|                 }
 | |
|             }));
 | |
|             hoist_vars = hoist_vars && var_decl > 1;
 | |
|             var tt = new TreeTransformer(
 | |
|                 function before(node) {
 | |
|                     if (node !== self) {
 | |
|                         if (node instanceof AST_Directive) {
 | |
|                             dirs.push(node);
 | |
|                             return make_node(AST_EmptyStatement, node);
 | |
|                         }
 | |
|                         if (node instanceof AST_Defun && hoist_funs) {
 | |
|                             hoisted.push(node);
 | |
|                             return make_node(AST_EmptyStatement, node);
 | |
|                         }
 | |
|                         if (node instanceof AST_Var && hoist_vars) {
 | |
|                             node.definitions.forEach(function(def){
 | |
|                                 vars.set(def.name.name, def);
 | |
|                                 ++vars_found;
 | |
|                             });
 | |
|                             var seq = node.to_assignments();
 | |
|                             var p = tt.parent();
 | |
|                             if (p instanceof AST_ForIn && p.init === node) {
 | |
|                                 if (seq == null) return node.definitions[0].name;
 | |
|                                 return seq;
 | |
|                             }
 | |
|                             if (p instanceof AST_For && p.init === node) {
 | |
|                                 return seq;
 | |
|                             }
 | |
|                             if (!seq) return make_node(AST_EmptyStatement, node);
 | |
|                             return make_node(AST_SimpleStatement, node, {
 | |
|                                 body: seq
 | |
|                             });
 | |
|                         }
 | |
|                         if (node instanceof AST_Scope)
 | |
|                             return node; // to avoid descending in nested scopes
 | |
|                     }
 | |
|                 }
 | |
|             );
 | |
|             self = self.transform(tt);
 | |
|             if (vars_found > 0) {
 | |
|                 // collect only vars which don't show up in self's arguments list
 | |
|                 var defs = [];
 | |
|                 vars.each(function(def, name){
 | |
|                     if (self instanceof AST_Lambda
 | |
|                         && find_if(function(x){ return x.name == def.name.name },
 | |
|                                    self.argnames)) {
 | |
|                         vars.del(name);
 | |
|                     } else {
 | |
|                         def = def.clone();
 | |
|                         def.value = null;
 | |
|                         defs.push(def);
 | |
|                         vars.set(name, def);
 | |
|                     }
 | |
|                 });
 | |
|                 if (defs.length > 0) {
 | |
|                     // try to merge in assignments
 | |
|                     for (var i = 0; i < self.body.length;) {
 | |
|                         if (self.body[i] instanceof AST_SimpleStatement) {
 | |
|                             var expr = self.body[i].body, sym, assign;
 | |
|                             if (expr instanceof AST_Assign
 | |
|                                 && expr.operator == "="
 | |
|                                 && (sym = expr.left) instanceof AST_Symbol
 | |
|                                 && vars.has(sym.name))
 | |
|                             {
 | |
|                                 var def = vars.get(sym.name);
 | |
|                                 if (def.value) break;
 | |
|                                 def.value = expr.right;
 | |
|                                 remove(defs, def);
 | |
|                                 defs.push(def);
 | |
|                                 self.body.splice(i, 1);
 | |
|                                 continue;
 | |
|                             }
 | |
|                             if (expr instanceof AST_Seq
 | |
|                                 && (assign = expr.car) instanceof AST_Assign
 | |
|                                 && assign.operator == "="
 | |
|                                 && (sym = assign.left) instanceof AST_Symbol
 | |
|                                 && vars.has(sym.name))
 | |
|                             {
 | |
|                                 var def = vars.get(sym.name);
 | |
|                                 if (def.value) break;
 | |
|                                 def.value = assign.right;
 | |
|                                 remove(defs, def);
 | |
|                                 defs.push(def);
 | |
|                                 self.body[i].body = expr.cdr;
 | |
|                                 continue;
 | |
|                             }
 | |
|                         }
 | |
|                         if (self.body[i] instanceof AST_EmptyStatement) {
 | |
|                             self.body.splice(i, 1);
 | |
|                             continue;
 | |
|                         }
 | |
|                         if (self.body[i] instanceof AST_BlockStatement) {
 | |
|                             var tmp = [ i, 1 ].concat(self.body[i].body);
 | |
|                             self.body.splice.apply(self.body, tmp);
 | |
|                             continue;
 | |
|                         }
 | |
|                         break;
 | |
|                     }
 | |
|                     defs = make_node(AST_Var, self, {
 | |
|                         definitions: defs
 | |
|                     });
 | |
|                     hoisted.push(defs);
 | |
|                 };
 | |
|             }
 | |
|             self.body = dirs.concat(hoisted, self.body);
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_SimpleStatement, function(self, compressor){
 | |
|         if (compressor.option("side_effects")) {
 | |
|             if (!self.body.has_side_effects(compressor)) {
 | |
|                 compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
 | |
|                 return make_node(AST_EmptyStatement, self);
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_DWLoop, function(self, compressor){
 | |
|         var cond = self.condition.evaluate(compressor);
 | |
|         self.condition = cond[0];
 | |
|         if (!compressor.option("loops")) return self;
 | |
|         if (cond.length > 1) {
 | |
|             if (cond[1]) {
 | |
|                 return make_node(AST_For, self, {
 | |
|                     body: self.body
 | |
|                 });
 | |
|             } else if (self instanceof AST_While) {
 | |
|                 if (compressor.option("dead_code")) {
 | |
|                     var a = [];
 | |
|                     extract_declarations_from_unreachable_code(compressor, self.body, a);
 | |
|                     return make_node(AST_BlockStatement, self, { body: a });
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     function if_break_in_loop(self, compressor) {
 | |
|         function drop_it(rest) {
 | |
|             rest = as_statement_array(rest);
 | |
|             if (self.body instanceof AST_BlockStatement) {
 | |
|                 self.body = self.body.clone();
 | |
|                 self.body.body = rest.concat(self.body.body.slice(1));
 | |
|                 self.body = self.body.transform(compressor);
 | |
|             } else {
 | |
|                 self.body = make_node(AST_BlockStatement, self.body, {
 | |
|                     body: rest
 | |
|                 }).transform(compressor);
 | |
|             }
 | |
|             if_break_in_loop(self, compressor);
 | |
|         }
 | |
|         var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
 | |
|         if (first instanceof AST_If) {
 | |
|             if (first.body instanceof AST_Break
 | |
|                 && compressor.loopcontrol_target(first.body.label) === self) {
 | |
|                 if (self.condition) {
 | |
|                     self.condition = make_node(AST_Binary, self.condition, {
 | |
|                         left: self.condition,
 | |
|                         operator: "&&",
 | |
|                         right: first.condition.negate(compressor),
 | |
|                     });
 | |
|                 } else {
 | |
|                     self.condition = first.condition.negate(compressor);
 | |
|                 }
 | |
|                 drop_it(first.alternative);
 | |
|             }
 | |
|             else if (first.alternative instanceof AST_Break
 | |
|                      && compressor.loopcontrol_target(first.alternative.label) === self) {
 | |
|                 if (self.condition) {
 | |
|                     self.condition = make_node(AST_Binary, self.condition, {
 | |
|                         left: self.condition,
 | |
|                         operator: "&&",
 | |
|                         right: first.condition,
 | |
|                     });
 | |
|                 } else {
 | |
|                     self.condition = first.condition;
 | |
|                 }
 | |
|                 drop_it(first.body);
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     OPT(AST_While, function(self, compressor) {
 | |
|         if (!compressor.option("loops")) return self;
 | |
|         self = AST_DWLoop.prototype.optimize.call(self, compressor);
 | |
|         if (self instanceof AST_While) {
 | |
|             if_break_in_loop(self, compressor);
 | |
|             self = make_node(AST_For, self, self).transform(compressor);
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_For, function(self, compressor){
 | |
|         var cond = self.condition;
 | |
|         if (cond) {
 | |
|             cond = cond.evaluate(compressor);
 | |
|             self.condition = cond[0];
 | |
|         }
 | |
|         if (!compressor.option("loops")) return self;
 | |
|         if (cond) {
 | |
|             if (cond.length > 1 && !cond[1]) {
 | |
|                 if (compressor.option("dead_code")) {
 | |
|                     var a = [];
 | |
|                     if (self.init instanceof AST_Statement) {
 | |
|                         a.push(self.init);
 | |
|                     }
 | |
|                     else if (self.init) {
 | |
|                         a.push(make_node(AST_SimpleStatement, self.init, {
 | |
|                             body: self.init
 | |
|                         }));
 | |
|                     }
 | |
|                     extract_declarations_from_unreachable_code(compressor, self.body, a);
 | |
|                     return make_node(AST_BlockStatement, self, { body: a });
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if_break_in_loop(self, compressor);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_If, function(self, compressor){
 | |
|         if (!compressor.option("conditionals")) return self;
 | |
|         // if condition can be statically determined, warn and drop
 | |
|         // one of the blocks.  note, statically determined implies
 | |
|         // “has no side effects”; also it doesn't work for cases like
 | |
|         // `x && true`, though it probably should.
 | |
|         var cond = self.condition.evaluate(compressor);
 | |
|         self.condition = cond[0];
 | |
|         if (cond.length > 1) {
 | |
|             if (cond[1]) {
 | |
|                 compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
 | |
|                 if (compressor.option("dead_code")) {
 | |
|                     var a = [];
 | |
|                     if (self.alternative) {
 | |
|                         extract_declarations_from_unreachable_code(compressor, self.alternative, a);
 | |
|                     }
 | |
|                     a.push(self.body);
 | |
|                     return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);
 | |
|                 }
 | |
|             } else {
 | |
|                 compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
 | |
|                 if (compressor.option("dead_code")) {
 | |
|                     var a = [];
 | |
|                     extract_declarations_from_unreachable_code(compressor, self.body, a);
 | |
|                     if (self.alternative) a.push(self.alternative);
 | |
|                     return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if (is_empty(self.alternative)) self.alternative = null;
 | |
|         var negated = self.condition.negate(compressor);
 | |
|         var negated_is_best = best_of(self.condition, negated) === negated;
 | |
|         if (self.alternative && negated_is_best) {
 | |
|             negated_is_best = false; // because we already do the switch here.
 | |
|             self.condition = negated;
 | |
|             var tmp = self.body;
 | |
|             self.body = self.alternative || make_node(AST_EmptyStatement);
 | |
|             self.alternative = tmp;
 | |
|         }
 | |
|         if (is_empty(self.body) && is_empty(self.alternative)) {
 | |
|             return make_node(AST_SimpleStatement, self.condition, {
 | |
|                 body: self.condition
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         if (self.body instanceof AST_SimpleStatement
 | |
|             && self.alternative instanceof AST_SimpleStatement) {
 | |
|             return make_node(AST_SimpleStatement, self, {
 | |
|                 body: make_node(AST_Conditional, self, {
 | |
|                     condition   : self.condition,
 | |
|                     consequent  : self.body.body,
 | |
|                     alternative : self.alternative.body
 | |
|                 })
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
 | |
|             if (negated_is_best) return make_node(AST_SimpleStatement, self, {
 | |
|                 body: make_node(AST_Binary, self, {
 | |
|                     operator : "||",
 | |
|                     left     : negated,
 | |
|                     right    : self.body.body
 | |
|                 })
 | |
|             }).transform(compressor);
 | |
|             return make_node(AST_SimpleStatement, self, {
 | |
|                 body: make_node(AST_Binary, self, {
 | |
|                     operator : "&&",
 | |
|                     left     : self.condition,
 | |
|                     right    : self.body.body
 | |
|                 })
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         if (self.body instanceof AST_EmptyStatement
 | |
|             && self.alternative
 | |
|             && self.alternative instanceof AST_SimpleStatement) {
 | |
|             return make_node(AST_SimpleStatement, self, {
 | |
|                 body: make_node(AST_Binary, self, {
 | |
|                     operator : "||",
 | |
|                     left     : self.condition,
 | |
|                     right    : self.alternative.body
 | |
|                 })
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         if (self.body instanceof AST_Exit
 | |
|             && self.alternative instanceof AST_Exit
 | |
|             && self.body.TYPE == self.alternative.TYPE) {
 | |
|             return make_node(self.body.CTOR, self, {
 | |
|                 value: make_node(AST_Conditional, self, {
 | |
|                     condition   : self.condition,
 | |
|                     consequent  : self.body.value || make_node(AST_Undefined, self.body).optimize(compressor),
 | |
|                     alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).optimize(compressor)
 | |
|                 })
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         if (self.body instanceof AST_If
 | |
|             && !self.body.alternative
 | |
|             && !self.alternative) {
 | |
|             self.condition = make_node(AST_Binary, self.condition, {
 | |
|                 operator: "&&",
 | |
|                 left: self.condition,
 | |
|                 right: self.body.condition
 | |
|             }).transform(compressor);
 | |
|             self.body = self.body.body;
 | |
|         }
 | |
|         if (aborts(self.body)) {
 | |
|             if (self.alternative) {
 | |
|                 var alt = self.alternative;
 | |
|                 self.alternative = null;
 | |
|                 return make_node(AST_BlockStatement, self, {
 | |
|                     body: [ self, alt ]
 | |
|                 }).transform(compressor);
 | |
|             }
 | |
|         }
 | |
|         if (aborts(self.alternative)) {
 | |
|             var body = self.body;
 | |
|             self.body = self.alternative;
 | |
|             self.condition = negated_is_best ? negated : self.condition.negate(compressor);
 | |
|             self.alternative = null;
 | |
|             return make_node(AST_BlockStatement, self, {
 | |
|                 body: [ self, body ]
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Switch, function(self, compressor){
 | |
|         if (self.body.length == 0 && compressor.option("conditionals")) {
 | |
|             return make_node(AST_SimpleStatement, self, {
 | |
|                 body: self.expression
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         for(;;) {
 | |
|             var last_branch = self.body[self.body.length - 1];
 | |
|             if (last_branch) {
 | |
|                 var stat = last_branch.body[last_branch.body.length - 1]; // last statement
 | |
|                 if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
 | |
|                     last_branch.body.pop();
 | |
|                 if (last_branch instanceof AST_Default && last_branch.body.length == 0) {
 | |
|                     self.body.pop();
 | |
|                     continue;
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         var exp = self.expression.evaluate(compressor);
 | |
|         out: if (exp.length == 2) try {
 | |
|             // constant expression
 | |
|             self.expression = exp[0];
 | |
|             if (!compressor.option("dead_code")) break out;
 | |
|             var value = exp[1];
 | |
|             var in_if = false;
 | |
|             var in_block = false;
 | |
|             var started = false;
 | |
|             var stopped = false;
 | |
|             var ruined = false;
 | |
|             var tt = new TreeTransformer(function(node, descend, in_list){
 | |
|                 if (node instanceof AST_Lambda || node instanceof AST_SimpleStatement) {
 | |
|                     // no need to descend these node types
 | |
|                     return node;
 | |
|                 }
 | |
|                 else if (node instanceof AST_Switch && node === self) {
 | |
|                     node = node.clone();
 | |
|                     descend(node, this);
 | |
|                     return ruined ? node : make_node(AST_BlockStatement, node, {
 | |
|                         body: node.body.reduce(function(a, branch){
 | |
|                             return a.concat(branch.body);
 | |
|                         }, [])
 | |
|                     }).transform(compressor);
 | |
|                 }
 | |
|                 else if (node instanceof AST_If || node instanceof AST_Try) {
 | |
|                     var save = in_if;
 | |
|                     in_if = !in_block;
 | |
|                     descend(node, this);
 | |
|                     in_if = save;
 | |
|                     return node;
 | |
|                 }
 | |
|                 else if (node instanceof AST_StatementWithBody || node instanceof AST_Switch) {
 | |
|                     var save = in_block;
 | |
|                     in_block = true;
 | |
|                     descend(node, this);
 | |
|                     in_block = save;
 | |
|                     return node;
 | |
|                 }
 | |
|                 else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) {
 | |
|                     if (in_if) {
 | |
|                         ruined = true;
 | |
|                         return node;
 | |
|                     }
 | |
|                     if (in_block) return node;
 | |
|                     stopped = true;
 | |
|                     return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
 | |
|                 }
 | |
|                 else if (node instanceof AST_SwitchBranch && this.parent() === self) {
 | |
|                     if (stopped) return MAP.skip;
 | |
|                     if (node instanceof AST_Case) {
 | |
|                         var exp = node.expression.evaluate(compressor);
 | |
|                         if (exp.length < 2) {
 | |
|                             // got a case with non-constant expression, baling out
 | |
|                             throw self;
 | |
|                         }
 | |
|                         if (exp[1] === value || started) {
 | |
|                             started = true;
 | |
|                             if (aborts(node)) stopped = true;
 | |
|                             descend(node, this);
 | |
|                             return node;
 | |
|                         }
 | |
|                         return MAP.skip;
 | |
|                     }
 | |
|                     descend(node, this);
 | |
|                     return node;
 | |
|                 }
 | |
|             });
 | |
|             tt.stack = compressor.stack.slice(); // so that's able to see parent nodes
 | |
|             self = self.transform(tt);
 | |
|         } catch(ex) {
 | |
|             if (ex !== self) throw ex;
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Case, function(self, compressor){
 | |
|         self.body = tighten_body(self.body, compressor);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Try, function(self, compressor){
 | |
|         self.body = tighten_body(self.body, compressor);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     AST_Definitions.DEFMETHOD("remove_initializers", function(){
 | |
|         this.definitions.forEach(function(def){ def.value = null });
 | |
|     });
 | |
| 
 | |
|     AST_Definitions.DEFMETHOD("to_assignments", function(){
 | |
|         var assignments = this.definitions.reduce(function(a, def){
 | |
|             if (def.value) {
 | |
|                 var name = make_node(AST_SymbolRef, def.name, def.name);
 | |
|                 a.push(make_node(AST_Assign, def, {
 | |
|                     operator : "=",
 | |
|                     left     : name,
 | |
|                     right    : def.value
 | |
|                 }));
 | |
|             }
 | |
|             return a;
 | |
|         }, []);
 | |
|         if (assignments.length == 0) return null;
 | |
|         return AST_Seq.from_array(assignments);
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Definitions, function(self, compressor){
 | |
|         if (self.definitions.length == 0)
 | |
|             return make_node(AST_EmptyStatement, self);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Function, function(self, compressor){
 | |
|         self = AST_Lambda.prototype.optimize.call(self, compressor);
 | |
|         if (compressor.option("unused")) {
 | |
|             if (self.name && self.name.unreferenced()) {
 | |
|                 self.name = null;
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Call, function(self, compressor){
 | |
|         if (compressor.option("unsafe")) {
 | |
|             var exp = self.expression;
 | |
|             if (exp instanceof AST_SymbolRef && exp.undeclared()) {
 | |
|                 switch (exp.name) {
 | |
|                   case "Array":
 | |
|                     if (self.args.length != 1) {
 | |
|                         return make_node(AST_Array, self, {
 | |
|                             elements: self.args
 | |
|                         }).transform(compressor);
 | |
|                     }
 | |
|                     break;
 | |
|                   case "Object":
 | |
|                     if (self.args.length == 0) {
 | |
|                         return make_node(AST_Object, self, {
 | |
|                             properties: []
 | |
|                         });
 | |
|                     }
 | |
|                     break;
 | |
|                   case "String":
 | |
|                     if (self.args.length == 0) return make_node(AST_String, self, {
 | |
|                         value: ""
 | |
|                     });
 | |
|                     if (self.args.length <= 1) return make_node(AST_Binary, self, {
 | |
|                         left: self.args[0],
 | |
|                         operator: "+",
 | |
|                         right: make_node(AST_String, self, { value: "" })
 | |
|                     }).transform(compressor);
 | |
|                     break;
 | |
|                   case "Number":
 | |
|                     if (self.args.length == 0) return make_node(AST_Number, self, {
 | |
|                         value: 0
 | |
|                     });
 | |
|                     if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
 | |
|                         expression: self.args[0],
 | |
|                         operator: "+"
 | |
|                     }).transform(compressor);
 | |
|                   case "Boolean":
 | |
|                     if (self.args.length == 0) return make_node(AST_False, self);
 | |
|                     if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
 | |
|                         expression: make_node(AST_UnaryPrefix, null, {
 | |
|                             expression: self.args[0],
 | |
|                             operator: "!"
 | |
|                         }),
 | |
|                         operator: "!"
 | |
|                     }).transform(compressor);
 | |
|                     break;
 | |
|                   case "Function":
 | |
|                     if (all(self.args, function(x){ return x instanceof AST_String })) {
 | |
|                         // quite a corner-case, but we can handle it:
 | |
|                         //   https://github.com/mishoo/UglifyJS2/issues/203
 | |
|                         // if the code argument is a constant, then we can minify it.
 | |
|                         try {
 | |
|                             var code = "(function(" + self.args.slice(0, -1).map(function(arg){
 | |
|                                 return arg.value;
 | |
|                             }).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
 | |
|                             var ast = parse(code);
 | |
|                             ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
 | |
|                             var comp = new Compressor(compressor.options);
 | |
|                             ast = ast.transform(comp);
 | |
|                             ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
 | |
|                             ast.mangle_names();
 | |
|                             var fun;
 | |
|                             try {
 | |
|                                 ast.walk(new TreeWalker(function(node){
 | |
|                                     if (node instanceof AST_Lambda) {
 | |
|                                         fun = node;
 | |
|                                         throw ast;
 | |
|                                     }
 | |
|                                 }));
 | |
|                             } catch(ex) {
 | |
|                                 if (ex !== ast) throw ex;
 | |
|                             };
 | |
|                             var args = fun.argnames.map(function(arg, i){
 | |
|                                 return make_node(AST_String, self.args[i], {
 | |
|                                     value: arg.print_to_string()
 | |
|                                 });
 | |
|                             });
 | |
|                             var code = OutputStream();
 | |
|                             AST_BlockStatement.prototype._codegen.call(fun, fun, code);
 | |
|                             code = code.toString().replace(/^\{|\}$/g, "");
 | |
|                             args.push(make_node(AST_String, self.args[self.args.length - 1], {
 | |
|                                 value: code
 | |
|                             }));
 | |
|                             self.args = args;
 | |
|                             return self;
 | |
|                         } catch(ex) {
 | |
|                             if (ex instanceof JS_Parse_Error) {
 | |
|                                 compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
 | |
|                                 compressor.warn(ex.toString());
 | |
|                             } else {
 | |
|                                 console.log(ex);
 | |
|                                 throw ex;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
 | |
|                 return make_node(AST_Binary, self, {
 | |
|                     left: make_node(AST_String, self, { value: "" }),
 | |
|                     operator: "+",
 | |
|                     right: exp.expression
 | |
|                 }).transform(compressor);
 | |
|             }
 | |
|             else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: {
 | |
|                 var separator = self.args.length == 0 ? "," : self.args[0].evaluate(compressor)[1];
 | |
|                 if (separator == null) break EXIT; // not a constant
 | |
|                 var elements = exp.expression.elements.reduce(function(a, el){
 | |
|                     el = el.evaluate(compressor);
 | |
|                     if (a.length == 0 || el.length == 1) {
 | |
|                         a.push(el);
 | |
|                     } else {
 | |
|                         var last = a[a.length - 1];
 | |
|                         if (last.length == 2) {
 | |
|                             // it's a constant
 | |
|                             var val = "" + last[1] + separator + el[1];
 | |
|                             a[a.length - 1] = [ make_node_from_constant(compressor, val, last[0]), val ];
 | |
|                         } else {
 | |
|                             a.push(el);
 | |
|                         }
 | |
|                     }
 | |
|                     return a;
 | |
|                 }, []);
 | |
|                 if (elements.length == 0) return make_node(AST_String, self, { value: "" });
 | |
|                 if (elements.length == 1) return elements[0][0];
 | |
|                 if (separator == "") {
 | |
|                     var first;
 | |
|                     if (elements[0][0] instanceof AST_String
 | |
|                         || elements[1][0] instanceof AST_String) {
 | |
|                         first = elements.shift()[0];
 | |
|                     } else {
 | |
|                         first = make_node(AST_String, self, { value: "" });
 | |
|                     }
 | |
|                     return elements.reduce(function(prev, el){
 | |
|                         return make_node(AST_Binary, el[0], {
 | |
|                             operator : "+",
 | |
|                             left     : prev,
 | |
|                             right    : el[0],
 | |
|                         });
 | |
|                     }, first).transform(compressor);
 | |
|                 }
 | |
|                 // need this awkward cloning to not affect original element
 | |
|                 // best_of will decide which one to get through.
 | |
|                 var node = self.clone();
 | |
|                 node.expression = node.expression.clone();
 | |
|                 node.expression.expression = node.expression.expression.clone();
 | |
|                 node.expression.expression.elements = elements.map(function(el){
 | |
|                     return el[0];
 | |
|                 });
 | |
|                 return best_of(self, node);
 | |
|             }
 | |
|         }
 | |
|         if (compressor.option("side_effects")) {
 | |
|             if (self.expression instanceof AST_Function
 | |
|                 && self.args.length == 0
 | |
|                 && !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) {
 | |
|                 return make_node(AST_Undefined, self).transform(compressor);
 | |
|             }
 | |
|         }
 | |
|         if (compressor.option("drop_console")) {
 | |
|             if (self.expression instanceof AST_PropAccess &&
 | |
|                 self.expression.expression instanceof AST_SymbolRef &&
 | |
|                 self.expression.expression.name == "console" &&
 | |
|                 self.expression.expression.undeclared()) {
 | |
|                 return make_node(AST_Undefined, self).transform(compressor);
 | |
|             }
 | |
|         }
 | |
|         return self.evaluate(compressor)[0];
 | |
|     });
 | |
| 
 | |
|     OPT(AST_New, function(self, compressor){
 | |
|         if (compressor.option("unsafe")) {
 | |
|             var exp = self.expression;
 | |
|             if (exp instanceof AST_SymbolRef && exp.undeclared()) {
 | |
|                 switch (exp.name) {
 | |
|                   case "Object":
 | |
|                   case "RegExp":
 | |
|                   case "Function":
 | |
|                   case "Error":
 | |
|                   case "Array":
 | |
|                     return make_node(AST_Call, self, self).transform(compressor);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Seq, function(self, compressor){
 | |
|         if (!compressor.option("side_effects"))
 | |
|             return self;
 | |
|         if (!self.car.has_side_effects(compressor)) {
 | |
|             // we shouldn't compress (1,eval)(something) to
 | |
|             // eval(something) because that changes the meaning of
 | |
|             // eval (becomes lexical instead of global).
 | |
|             var p;
 | |
|             if (!(self.cdr instanceof AST_SymbolRef
 | |
|                   && self.cdr.name == "eval"
 | |
|                   && self.cdr.undeclared()
 | |
|                   && (p = compressor.parent()) instanceof AST_Call
 | |
|                   && p.expression === self)) {
 | |
|                 return self.cdr;
 | |
|             }
 | |
|         }
 | |
|         if (compressor.option("cascade")) {
 | |
|             if (self.car instanceof AST_Assign
 | |
|                 && !self.car.left.has_side_effects(compressor)) {
 | |
|                 if (self.car.left.equivalent_to(self.cdr)) {
 | |
|                     return self.car;
 | |
|                 }
 | |
|                 if (self.cdr instanceof AST_Call
 | |
|                     && self.cdr.expression.equivalent_to(self.car.left)) {
 | |
|                     self.cdr.expression = self.car;
 | |
|                     return self.cdr;
 | |
|                 }
 | |
|             }
 | |
|             if (!self.car.has_side_effects(compressor)
 | |
|                 && !self.cdr.has_side_effects(compressor)
 | |
|                 && self.car.equivalent_to(self.cdr)) {
 | |
|                 return self.car;
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
 | |
|         if (compressor.option("sequences")) {
 | |
|             if (this.expression instanceof AST_Seq) {
 | |
|                 var seq = this.expression;
 | |
|                 var x = seq.to_array();
 | |
|                 this.expression = x.pop();
 | |
|                 x.push(this);
 | |
|                 seq = AST_Seq.from_array(x).transform(compressor);
 | |
|                 return seq;
 | |
|             }
 | |
|         }
 | |
|         return this;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_UnaryPostfix, function(self, compressor){
 | |
|         return self.lift_sequences(compressor);
 | |
|     });
 | |
| 
 | |
|     OPT(AST_UnaryPrefix, function(self, compressor){
 | |
|         self = self.lift_sequences(compressor);
 | |
|         var e = self.expression;
 | |
|         if (compressor.option("booleans") && compressor.in_boolean_context()) {
 | |
|             switch (self.operator) {
 | |
|               case "!":
 | |
|                 if (e instanceof AST_UnaryPrefix && e.operator == "!") {
 | |
|                     // !!foo ==> foo, if we're in boolean context
 | |
|                     return e.expression;
 | |
|                 }
 | |
|                 break;
 | |
|               case "typeof":
 | |
|                 // typeof always returns a non-empty string, thus it's
 | |
|                 // always true in booleans
 | |
|                 compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
 | |
|                 return make_node(AST_True, self);
 | |
|             }
 | |
|             if (e instanceof AST_Binary && self.operator == "!") {
 | |
|                 self = best_of(self, e.negate(compressor));
 | |
|             }
 | |
|         }
 | |
|         return self.evaluate(compressor)[0];
 | |
|     });
 | |
| 
 | |
|     function has_side_effects_or_prop_access(node, compressor) {
 | |
|         var save_pure_getters = compressor.option("pure_getters");
 | |
|         compressor.options.pure_getters = false;
 | |
|         var ret = node.has_side_effects(compressor);
 | |
|         compressor.options.pure_getters = save_pure_getters;
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
 | |
|         if (compressor.option("sequences")) {
 | |
|             if (this.left instanceof AST_Seq) {
 | |
|                 var seq = this.left;
 | |
|                 var x = seq.to_array();
 | |
|                 this.left = x.pop();
 | |
|                 x.push(this);
 | |
|                 seq = AST_Seq.from_array(x).transform(compressor);
 | |
|                 return seq;
 | |
|             }
 | |
|             if (this.right instanceof AST_Seq
 | |
|                 && this instanceof AST_Assign
 | |
|                 && !has_side_effects_or_prop_access(this.left, compressor)) {
 | |
|                 var seq = this.right;
 | |
|                 var x = seq.to_array();
 | |
|                 this.right = x.pop();
 | |
|                 x.push(this);
 | |
|                 seq = AST_Seq.from_array(x).transform(compressor);
 | |
|                 return seq;
 | |
|             }
 | |
|         }
 | |
|         return this;
 | |
|     });
 | |
| 
 | |
|     var commutativeOperators = makePredicate("== === != !== * & | ^");
 | |
| 
 | |
|     OPT(AST_Binary, function(self, compressor){
 | |
|         var reverse = compressor.has_directive("use asm") ? noop
 | |
|             : function(op, force) {
 | |
|                 if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) {
 | |
|                     if (op) self.operator = op;
 | |
|                     var tmp = self.left;
 | |
|                     self.left = self.right;
 | |
|                     self.right = tmp;
 | |
|                 }
 | |
|             };
 | |
|         if (commutativeOperators(self.operator)) {
 | |
|             if (self.right instanceof AST_Constant
 | |
|                 && !(self.left instanceof AST_Constant)) {
 | |
|                 // if right is a constant, whatever side effects the
 | |
|                 // left side might have could not influence the
 | |
|                 // result.  hence, force switch.
 | |
| 
 | |
|                 if (!(self.left instanceof AST_Binary
 | |
|                       && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
 | |
|                     reverse(null, true);
 | |
|                 }
 | |
|             }
 | |
|             if (/^[!=]==?$/.test(self.operator)) {
 | |
|                 if (self.left instanceof AST_SymbolRef && self.right instanceof AST_Conditional) {
 | |
|                     if (self.right.consequent instanceof AST_SymbolRef
 | |
|                         && self.right.consequent.definition() === self.left.definition()) {
 | |
|                         if (/^==/.test(self.operator)) return self.right.condition;
 | |
|                         if (/^!=/.test(self.operator)) return self.right.condition.negate(compressor);
 | |
|                     }
 | |
|                     if (self.right.alternative instanceof AST_SymbolRef
 | |
|                         && self.right.alternative.definition() === self.left.definition()) {
 | |
|                         if (/^==/.test(self.operator)) return self.right.condition.negate(compressor);
 | |
|                         if (/^!=/.test(self.operator)) return self.right.condition;
 | |
|                     }
 | |
|                 }
 | |
|                 if (self.right instanceof AST_SymbolRef && self.left instanceof AST_Conditional) {
 | |
|                     if (self.left.consequent instanceof AST_SymbolRef
 | |
|                         && self.left.consequent.definition() === self.right.definition()) {
 | |
|                         if (/^==/.test(self.operator)) return self.left.condition;
 | |
|                         if (/^!=/.test(self.operator)) return self.left.condition.negate(compressor);
 | |
|                     }
 | |
|                     if (self.left.alternative instanceof AST_SymbolRef
 | |
|                         && self.left.alternative.definition() === self.right.definition()) {
 | |
|                         if (/^==/.test(self.operator)) return self.left.condition.negate(compressor);
 | |
|                         if (/^!=/.test(self.operator)) return self.left.condition;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         self = self.lift_sequences(compressor);
 | |
|         if (compressor.option("comparisons")) switch (self.operator) {
 | |
|           case "===":
 | |
|           case "!==":
 | |
|             if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
 | |
|                 (self.left.is_boolean() && self.right.is_boolean())) {
 | |
|                 self.operator = self.operator.substr(0, 2);
 | |
|             }
 | |
|             // XXX: intentionally falling down to the next case
 | |
|           case "==":
 | |
|           case "!=":
 | |
|             if (self.left instanceof AST_String
 | |
|                 && self.left.value == "undefined"
 | |
|                 && self.right instanceof AST_UnaryPrefix
 | |
|                 && self.right.operator == "typeof"
 | |
|                 && compressor.option("unsafe")) {
 | |
|                 if (!(self.right.expression instanceof AST_SymbolRef)
 | |
|                     || !self.right.expression.undeclared()) {
 | |
|                     self.right = self.right.expression;
 | |
|                     self.left = make_node(AST_Undefined, self.left).optimize(compressor);
 | |
|                     if (self.operator.length == 2) self.operator += "=";
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
 | |
|           case "&&":
 | |
|             var ll = self.left.evaluate(compressor);
 | |
|             var rr = self.right.evaluate(compressor);
 | |
|             if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {
 | |
|                 compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
 | |
|                 return make_node(AST_False, self);
 | |
|             }
 | |
|             if (ll.length > 1 && ll[1]) {
 | |
|                 return rr[0];
 | |
|             }
 | |
|             if (rr.length > 1 && rr[1]) {
 | |
|                 return ll[0];
 | |
|             }
 | |
|             break;
 | |
|           case "||":
 | |
|             var ll = self.left.evaluate(compressor);
 | |
|             var rr = self.right.evaluate(compressor);
 | |
|             if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {
 | |
|                 compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
 | |
|                 return make_node(AST_True, self);
 | |
|             }
 | |
|             if (ll.length > 1 && !ll[1]) {
 | |
|                 return rr[0];
 | |
|             }
 | |
|             if (rr.length > 1 && !rr[1]) {
 | |
|                 return ll[0];
 | |
|             }
 | |
|             break;
 | |
|           case "+":
 | |
|             var ll = self.left.evaluate(compressor);
 | |
|             var rr = self.right.evaluate(compressor);
 | |
|             if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1]) ||
 | |
|                 (rr.length > 1 && rr[0] instanceof AST_String && rr[1])) {
 | |
|                 compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
 | |
|                 return make_node(AST_True, self);
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         if (compressor.option("comparisons")) {
 | |
|             if (!(compressor.parent() instanceof AST_Binary)
 | |
|                 || compressor.parent() instanceof AST_Assign) {
 | |
|                 var negated = make_node(AST_UnaryPrefix, self, {
 | |
|                     operator: "!",
 | |
|                     expression: self.negate(compressor)
 | |
|                 });
 | |
|                 self = best_of(self, negated);
 | |
|             }
 | |
|             switch (self.operator) {
 | |
|               case "<": reverse(">"); break;
 | |
|               case "<=": reverse(">="); break;
 | |
|             }
 | |
|         }
 | |
|         if (self.operator == "+" && self.right instanceof AST_String
 | |
|             && self.right.getValue() === "" && self.left instanceof AST_Binary
 | |
|             && self.left.operator == "+" && self.left.is_string(compressor)) {
 | |
|             return self.left;
 | |
|         }
 | |
|         if (compressor.option("evaluate")) {
 | |
|             if (self.operator == "+") {
 | |
|                 if (self.left instanceof AST_Constant
 | |
|                     && self.right instanceof AST_Binary
 | |
|                     && self.right.operator == "+"
 | |
|                     && self.right.left instanceof AST_Constant
 | |
|                     && self.right.is_string(compressor)) {
 | |
|                     self = make_node(AST_Binary, self, {
 | |
|                         operator: "+",
 | |
|                         left: make_node(AST_String, null, {
 | |
|                             value: "" + self.left.getValue() + self.right.left.getValue(),
 | |
|                             start: self.left.start,
 | |
|                             end: self.right.left.end
 | |
|                         }),
 | |
|                         right: self.right.right
 | |
|                     });
 | |
|                 }
 | |
|                 if (self.right instanceof AST_Constant
 | |
|                     && self.left instanceof AST_Binary
 | |
|                     && self.left.operator == "+"
 | |
|                     && self.left.right instanceof AST_Constant
 | |
|                     && self.left.is_string(compressor)) {
 | |
|                     self = make_node(AST_Binary, self, {
 | |
|                         operator: "+",
 | |
|                         left: self.left.left,
 | |
|                         right: make_node(AST_String, null, {
 | |
|                             value: "" + self.left.right.getValue() + self.right.getValue(),
 | |
|                             start: self.left.right.start,
 | |
|                             end: self.right.end
 | |
|                         })
 | |
|                     });
 | |
|                 }
 | |
|                 if (self.left instanceof AST_Binary
 | |
|                     && self.left.operator == "+"
 | |
|                     && self.left.is_string(compressor)
 | |
|                     && self.left.right instanceof AST_Constant
 | |
|                     && self.right instanceof AST_Binary
 | |
|                     && self.right.operator == "+"
 | |
|                     && self.right.left instanceof AST_Constant
 | |
|                     && self.right.is_string(compressor)) {
 | |
|                     self = make_node(AST_Binary, self, {
 | |
|                         operator: "+",
 | |
|                         left: make_node(AST_Binary, self.left, {
 | |
|                             operator: "+",
 | |
|                             left: self.left.left,
 | |
|                             right: make_node(AST_String, null, {
 | |
|                                 value: "" + self.left.right.getValue() + self.right.left.getValue(),
 | |
|                                 start: self.left.right.start,
 | |
|                                 end: self.right.left.end
 | |
|                             })
 | |
|                         }),
 | |
|                         right: self.right.right
 | |
|                     });
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         // x * (y * z)  ==>  x * y * z
 | |
|         if (self.right instanceof AST_Binary
 | |
|             && self.right.operator == self.operator
 | |
|             && (self.operator == "*" || self.operator == "&&" || self.operator == "||"))
 | |
|         {
 | |
|             self.left = make_node(AST_Binary, self.left, {
 | |
|                 operator : self.operator,
 | |
|                 left     : self.left,
 | |
|                 right    : self.right.left
 | |
|             });
 | |
|             self.right = self.right.right;
 | |
|             return self.transform(compressor);
 | |
|         }
 | |
|         return self.evaluate(compressor)[0];
 | |
|     });
 | |
| 
 | |
|     OPT(AST_SymbolRef, function(self, compressor){
 | |
|         if (self.undeclared()) {
 | |
|             var defines = compressor.option("global_defs");
 | |
|             if (defines && defines.hasOwnProperty(self.name)) {
 | |
|                 return make_node_from_constant(compressor, defines[self.name], self);
 | |
|             }
 | |
|             switch (self.name) {
 | |
|               case "undefined":
 | |
|                 return make_node(AST_Undefined, self);
 | |
|               case "NaN":
 | |
|                 return make_node(AST_NaN, self);
 | |
|               case "Infinity":
 | |
|                 return make_node(AST_Infinity, self);
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Undefined, function(self, compressor){
 | |
|         if (compressor.option("unsafe")) {
 | |
|             var scope = compressor.find_parent(AST_Scope);
 | |
|             var undef = scope.find_variable("undefined");
 | |
|             if (undef) {
 | |
|                 var ref = make_node(AST_SymbolRef, self, {
 | |
|                     name   : "undefined",
 | |
|                     scope  : scope,
 | |
|                     thedef : undef
 | |
|                 });
 | |
|                 ref.reference();
 | |
|                 return ref;
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
 | |
|     OPT(AST_Assign, function(self, compressor){
 | |
|         self = self.lift_sequences(compressor);
 | |
|         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)) {
 | |
|             self.operator = self.right.operator + "=";
 | |
|             self.right = self.right.right;
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Conditional, function(self, compressor){
 | |
|         if (!compressor.option("conditionals")) return self;
 | |
|         if (self.condition instanceof AST_Seq) {
 | |
|             var car = self.condition.car;
 | |
|             self.condition = self.condition.cdr;
 | |
|             return AST_Seq.cons(car, self);
 | |
|         }
 | |
|         var cond = self.condition.evaluate(compressor);
 | |
|         if (cond.length > 1) {
 | |
|             if (cond[1]) {
 | |
|                 compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
 | |
|                 return self.consequent;
 | |
|             } else {
 | |
|                 compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
 | |
|                 return self.alternative;
 | |
|             }
 | |
|         }
 | |
|         var negated = cond[0].negate(compressor);
 | |
|         if (best_of(cond[0], negated) === negated) {
 | |
|             self = make_node(AST_Conditional, self, {
 | |
|                 condition: negated,
 | |
|                 consequent: self.alternative,
 | |
|                 alternative: self.consequent
 | |
|             });
 | |
|         }
 | |
|         var consequent = self.consequent;
 | |
|         var alternative = self.alternative;
 | |
|         if (consequent instanceof AST_Assign
 | |
|             && alternative instanceof AST_Assign
 | |
|             && consequent.operator == alternative.operator
 | |
|             && consequent.left.equivalent_to(alternative.left)
 | |
|            ) {
 | |
|             /*
 | |
|              * Stuff like this:
 | |
|              * if (foo) exp = something; else exp = something_else;
 | |
|              * ==>
 | |
|              * exp = foo ? something : something_else;
 | |
|              */
 | |
|             self = make_node(AST_Assign, self, {
 | |
|                 operator: consequent.operator,
 | |
|                 left: consequent.left,
 | |
|                 right: make_node(AST_Conditional, self, {
 | |
|                     condition: self.condition,
 | |
|                     consequent: consequent.right,
 | |
|                     alternative: alternative.right
 | |
|                 })
 | |
|             });
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Boolean, function(self, compressor){
 | |
|         if (compressor.option("booleans")) {
 | |
|             var p = compressor.parent();
 | |
|             if (p instanceof AST_Binary && (p.operator == "=="
 | |
|                                             || p.operator == "!=")) {
 | |
|                 compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
 | |
|                     operator : p.operator,
 | |
|                     value    : self.value,
 | |
|                     file     : p.start.file,
 | |
|                     line     : p.start.line,
 | |
|                     col      : p.start.col,
 | |
|                 });
 | |
|                 return make_node(AST_Number, self, {
 | |
|                     value: +self.value
 | |
|                 });
 | |
|             }
 | |
|             return make_node(AST_UnaryPrefix, self, {
 | |
|                 operator: "!",
 | |
|                 expression: make_node(AST_Number, self, {
 | |
|                     value: 1 - self.value
 | |
|                 })
 | |
|             });
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Sub, function(self, compressor){
 | |
|         var prop = self.property;
 | |
|         if (prop instanceof AST_String && compressor.option("properties")) {
 | |
|             prop = prop.getValue();
 | |
|             if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) {
 | |
|                 return make_node(AST_Dot, self, {
 | |
|                     expression : self.expression,
 | |
|                     property   : prop
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     function literals_in_boolean_context(self, compressor) {
 | |
|         if (compressor.option("booleans") && compressor.in_boolean_context()) {
 | |
|             return make_node(AST_True, self);
 | |
|         }
 | |
|         return self;
 | |
|     };
 | |
|     OPT(AST_Array, literals_in_boolean_context);
 | |
|     OPT(AST_Object, literals_in_boolean_context);
 | |
|     OPT(AST_RegExp, literals_in_boolean_context);
 | |
| 
 | |
| })();
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| // a small wrapper around fitzgen's source-map library
 | |
| function SourceMap(options) {
 | |
|     options = defaults(options, {
 | |
|         file : null,
 | |
|         root : null,
 | |
|         orig : null,
 | |
| 
 | |
|         orig_line_diff : 0,
 | |
|         dest_line_diff : 0,
 | |
|     });
 | |
|     var generator = new MOZ_SourceMap.SourceMapGenerator({
 | |
|         file       : options.file,
 | |
|         sourceRoot : options.root
 | |
|     });
 | |
|     var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig);
 | |
|     function add(source, gen_line, gen_col, orig_line, orig_col, name) {
 | |
|         if (orig_map) {
 | |
|             var info = orig_map.originalPositionFor({
 | |
|                 line: orig_line,
 | |
|                 column: orig_col
 | |
|             });
 | |
|             source = info.source;
 | |
|             orig_line = info.line;
 | |
|             orig_col = info.column;
 | |
|             name = info.name;
 | |
|         }
 | |
|         generator.addMapping({
 | |
|             generated : { line: gen_line + options.dest_line_diff, column: gen_col },
 | |
|             original  : { line: orig_line + options.orig_line_diff, column: orig_col },
 | |
|             source    : source,
 | |
|             name      : name
 | |
|         });
 | |
|     };
 | |
|     return {
 | |
|         add        : add,
 | |
|         get        : function() { return generator },
 | |
|         toString   : function() { return generator.toString() }
 | |
|     };
 | |
| };
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| (function(){
 | |
| 
 | |
|     var MOZ_TO_ME = {
 | |
|         TryStatement : function(M) {
 | |
|             return new AST_Try({
 | |
|                 start    : my_start_token(M),
 | |
|                 end      : my_end_token(M),
 | |
|                 body     : from_moz(M.block).body,
 | |
|                 bcatch   : from_moz(M.handlers[0]),
 | |
|                 bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
 | |
|             });
 | |
|         },
 | |
|         CatchClause : function(M) {
 | |
|             return new AST_Catch({
 | |
|                 start   : my_start_token(M),
 | |
|                 end     : my_end_token(M),
 | |
|                 argname : from_moz(M.param),
 | |
|                 body    : from_moz(M.body).body
 | |
|             });
 | |
|         },
 | |
|         ObjectExpression : function(M) {
 | |
|             return new AST_Object({
 | |
|                 start      : my_start_token(M),
 | |
|                 end        : my_end_token(M),
 | |
|                 properties : M.properties.map(function(prop){
 | |
|                     var key = prop.key;
 | |
|                     var name = key.type == "Identifier" ? key.name : key.value;
 | |
|                     var args = {
 | |
|                         start    : my_start_token(key),
 | |
|                         end      : my_end_token(prop.value),
 | |
|                         key      : name,
 | |
|                         value    : from_moz(prop.value)
 | |
|                     };
 | |
|                     switch (prop.kind) {
 | |
|                       case "init":
 | |
|                         return new AST_ObjectKeyVal(args);
 | |
|                       case "set":
 | |
|                         args.value.name = from_moz(key);
 | |
|                         return new AST_ObjectSetter(args);
 | |
|                       case "get":
 | |
|                         args.value.name = from_moz(key);
 | |
|                         return new AST_ObjectGetter(args);
 | |
|                     }
 | |
|                 })
 | |
|             });
 | |
|         },
 | |
|         SequenceExpression : function(M) {
 | |
|             return AST_Seq.from_array(M.expressions.map(from_moz));
 | |
|         },
 | |
|         MemberExpression : function(M) {
 | |
|             return new (M.computed ? AST_Sub : AST_Dot)({
 | |
|                 start      : my_start_token(M),
 | |
|                 end        : my_end_token(M),
 | |
|                 property   : M.computed ? from_moz(M.property) : M.property.name,
 | |
|                 expression : from_moz(M.object)
 | |
|             });
 | |
|         },
 | |
|         SwitchCase : function(M) {
 | |
|             return new (M.test ? AST_Case : AST_Default)({
 | |
|                 start      : my_start_token(M),
 | |
|                 end        : my_end_token(M),
 | |
|                 expression : from_moz(M.test),
 | |
|                 body       : M.consequent.map(from_moz)
 | |
|             });
 | |
|         },
 | |
|         Literal : function(M) {
 | |
|             var val = M.value, args = {
 | |
|                 start  : my_start_token(M),
 | |
|                 end    : my_end_token(M)
 | |
|             };
 | |
|             if (val === null) return new AST_Null(args);
 | |
|             switch (typeof val) {
 | |
|               case "string":
 | |
|                 args.value = val;
 | |
|                 return new AST_String(args);
 | |
|               case "number":
 | |
|                 args.value = val;
 | |
|                 return new AST_Number(args);
 | |
|               case "boolean":
 | |
|                 return new (val ? AST_True : AST_False)(args);
 | |
|               default:
 | |
|                 args.value = val;
 | |
|                 return new AST_RegExp(args);
 | |
|             }
 | |
|         },
 | |
|         UnaryExpression: From_Moz_Unary,
 | |
|         UpdateExpression: From_Moz_Unary,
 | |
|         Identifier: function(M) {
 | |
|             var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
 | |
|             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)({
 | |
|                             start : my_start_token(M),
 | |
|                             end   : my_end_token(M),
 | |
|                             name  : M.name
 | |
|                         });
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function From_Moz_Unary(M) {
 | |
|         var prefix = "prefix" in M ? M.prefix
 | |
|             : M.type == "UnaryExpression" ? true : false;
 | |
|         return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
 | |
|             start      : my_start_token(M),
 | |
|             end        : my_end_token(M),
 | |
|             operator   : M.operator,
 | |
|             expression : from_moz(M.argument)
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     var ME_TO_MOZ = {};
 | |
| 
 | |
|     map("Node", AST_Node);
 | |
|     map("Program", AST_Toplevel, "body@body");
 | |
|     map("Function", AST_Function, "id>name, params@argnames, body%body");
 | |
|     map("EmptyStatement", AST_EmptyStatement);
 | |
|     map("BlockStatement", AST_BlockStatement, "body@body");
 | |
|     map("ExpressionStatement", AST_SimpleStatement, "expression>body");
 | |
|     map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
 | |
|     map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
 | |
|     map("BreakStatement", AST_Break, "label>label");
 | |
|     map("ContinueStatement", AST_Continue, "label>label");
 | |
|     map("WithStatement", AST_With, "object>expression, body>body");
 | |
|     map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body");
 | |
|     map("ReturnStatement", AST_Return, "argument>value");
 | |
|     map("ThrowStatement", AST_Throw, "argument>value");
 | |
|     map("WhileStatement", AST_While, "test>condition, body>body");
 | |
|     map("DoWhileStatement", AST_Do, "test>condition, body>body");
 | |
|     map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body");
 | |
|     map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
 | |
|     map("DebuggerStatement", AST_Debugger);
 | |
|     map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body");
 | |
|     map("VariableDeclaration", AST_Var, "declarations@definitions");
 | |
|     map("VariableDeclarator", AST_VarDef, "id>name, init>value");
 | |
| 
 | |
|     map("ThisExpression", AST_This);
 | |
|     map("ArrayExpression", AST_Array, "elements@elements");
 | |
|     map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body");
 | |
|     map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
 | |
|     map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
 | |
|     map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
 | |
|     map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
 | |
|     map("NewExpression", AST_New, "callee>expression, arguments@args");
 | |
|     map("CallExpression", AST_Call, "callee>expression, arguments@args");
 | |
| 
 | |
|     /* -----[ tools ]----- */
 | |
| 
 | |
|     function my_start_token(moznode) {
 | |
|         return new AST_Token({
 | |
|             file   : moznode.loc && moznode.loc.source,
 | |
|             line   : moznode.loc && moznode.loc.start.line,
 | |
|             col    : moznode.loc && moznode.loc.start.column,
 | |
|             pos    : moznode.start,
 | |
|             endpos : moznode.start
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function my_end_token(moznode) {
 | |
|         return new AST_Token({
 | |
|             file   : moznode.loc && moznode.loc.source,
 | |
|             line   : moznode.loc && moznode.loc.end.line,
 | |
|             col    : moznode.loc && moznode.loc.end.column,
 | |
|             pos    : moznode.end,
 | |
|             endpos : moznode.end
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function map(moztype, mytype, propmap) {
 | |
|         var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
 | |
|         moz_to_me += "return new mytype({\n" +
 | |
|             "start: my_start_token(M),\n" +
 | |
|             "end: my_end_token(M)";
 | |
| 
 | |
|         if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){
 | |
|             var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
 | |
|             if (!m) throw new Error("Can't understand property map: " + prop);
 | |
|             var moz = "M." + m[1], how = m[2], my = m[3];
 | |
|             moz_to_me += ",\n" + my + ": ";
 | |
|             if (how == "@") {
 | |
|                 moz_to_me += moz + ".map(from_moz)";
 | |
|             } else if (how == ">") {
 | |
|                 moz_to_me += "from_moz(" + moz + ")";
 | |
|             } else if (how == "=") {
 | |
|                 moz_to_me += moz;
 | |
|             } else if (how == "%") {
 | |
|                 moz_to_me += "from_moz(" + moz + ").body";
 | |
|             } else throw new Error("Can't understand operator in propmap: " + prop);
 | |
|         });
 | |
|         moz_to_me += "\n})}";
 | |
| 
 | |
|         // moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
 | |
|         // console.log(moz_to_me);
 | |
| 
 | |
|         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
 | |
|         );
 | |
|         return MOZ_TO_ME[moztype] = moz_to_me;
 | |
|     };
 | |
| 
 | |
|     var FROM_MOZ_STACK = null;
 | |
| 
 | |
|     function from_moz(node) {
 | |
|         FROM_MOZ_STACK.push(node);
 | |
|         var ret = node != null ? MOZ_TO_ME[node.type](node) : null;
 | |
|         FROM_MOZ_STACK.pop();
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     AST_Node.from_mozilla_ast = function(node){
 | |
|         var save_stack = FROM_MOZ_STACK;
 | |
|         FROM_MOZ_STACK = [];
 | |
|         var ast = from_moz(node);
 | |
|         FROM_MOZ_STACK = save_stack;
 | |
|         return ast;
 | |
|     };
 | |
| 
 | |
| })();
 | |
| 
 | |
| 
 | |
| exports.sys = sys;
 | |
| exports.MOZ_SourceMap = MOZ_SourceMap;
 | |
| exports.UglifyJS = UglifyJS;
 | |
| exports.array_to_hash = array_to_hash;
 | |
| exports.slice = slice;
 | |
| exports.characters = characters;
 | |
| exports.member = member;
 | |
| exports.find_if = find_if;
 | |
| exports.repeat_string = repeat_string;
 | |
| exports.DefaultsError = DefaultsError;
 | |
| exports.defaults = defaults;
 | |
| exports.merge = merge;
 | |
| exports.noop = noop;
 | |
| exports.MAP = MAP;
 | |
| exports.push_uniq = push_uniq;
 | |
| exports.string_template = string_template;
 | |
| exports.remove = remove;
 | |
| exports.mergeSort = mergeSort;
 | |
| exports.set_difference = set_difference;
 | |
| exports.set_intersection = set_intersection;
 | |
| exports.makePredicate = makePredicate;
 | |
| exports.all = all;
 | |
| exports.Dictionary = Dictionary;
 | |
| exports.DEFNODE = DEFNODE;
 | |
| exports.AST_Token = AST_Token;
 | |
| exports.AST_Node = AST_Node;
 | |
| exports.AST_Statement = AST_Statement;
 | |
| exports.AST_Debugger = AST_Debugger;
 | |
| exports.AST_Directive = AST_Directive;
 | |
| exports.AST_SimpleStatement = AST_SimpleStatement;
 | |
| exports.walk_body = walk_body;
 | |
| exports.AST_Block = AST_Block;
 | |
| exports.AST_BlockStatement = AST_BlockStatement;
 | |
| exports.AST_EmptyStatement = AST_EmptyStatement;
 | |
| exports.AST_StatementWithBody = AST_StatementWithBody;
 | |
| exports.AST_LabeledStatement = AST_LabeledStatement;
 | |
| exports.AST_IterationStatement = AST_IterationStatement;
 | |
| exports.AST_DWLoop = AST_DWLoop;
 | |
| exports.AST_Do = AST_Do;
 | |
| exports.AST_While = AST_While;
 | |
| exports.AST_For = AST_For;
 | |
| exports.AST_ForIn = AST_ForIn;
 | |
| exports.AST_With = AST_With;
 | |
| exports.AST_Scope = AST_Scope;
 | |
| exports.AST_Toplevel = AST_Toplevel;
 | |
| exports.AST_Lambda = AST_Lambda;
 | |
| exports.AST_Accessor = AST_Accessor;
 | |
| exports.AST_Function = AST_Function;
 | |
| exports.AST_Defun = AST_Defun;
 | |
| exports.AST_Jump = AST_Jump;
 | |
| exports.AST_Exit = AST_Exit;
 | |
| exports.AST_Return = AST_Return;
 | |
| exports.AST_Throw = AST_Throw;
 | |
| exports.AST_LoopControl = AST_LoopControl;
 | |
| exports.AST_Break = AST_Break;
 | |
| exports.AST_Continue = AST_Continue;
 | |
| exports.AST_If = AST_If;
 | |
| exports.AST_Switch = AST_Switch;
 | |
| exports.AST_SwitchBranch = AST_SwitchBranch;
 | |
| exports.AST_Default = AST_Default;
 | |
| exports.AST_Case = AST_Case;
 | |
| exports.AST_Try = AST_Try;
 | |
| exports.AST_Catch = AST_Catch;
 | |
| exports.AST_Finally = AST_Finally;
 | |
| exports.AST_Definitions = AST_Definitions;
 | |
| exports.AST_Var = AST_Var;
 | |
| exports.AST_Const = AST_Const;
 | |
| exports.AST_VarDef = AST_VarDef;
 | |
| exports.AST_Call = AST_Call;
 | |
| exports.AST_New = AST_New;
 | |
| exports.AST_Seq = AST_Seq;
 | |
| exports.AST_PropAccess = AST_PropAccess;
 | |
| exports.AST_Dot = AST_Dot;
 | |
| exports.AST_Sub = AST_Sub;
 | |
| exports.AST_Unary = AST_Unary;
 | |
| exports.AST_UnaryPrefix = AST_UnaryPrefix;
 | |
| exports.AST_UnaryPostfix = AST_UnaryPostfix;
 | |
| exports.AST_Binary = AST_Binary;
 | |
| exports.AST_Conditional = AST_Conditional;
 | |
| exports.AST_Assign = AST_Assign;
 | |
| exports.AST_Array = AST_Array;
 | |
| exports.AST_Object = AST_Object;
 | |
| exports.AST_ObjectProperty = AST_ObjectProperty;
 | |
| exports.AST_ObjectKeyVal = AST_ObjectKeyVal;
 | |
| exports.AST_ObjectSetter = AST_ObjectSetter;
 | |
| exports.AST_ObjectGetter = AST_ObjectGetter;
 | |
| exports.AST_Symbol = AST_Symbol;
 | |
| exports.AST_SymbolAccessor = AST_SymbolAccessor;
 | |
| exports.AST_SymbolDeclaration = AST_SymbolDeclaration;
 | |
| exports.AST_SymbolVar = AST_SymbolVar;
 | |
| exports.AST_SymbolConst = AST_SymbolConst;
 | |
| exports.AST_SymbolFunarg = AST_SymbolFunarg;
 | |
| exports.AST_SymbolDefun = AST_SymbolDefun;
 | |
| exports.AST_SymbolLambda = AST_SymbolLambda;
 | |
| exports.AST_SymbolCatch = AST_SymbolCatch;
 | |
| exports.AST_Label = AST_Label;
 | |
| exports.AST_SymbolRef = AST_SymbolRef;
 | |
| exports.AST_LabelRef = AST_LabelRef;
 | |
| exports.AST_This = AST_This;
 | |
| exports.AST_Constant = AST_Constant;
 | |
| exports.AST_String = AST_String;
 | |
| exports.AST_Number = AST_Number;
 | |
| exports.AST_RegExp = AST_RegExp;
 | |
| exports.AST_Atom = AST_Atom;
 | |
| exports.AST_Null = AST_Null;
 | |
| exports.AST_NaN = AST_NaN;
 | |
| exports.AST_Undefined = AST_Undefined;
 | |
| exports.AST_Hole = AST_Hole;
 | |
| exports.AST_Infinity = AST_Infinity;
 | |
| exports.AST_Boolean = AST_Boolean;
 | |
| exports.AST_False = AST_False;
 | |
| exports.AST_True = AST_True;
 | |
| exports.TreeWalker = TreeWalker;
 | |
| exports.KEYWORDS = KEYWORDS;
 | |
| exports.KEYWORDS_ATOM = KEYWORDS_ATOM;
 | |
| exports.RESERVED_WORDS = RESERVED_WORDS;
 | |
| exports.KEYWORDS_BEFORE_EXPRESSION = KEYWORDS_BEFORE_EXPRESSION;
 | |
| exports.OPERATOR_CHARS = OPERATOR_CHARS;
 | |
| exports.RE_HEX_NUMBER = RE_HEX_NUMBER;
 | |
| exports.RE_OCT_NUMBER = RE_OCT_NUMBER;
 | |
| exports.RE_DEC_NUMBER = RE_DEC_NUMBER;
 | |
| exports.OPERATORS = OPERATORS;
 | |
| exports.WHITESPACE_CHARS = WHITESPACE_CHARS;
 | |
| exports.PUNC_BEFORE_EXPRESSION = PUNC_BEFORE_EXPRESSION;
 | |
| exports.PUNC_CHARS = PUNC_CHARS;
 | |
| exports.REGEXP_MODIFIERS = REGEXP_MODIFIERS;
 | |
| exports.UNICODE = UNICODE;
 | |
| exports.is_letter = is_letter;
 | |
| exports.is_digit = is_digit;
 | |
| exports.is_alphanumeric_char = is_alphanumeric_char;
 | |
| exports.is_unicode_combining_mark = is_unicode_combining_mark;
 | |
| exports.is_unicode_connector_punctuation = is_unicode_connector_punctuation;
 | |
| exports.is_identifier = is_identifier;
 | |
| exports.is_identifier_start = is_identifier_start;
 | |
| exports.is_identifier_char = is_identifier_char;
 | |
| exports.is_identifier_string = is_identifier_string;
 | |
| exports.parse_js_number = parse_js_number;
 | |
| exports.JS_Parse_Error = JS_Parse_Error;
 | |
| exports.js_error = js_error;
 | |
| exports.is_token = is_token;
 | |
| exports.EX_EOF = EX_EOF;
 | |
| exports.tokenizer = tokenizer;
 | |
| exports.UNARY_PREFIX = UNARY_PREFIX;
 | |
| exports.UNARY_POSTFIX = UNARY_POSTFIX;
 | |
| exports.ASSIGNMENT = ASSIGNMENT;
 | |
| exports.PRECEDENCE = PRECEDENCE;
 | |
| exports.STATEMENTS_WITH_LABELS = STATEMENTS_WITH_LABELS;
 | |
| exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;
 | |
| exports.parse = parse;
 | |
| exports.TreeTransformer = TreeTransformer;
 | |
| exports.SymbolDef = SymbolDef;
 | |
| exports.base54 = base54;
 | |
| exports.OutputStream = OutputStream;
 | |
| exports.Compressor = Compressor;
 | |
| exports.SourceMap = SourceMap;
 | |
| 
 | |
| exports.AST_Node.warn_function = function (txt) { if (typeof console != "undefined" && typeof console.warn === "function") console.warn(txt) }
 | |
| 
 | |
| exports.minify = function (files, options) {
 | |
|     options = UglifyJS.defaults(options, {
 | |
|         outSourceMap : null,
 | |
|         sourceRoot   : null,
 | |
|         inSourceMap  : null,
 | |
|         fromString   : false,
 | |
|         warnings     : false,
 | |
|         mangle       : {},
 | |
|         output       : null,
 | |
|         compress     : {}
 | |
|     });
 | |
|     if (typeof files == "string")
 | |
|         files = [ files ];
 | |
| 
 | |
|     UglifyJS.base54.reset();
 | |
| 
 | |
|     // 1. parse
 | |
|     var toplevel = null;
 | |
|     files.forEach(function(file){
 | |
|         var code = options.fromString
 | |
|             ? file
 | |
|             : fs.readFileSync(file, "utf8");
 | |
|         toplevel = UglifyJS.parse(code, {
 | |
|             filename: options.fromString ? "?" : file,
 | |
|             toplevel: toplevel
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     // 2. compress
 | |
|     if (options.compress) {
 | |
|         var compress = { warnings: options.warnings };
 | |
|         UglifyJS.merge(compress, options.compress);
 | |
|         toplevel.figure_out_scope();
 | |
|         var sq = UglifyJS.Compressor(compress);
 | |
|         toplevel = toplevel.transform(sq);
 | |
|     }
 | |
| 
 | |
|     // 3. mangle
 | |
|     if (options.mangle) {
 | |
|         toplevel.figure_out_scope();
 | |
|         toplevel.compute_char_frequency();
 | |
|         toplevel.mangle_names(options.mangle);
 | |
|     }
 | |
| 
 | |
|     // 4. output
 | |
|     var inMap = options.inSourceMap;
 | |
|     var output = {};
 | |
|     if (typeof options.inSourceMap == "string") {
 | |
|         inMap = fs.readFileSync(options.inSourceMap, "utf8");
 | |
|     }
 | |
|     if (options.outSourceMap) {
 | |
|         output.source_map = UglifyJS.SourceMap({
 | |
|             file: options.outSourceMap,
 | |
|             orig: inMap,
 | |
|             root: options.sourceRoot
 | |
|         });
 | |
|     }
 | |
|     if (options.output) {
 | |
|         UglifyJS.merge(output, options.output);
 | |
|     }
 | |
|     var stream = UglifyJS.OutputStream(output);
 | |
|     toplevel.print(stream);
 | |
|     return {
 | |
|         code : stream + "",
 | |
|         map  : output.source_map + ""
 | |
|     };
 | |
| };
 | |
| 
 | |
| exports.describe_ast = function () {
 | |
|     var out = UglifyJS.OutputStream({ beautify: true });
 | |
|     function doitem(ctor) {
 | |
|         out.print("AST_" + ctor.TYPE);
 | |
|         var props = ctor.SELF_PROPS.filter(function(prop){
 | |
|             return !/^\$/.test(prop);
 | |
|         });
 | |
|         if (props.length > 0) {
 | |
|             out.space();
 | |
|             out.with_parens(function(){
 | |
|                 props.forEach(function(prop, i){
 | |
|                     if (i) out.space();
 | |
|                     out.print(prop);
 | |
|                 });
 | |
|             });
 | |
|         }
 | |
|         if (ctor.documentation) {
 | |
|             out.space();
 | |
|             out.print_string(ctor.documentation);
 | |
|         }
 | |
|         if (ctor.SUBCLASSES.length > 0) {
 | |
|             out.space();
 | |
|             out.with_block(function(){
 | |
|                 ctor.SUBCLASSES.forEach(function(ctor, i){
 | |
|                     out.indent();
 | |
|                     doitem(ctor);
 | |
|                     out.newline();
 | |
|                 });
 | |
|             });
 | |
|         }
 | |
|     };
 | |
|     doitem(UglifyJS.AST_Node);
 | |
|     return out + "";
 | |
| };
 | |
| },{"source-map":35,"util":32}],46:[function(require,module,exports){
 | |
| var uglify = require('uglify-js')
 | |
| var globalVars = require('./vars')
 | |
| 
 | |
| module.exports = addWith
 | |
| 
 | |
| function addWith(obj, src, exclude, environments) {
 | |
|   environments = environments || ['reservedVars', 'ecmaIdentifiers', 'nonstandard', 'node']
 | |
|   exclude = exclude || []
 | |
|   exclude = exclude.concat(detect(obj))
 | |
|   var vars = detect('(function () {' + src + '}())')//allows the `return` keyword
 | |
|     .filter(function (v) {
 | |
|       for (var i = 0; i < environments.length; i++) {
 | |
|         if (v in globalVars[environments[i]]) {
 | |
|           return false;
 | |
|         }
 | |
|       }
 | |
|       return exclude.indexOf(v) === -1
 | |
|     })
 | |
| 
 | |
|   if (vars.length === 0) return src
 | |
| 
 | |
|   var declareLocal = ''
 | |
|   var local = 'locals'
 | |
|   if (/^[a-zA-Z0-9$_]+$/.test(obj)) {
 | |
|     local = obj
 | |
|   } else {
 | |
|     while (vars.indexOf(local) != -1 || exclude.indexOf(local) != -1) {
 | |
|       local += '_'
 | |
|     }
 | |
|     declareLocal = local + ' = (' + obj + '),'
 | |
|   }
 | |
|   return 'var ' + declareLocal + vars
 | |
|     .map(function (v) {
 | |
|       return v + ' = ' + local + '.' + v
 | |
|     }).join(',') + ';' + src
 | |
| }
 | |
| 
 | |
| function detect(src) {
 | |
|     var ast = uglify.parse(src.toString())
 | |
|     ast.figure_out_scope()
 | |
|     var globals = ast.globals
 | |
|         .map(function (node, name) {
 | |
|             return name
 | |
|         })
 | |
|     return globals;
 | |
| }
 | |
| },{"./vars":58,"uglify-js":57}],47:[function(require,module,exports){
 | |
| arguments[4][35][0].apply(exports,arguments)
 | |
| },{"./source-map/source-map-consumer":52,"./source-map/source-map-generator":53,"./source-map/source-node":54}],48:[function(require,module,exports){
 | |
| arguments[4][36][0].apply(exports,arguments)
 | |
| },{"./util":55,"amdefine":56}],49:[function(require,module,exports){
 | |
| arguments[4][37][0].apply(exports,arguments)
 | |
| },{"./base64":50,"amdefine":56}],50:[function(require,module,exports){
 | |
| arguments[4][38][0].apply(exports,arguments)
 | |
| },{"amdefine":56}],51:[function(require,module,exports){
 | |
| arguments[4][39][0].apply(exports,arguments)
 | |
| },{"amdefine":56}],52:[function(require,module,exports){
 | |
| arguments[4][40][0].apply(exports,arguments)
 | |
| },{"./array-set":48,"./base64-vlq":49,"./binary-search":51,"./util":55,"amdefine":56}],53:[function(require,module,exports){
 | |
| arguments[4][41][0].apply(exports,arguments)
 | |
| },{"./array-set":48,"./base64-vlq":49,"./util":55,"amdefine":56}],54:[function(require,module,exports){
 | |
| arguments[4][42][0].apply(exports,arguments)
 | |
| },{"./source-map-generator":53,"./util":55,"amdefine":56}],55:[function(require,module,exports){
 | |
| arguments[4][43][0].apply(exports,arguments)
 | |
| },{"amdefine":56}],56:[function(require,module,exports){
 | |
| var process=require("__browserify_process"),__filename="/../node_modules/with/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/amdefine.js";/** vim: et:ts=4:sw=4:sts=4
 | |
|  * @license amdefine 0.1.0 Copyright (c) 2011, The Dojo Foundation All Rights Reserved.
 | |
|  * Available via the MIT or new BSD license.
 | |
|  * see: http://github.com/jrburke/amdefine for details
 | |
|  */
 | |
| 
 | |
| /*jslint node: true */
 | |
| /*global module, process */
 | |
| 'use strict';
 | |
| 
 | |
| /**
 | |
|  * Creates a define for node.
 | |
|  * @param {Object} module the "module" object that is defined by Node for the
 | |
|  * current module.
 | |
|  * @param {Function} [requireFn]. Node's require function for the current module.
 | |
|  * It only needs to be passed in Node versions before 0.5, when module.require
 | |
|  * did not exist.
 | |
|  * @returns {Function} a define function that is usable for the current node
 | |
|  * module.
 | |
|  */
 | |
| function amdefine(module, requireFn) {
 | |
|     'use strict';
 | |
|     var defineCache = {},
 | |
|         loaderCache = {},
 | |
|         alreadyCalled = false,
 | |
|         path = require('path'),
 | |
|         makeRequire, stringRequire;
 | |
| 
 | |
|     /**
 | |
|      * Trims the . and .. from an array of path segments.
 | |
|      * It will keep a leading path segment if a .. will become
 | |
|      * the first path segment, to help with module name lookups,
 | |
|      * which act like paths, but can be remapped. But the end result,
 | |
|      * all paths that use this function should look normalized.
 | |
|      * NOTE: this method MODIFIES the input array.
 | |
|      * @param {Array} ary the array of path segments.
 | |
|      */
 | |
|     function trimDots(ary) {
 | |
|         var i, part;
 | |
|         for (i = 0; ary[i]; i+= 1) {
 | |
|             part = ary[i];
 | |
|             if (part === '.') {
 | |
|                 ary.splice(i, 1);
 | |
|                 i -= 1;
 | |
|             } else if (part === '..') {
 | |
|                 if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
 | |
|                     //End of the line. Keep at least one non-dot
 | |
|                     //path segment at the front so it can be mapped
 | |
|                     //correctly to disk. Otherwise, there is likely
 | |
|                     //no path mapping for a path starting with '..'.
 | |
|                     //This can still fail, but catches the most reasonable
 | |
|                     //uses of ..
 | |
|                     break;
 | |
|                 } else if (i > 0) {
 | |
|                     ary.splice(i - 1, 2);
 | |
|                     i -= 2;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     function normalize(name, baseName) {
 | |
|         var baseParts;
 | |
| 
 | |
|         //Adjust any relative paths.
 | |
|         if (name && name.charAt(0) === '.') {
 | |
|             //If have a base name, try to normalize against it,
 | |
|             //otherwise, assume it is a top-level require that will
 | |
|             //be relative to baseUrl in the end.
 | |
|             if (baseName) {
 | |
|                 baseParts = baseName.split('/');
 | |
|                 baseParts = baseParts.slice(0, baseParts.length - 1);
 | |
|                 baseParts = baseParts.concat(name.split('/'));
 | |
|                 trimDots(baseParts);
 | |
|                 name = baseParts.join('/');
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return name;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Create the normalize() function passed to a loader plugin's
 | |
|      * normalize method.
 | |
|      */
 | |
|     function makeNormalize(relName) {
 | |
|         return function (name) {
 | |
|             return normalize(name, relName);
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     function makeLoad(id) {
 | |
|         function load(value) {
 | |
|             loaderCache[id] = value;
 | |
|         }
 | |
| 
 | |
|         load.fromText = function (id, text) {
 | |
|             //This one is difficult because the text can/probably uses
 | |
|             //define, and any relative paths and requires should be relative
 | |
|             //to that id was it would be found on disk. But this would require
 | |
|             //bootstrapping a module/require fairly deeply from node core.
 | |
|             //Not sure how best to go about that yet.
 | |
|             throw new Error('amdefine does not implement load.fromText');
 | |
|         };
 | |
| 
 | |
|         return load;
 | |
|     }
 | |
| 
 | |
|     makeRequire = function (systemRequire, exports, module, relId) {
 | |
|         function amdRequire(deps, callback) {
 | |
|             if (typeof deps === 'string') {
 | |
|                 //Synchronous, single module require('')
 | |
|                 return stringRequire(systemRequire, exports, module, deps, relId);
 | |
|             } else {
 | |
|                 //Array of dependencies with a callback.
 | |
| 
 | |
|                 //Convert the dependencies to modules.
 | |
|                 deps = deps.map(function (depName) {
 | |
|                     return stringRequire(systemRequire, exports, module, depName, relId);
 | |
|                 });
 | |
| 
 | |
|                 //Wait for next tick to call back the require call.
 | |
|                 process.nextTick(function () {
 | |
|                     callback.apply(null, deps);
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         amdRequire.toUrl = function (filePath) {
 | |
|             if (filePath.indexOf('.') === 0) {
 | |
|                 return normalize(filePath, path.dirname(module.filename));
 | |
|             } else {
 | |
|                 return filePath;
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         return amdRequire;
 | |
|     };
 | |
| 
 | |
|     //Favor explicit value, passed in if the module wants to support Node 0.4.
 | |
|     requireFn = requireFn || function req() {
 | |
|         return module.require.apply(module, arguments);
 | |
|     };
 | |
| 
 | |
|     function runFactory(id, deps, factory) {
 | |
|         var r, e, m, result;
 | |
| 
 | |
|         if (id) {
 | |
|             e = loaderCache[id] = {};
 | |
|             m = {
 | |
|                 id: id,
 | |
|                 uri: __filename,
 | |
|                 exports: e
 | |
|             };
 | |
|             r = makeRequire(requireFn, e, m, id);
 | |
|         } else {
 | |
|             //Only support one define call per file
 | |
|             if (alreadyCalled) {
 | |
|                 throw new Error('amdefine with no module ID cannot be called more than once per file.');
 | |
|             }
 | |
|             alreadyCalled = true;
 | |
| 
 | |
|             //Use the real variables from node
 | |
|             //Use module.exports for exports, since
 | |
|             //the exports in here is amdefine exports.
 | |
|             e = module.exports;
 | |
|             m = module;
 | |
|             r = makeRequire(requireFn, e, m, module.id);
 | |
|         }
 | |
| 
 | |
|         //If there are dependencies, they are strings, so need
 | |
|         //to convert them to dependency values.
 | |
|         if (deps) {
 | |
|             deps = deps.map(function (depName) {
 | |
|                 return r(depName);
 | |
|             });
 | |
|         }
 | |
| 
 | |
|         //Call the factory with the right dependencies.
 | |
|         if (typeof factory === 'function') {
 | |
|             result = factory.apply(m.exports, deps);
 | |
|         } else {
 | |
|             result = factory;
 | |
|         }
 | |
| 
 | |
|         if (result !== undefined) {
 | |
|             m.exports = result;
 | |
|             if (id) {
 | |
|                 loaderCache[id] = m.exports;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     stringRequire = function (systemRequire, exports, module, id, relId) {
 | |
|         //Split the ID by a ! so that
 | |
|         var index = id.indexOf('!'),
 | |
|             originalId = id,
 | |
|             prefix, plugin;
 | |
| 
 | |
|         if (index === -1) {
 | |
|             id = normalize(id, relId);
 | |
| 
 | |
|             //Straight module lookup. If it is one of the special dependencies,
 | |
|             //deal with it, otherwise, delegate to node.
 | |
|             if (id === 'require') {
 | |
|                 return makeRequire(systemRequire, exports, module, relId);
 | |
|             } else if (id === 'exports') {
 | |
|                 return exports;
 | |
|             } else if (id === 'module') {
 | |
|                 return module;
 | |
|             } else if (loaderCache.hasOwnProperty(id)) {
 | |
|                 return loaderCache[id];
 | |
|             } else if (defineCache[id]) {
 | |
|                 runFactory.apply(null, defineCache[id]);
 | |
|                 return loaderCache[id];
 | |
|             } else {
 | |
|                 if(systemRequire) {
 | |
|                     return systemRequire(originalId);
 | |
|                 } else {
 | |
|                     throw new Error('No module with ID: ' + id);
 | |
|                 }
 | |
|             }
 | |
|         } else {
 | |
|             //There is a plugin in play.
 | |
|             prefix = id.substring(0, index);
 | |
|             id = id.substring(index + 1, id.length);
 | |
| 
 | |
|             plugin = stringRequire(systemRequire, exports, module, prefix, relId);
 | |
| 
 | |
|             if (plugin.normalize) {
 | |
|                 id = plugin.normalize(id, makeNormalize(relId));
 | |
|             } else {
 | |
|                 //Normalize the ID normally.
 | |
|                 id = normalize(id, relId);
 | |
|             }
 | |
| 
 | |
|             if (loaderCache[id]) {
 | |
|                 return loaderCache[id];
 | |
|             } else {
 | |
|                 plugin.load(id, makeRequire(systemRequire, exports, module, relId), makeLoad(id), {});
 | |
| 
 | |
|                 return loaderCache[id];
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     //Create a define function specific to the module asking for amdefine.
 | |
|     function define(id, deps, factory) {
 | |
|         if (Array.isArray(id)) {
 | |
|             factory = deps;
 | |
|             deps = id;
 | |
|             id = undefined;
 | |
|         } else if (typeof id !== 'string') {
 | |
|             factory = id;
 | |
|             id = deps = undefined;
 | |
|         }
 | |
| 
 | |
|         if (deps && !Array.isArray(deps)) {
 | |
|             factory = deps;
 | |
|             deps = undefined;
 | |
|         }
 | |
| 
 | |
|         if (!deps) {
 | |
|             deps = ['require', 'exports', 'module'];
 | |
|         }
 | |
| 
 | |
|         //Set up properties for this module. If an ID, then use
 | |
|         //internal cache. If no ID, then use the external variables
 | |
|         //for this node module.
 | |
|         if (id) {
 | |
|             //Put the module in deep freeze until there is a
 | |
|             //require call for it.
 | |
|             defineCache[id] = [id, deps, factory];
 | |
|         } else {
 | |
|             runFactory(id, deps, factory);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     //define.require, which has access to all the values in the
 | |
|     //cache. Useful for AMD modules that all have IDs in the file,
 | |
|     //but need to finally export a value to node based on one of those
 | |
|     //IDs.
 | |
|     define.require = function (id) {
 | |
|         if (loaderCache[id]) {
 | |
|             return loaderCache[id];
 | |
|         }
 | |
| 
 | |
|         if (defineCache[id]) {
 | |
|             runFactory.apply(null, defineCache[id]);
 | |
|             return loaderCache[id];
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     define.amd = {};
 | |
| 
 | |
|     return define;
 | |
| }
 | |
| 
 | |
| module.exports = amdefine;
 | |
| 
 | |
| },{"__browserify_process":29,"path":30}],57:[function(require,module,exports){
 | |
| var sys = require("util");
 | |
| var MOZ_SourceMap = require("source-map");
 | |
| var UglifyJS = exports;
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| function array_to_hash(a) {
 | |
|     var ret = Object.create(null);
 | |
|     for (var i = 0; i < a.length; ++i)
 | |
|         ret[a[i]] = true;
 | |
|     return ret;
 | |
| };
 | |
| 
 | |
| function slice(a, start) {
 | |
|     return Array.prototype.slice.call(a, start || 0);
 | |
| };
 | |
| 
 | |
| function characters(str) {
 | |
|     return str.split("");
 | |
| };
 | |
| 
 | |
| function member(name, array) {
 | |
|     for (var i = array.length; --i >= 0;)
 | |
|         if (array[i] == name)
 | |
|             return true;
 | |
|     return false;
 | |
| };
 | |
| 
 | |
| function find_if(func, array) {
 | |
|     for (var i = 0, n = array.length; i < n; ++i) {
 | |
|         if (func(array[i]))
 | |
|             return array[i];
 | |
|     }
 | |
| };
 | |
| 
 | |
| function repeat_string(str, i) {
 | |
|     if (i <= 0) return "";
 | |
|     if (i == 1) return str;
 | |
|     var d = repeat_string(str, i >> 1);
 | |
|     d += d;
 | |
|     if (i & 1) d += str;
 | |
|     return d;
 | |
| };
 | |
| 
 | |
| function DefaultsError(msg, defs) {
 | |
|     this.msg = msg;
 | |
|     this.defs = defs;
 | |
| };
 | |
| 
 | |
| function defaults(args, defs, croak) {
 | |
|     if (args === true)
 | |
|         args = {};
 | |
|     var ret = args || {};
 | |
|     if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i))
 | |
|         throw new DefaultsError("`" + i + "` is not a supported option", defs);
 | |
|     for (var i in defs) if (defs.hasOwnProperty(i)) {
 | |
|         ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i];
 | |
|     }
 | |
|     return ret;
 | |
| };
 | |
| 
 | |
| function merge(obj, ext) {
 | |
|     for (var i in ext) if (ext.hasOwnProperty(i)) {
 | |
|         obj[i] = ext[i];
 | |
|     }
 | |
|     return obj;
 | |
| };
 | |
| 
 | |
| function noop() {};
 | |
| 
 | |
| var MAP = (function(){
 | |
|     function MAP(a, f, backwards) {
 | |
|         var ret = [], top = [], i;
 | |
|         function doit() {
 | |
|             var val = f(a[i], i);
 | |
|             var is_last = val instanceof Last;
 | |
|             if (is_last) val = val.v;
 | |
|             if (val instanceof AtTop) {
 | |
|                 val = val.v;
 | |
|                 if (val instanceof Splice) {
 | |
|                     top.push.apply(top, backwards ? val.v.slice().reverse() : val.v);
 | |
|                 } else {
 | |
|                     top.push(val);
 | |
|                 }
 | |
|             }
 | |
|             else if (val !== skip) {
 | |
|                 if (val instanceof Splice) {
 | |
|                     ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
 | |
|                 } else {
 | |
|                     ret.push(val);
 | |
|                 }
 | |
|             }
 | |
|             return is_last;
 | |
|         };
 | |
|         if (a instanceof Array) {
 | |
|             if (backwards) {
 | |
|                 for (i = a.length; --i >= 0;) if (doit()) break;
 | |
|                 ret.reverse();
 | |
|                 top.reverse();
 | |
|             } else {
 | |
|                 for (i = 0; i < a.length; ++i) if (doit()) break;
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             for (i in a) if (a.hasOwnProperty(i)) if (doit()) break;
 | |
|         }
 | |
|         return top.concat(ret);
 | |
|     };
 | |
|     MAP.at_top = function(val) { return new AtTop(val) };
 | |
|     MAP.splice = function(val) { return new Splice(val) };
 | |
|     MAP.last = function(val) { return new Last(val) };
 | |
|     var skip = MAP.skip = {};
 | |
|     function AtTop(val) { this.v = val };
 | |
|     function Splice(val) { this.v = val };
 | |
|     function Last(val) { this.v = val };
 | |
|     return MAP;
 | |
| })();
 | |
| 
 | |
| function push_uniq(array, el) {
 | |
|     if (array.indexOf(el) < 0)
 | |
|         array.push(el);
 | |
| };
 | |
| 
 | |
| function string_template(text, props) {
 | |
|     return text.replace(/\{(.+?)\}/g, function(str, p){
 | |
|         return props[p];
 | |
|     });
 | |
| };
 | |
| 
 | |
| function remove(array, el) {
 | |
|     for (var i = array.length; --i >= 0;) {
 | |
|         if (array[i] === el) array.splice(i, 1);
 | |
|     }
 | |
| };
 | |
| 
 | |
| function mergeSort(array, cmp) {
 | |
|     if (array.length < 2) return array.slice();
 | |
|     function merge(a, b) {
 | |
|         var r = [], ai = 0, bi = 0, i = 0;
 | |
|         while (ai < a.length && bi < b.length) {
 | |
|             cmp(a[ai], b[bi]) <= 0
 | |
|                 ? r[i++] = a[ai++]
 | |
|                 : r[i++] = b[bi++];
 | |
|         }
 | |
|         if (ai < a.length) r.push.apply(r, a.slice(ai));
 | |
|         if (bi < b.length) r.push.apply(r, b.slice(bi));
 | |
|         return r;
 | |
|     };
 | |
|     function _ms(a) {
 | |
|         if (a.length <= 1)
 | |
|             return a;
 | |
|         var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m);
 | |
|         left = _ms(left);
 | |
|         right = _ms(right);
 | |
|         return merge(left, right);
 | |
|     };
 | |
|     return _ms(array);
 | |
| };
 | |
| 
 | |
| function set_difference(a, b) {
 | |
|     return a.filter(function(el){
 | |
|         return b.indexOf(el) < 0;
 | |
|     });
 | |
| };
 | |
| 
 | |
| function set_intersection(a, b) {
 | |
|     return a.filter(function(el){
 | |
|         return b.indexOf(el) >= 0;
 | |
|     });
 | |
| };
 | |
| 
 | |
| // this function is taken from Acorn [1], written by Marijn Haverbeke
 | |
| // [1] https://github.com/marijnh/acorn
 | |
| function makePredicate(words) {
 | |
|     if (!(words instanceof Array)) words = words.split(" ");
 | |
|     var f = "", cats = [];
 | |
|     out: for (var i = 0; i < words.length; ++i) {
 | |
|         for (var j = 0; j < cats.length; ++j)
 | |
|             if (cats[j][0].length == words[i].length) {
 | |
|                 cats[j].push(words[i]);
 | |
|                 continue out;
 | |
|             }
 | |
|         cats.push([words[i]]);
 | |
|     }
 | |
|     function compareTo(arr) {
 | |
|         if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
 | |
|         f += "switch(str){";
 | |
|         for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":";
 | |
|         f += "return true}return false;";
 | |
|     }
 | |
|     // When there are more than three length categories, an outer
 | |
|     // switch first dispatches on the lengths, to save on comparisons.
 | |
|     if (cats.length > 3) {
 | |
|         cats.sort(function(a, b) {return b.length - a.length;});
 | |
|         f += "switch(str.length){";
 | |
|         for (var i = 0; i < cats.length; ++i) {
 | |
|             var cat = cats[i];
 | |
|             f += "case " + cat[0].length + ":";
 | |
|             compareTo(cat);
 | |
|         }
 | |
|         f += "}";
 | |
|         // Otherwise, simply generate a flat `switch` statement.
 | |
|     } else {
 | |
|         compareTo(words);
 | |
|     }
 | |
|     return new Function("str", f);
 | |
| };
 | |
| 
 | |
| function all(array, predicate) {
 | |
|     for (var i = array.length; --i >= 0;)
 | |
|         if (!predicate(array[i]))
 | |
|             return false;
 | |
|     return true;
 | |
| };
 | |
| 
 | |
| function Dictionary() {
 | |
|     this._values = Object.create(null);
 | |
|     this._size = 0;
 | |
| };
 | |
| Dictionary.prototype = {
 | |
|     set: function(key, val) {
 | |
|         if (!this.has(key)) ++this._size;
 | |
|         this._values["$" + key] = val;
 | |
|         return this;
 | |
|     },
 | |
|     add: function(key, val) {
 | |
|         if (this.has(key)) {
 | |
|             this.get(key).push(val);
 | |
|         } else {
 | |
|             this.set(key, [ val ]);
 | |
|         }
 | |
|         return this;
 | |
|     },
 | |
|     get: function(key) { return this._values["$" + key] },
 | |
|     del: function(key) {
 | |
|         if (this.has(key)) {
 | |
|             --this._size;
 | |
|             delete this._values["$" + key];
 | |
|         }
 | |
|         return this;
 | |
|     },
 | |
|     has: function(key) { return ("$" + key) in this._values },
 | |
|     each: function(f) {
 | |
|         for (var i in this._values)
 | |
|             f(this._values[i], i.substr(1));
 | |
|     },
 | |
|     size: function() {
 | |
|         return this._size;
 | |
|     },
 | |
|     map: function(f) {
 | |
|         var ret = [];
 | |
|         for (var i in this._values)
 | |
|             ret.push(f(this._values[i], i.substr(1)));
 | |
|         return ret;
 | |
|     }
 | |
| };
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| function DEFNODE(type, props, methods, base) {
 | |
|     if (arguments.length < 4) base = AST_Node;
 | |
|     if (!props) props = [];
 | |
|     else props = props.split(/\s+/);
 | |
|     var self_props = props;
 | |
|     if (base && base.PROPS)
 | |
|         props = props.concat(base.PROPS);
 | |
|     var code = "return function AST_" + type + "(props){ if (props) { ";
 | |
|     for (var i = props.length; --i >= 0;) {
 | |
|         code += "this." + props[i] + " = props." + props[i] + ";";
 | |
|     }
 | |
|     var proto = base && new base;
 | |
|     if (proto && proto.initialize || (methods && methods.initialize))
 | |
|         code += "this.initialize();";
 | |
|     code += "}}";
 | |
|     var ctor = new Function(code)();
 | |
|     if (proto) {
 | |
|         ctor.prototype = proto;
 | |
|         ctor.BASE = base;
 | |
|     }
 | |
|     if (base) base.SUBCLASSES.push(ctor);
 | |
|     ctor.prototype.CTOR = ctor;
 | |
|     ctor.PROPS = props || null;
 | |
|     ctor.SELF_PROPS = self_props;
 | |
|     ctor.SUBCLASSES = [];
 | |
|     if (type) {
 | |
|         ctor.prototype.TYPE = ctor.TYPE = type;
 | |
|     }
 | |
|     if (methods) for (i in methods) if (methods.hasOwnProperty(i)) {
 | |
|         if (/^\$/.test(i)) {
 | |
|             ctor[i.substr(1)] = methods[i];
 | |
|         } else {
 | |
|             ctor.prototype[i] = methods[i];
 | |
|         }
 | |
|     }
 | |
|     ctor.DEFMETHOD = function(name, method) {
 | |
|         this.prototype[name] = method;
 | |
|     };
 | |
|     return ctor;
 | |
| };
 | |
| 
 | |
| var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", {
 | |
| }, null);
 | |
| 
 | |
| var AST_Node = DEFNODE("Node", "start end", {
 | |
|     clone: function() {
 | |
|         return new this.CTOR(this);
 | |
|     },
 | |
|     $documentation: "Base class of all AST nodes",
 | |
|     $propdoc: {
 | |
|         start: "[AST_Token] The first token of this node",
 | |
|         end: "[AST_Token] The last token of this node"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this);
 | |
|     },
 | |
|     walk: function(visitor) {
 | |
|         return this._walk(visitor); // not sure the indirection will be any help
 | |
|     }
 | |
| }, null);
 | |
| 
 | |
| AST_Node.warn_function = null;
 | |
| AST_Node.warn = function(txt, props) {
 | |
|     if (AST_Node.warn_function)
 | |
|         AST_Node.warn_function(string_template(txt, props));
 | |
| };
 | |
| 
 | |
| /* -----[ statements ]----- */
 | |
| 
 | |
| var AST_Statement = DEFNODE("Statement", null, {
 | |
|     $documentation: "Base class of all statements",
 | |
| });
 | |
| 
 | |
| var AST_Debugger = DEFNODE("Debugger", null, {
 | |
|     $documentation: "Represents a debugger statement",
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_Directive = DEFNODE("Directive", "value scope", {
 | |
|     $documentation: "Represents a directive, like \"use strict\";",
 | |
|     $propdoc: {
 | |
|         value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
 | |
|         scope: "[AST_Scope/S] The scope that this directive affects"
 | |
|     },
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
 | |
|     $documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
 | |
|     $propdoc: {
 | |
|         body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Statement);
 | |
| 
 | |
| function walk_body(node, visitor) {
 | |
|     if (node.body instanceof AST_Statement) {
 | |
|         node.body._walk(visitor);
 | |
|     }
 | |
|     else node.body.forEach(function(stat){
 | |
|         stat._walk(visitor);
 | |
|     });
 | |
| };
 | |
| 
 | |
| var AST_Block = DEFNODE("Block", "body", {
 | |
|     $documentation: "A body of statements (usually bracketed)",
 | |
|     $propdoc: {
 | |
|         body: "[AST_Statement*] an array of statements"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             walk_body(this, visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_BlockStatement = DEFNODE("BlockStatement", null, {
 | |
|     $documentation: "A block statement",
 | |
| }, AST_Block);
 | |
| 
 | |
| var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
 | |
|     $documentation: "The empty statement (empty block or simply a semicolon)",
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this);
 | |
|     }
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
 | |
|     $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
 | |
|     $propdoc: {
 | |
|         body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
 | |
|     $documentation: "Statement with a label",
 | |
|     $propdoc: {
 | |
|         label: "[AST_Label] a label definition"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.label._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_StatementWithBody);
 | |
| 
 | |
| var AST_DWLoop = DEFNODE("DWLoop", "condition", {
 | |
|     $documentation: "Base class for do/while statements",
 | |
|     $propdoc: {
 | |
|         condition: "[AST_Node] the loop condition.  Should not be instanceof AST_Statement"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.condition._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_StatementWithBody);
 | |
| 
 | |
| var AST_Do = DEFNODE("Do", null, {
 | |
|     $documentation: "A `do` statement",
 | |
| }, AST_DWLoop);
 | |
| 
 | |
| var AST_While = DEFNODE("While", null, {
 | |
|     $documentation: "A `while` statement",
 | |
| }, AST_DWLoop);
 | |
| 
 | |
| var AST_For = DEFNODE("For", "init condition step", {
 | |
|     $documentation: "A `for` statement",
 | |
|     $propdoc: {
 | |
|         init: "[AST_Node?] the `for` initialization code, or null if empty",
 | |
|         condition: "[AST_Node?] the `for` termination clause, or null if empty",
 | |
|         step: "[AST_Node?] the `for` update clause, or null if empty"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             if (this.init) this.init._walk(visitor);
 | |
|             if (this.condition) this.condition._walk(visitor);
 | |
|             if (this.step) this.step._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_StatementWithBody);
 | |
| 
 | |
| var AST_ForIn = DEFNODE("ForIn", "init name object", {
 | |
|     $documentation: "A `for ... in` statement",
 | |
|     $propdoc: {
 | |
|         init: "[AST_Node] the `for/in` initialization code",
 | |
|         name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
 | |
|         object: "[AST_Node] the object that we're looping through"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.init._walk(visitor);
 | |
|             this.object._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_StatementWithBody);
 | |
| 
 | |
| var AST_With = DEFNODE("With", "expression", {
 | |
|     $documentation: "A `with` statement",
 | |
|     $propdoc: {
 | |
|         expression: "[AST_Node] the `with` expression"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_StatementWithBody);
 | |
| 
 | |
| /* -----[ scope and functions ]----- */
 | |
| 
 | |
| var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
 | |
|     $documentation: "Base class for all statements introducing a lexical scope",
 | |
|     $propdoc: {
 | |
|         directives: "[string*/S] an array of directives declared in this scope",
 | |
|         variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
 | |
|         functions: "[Object/S] like `variables`, but only lists function declarations",
 | |
|         uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
 | |
|         uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
 | |
|         parent_scope: "[AST_Scope?/S] link to the parent scope",
 | |
|         enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
 | |
|         cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
 | |
|     },
 | |
| }, AST_Block);
 | |
| 
 | |
| var AST_Toplevel = DEFNODE("Toplevel", "globals", {
 | |
|     $documentation: "The toplevel scope",
 | |
|     $propdoc: {
 | |
|         globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
 | |
|     },
 | |
|     wrap_enclose: function(arg_parameter_pairs) {
 | |
|         var self = this;
 | |
|         var args = [];
 | |
|         var parameters = [];
 | |
| 
 | |
|         arg_parameter_pairs.forEach(function(pair) {
 | |
|             var split = pair.split(":");
 | |
| 
 | |
|             args.push(split[0]);
 | |
|             parameters.push(split[1]);
 | |
|         });
 | |
| 
 | |
|         var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
 | |
|         wrapped_tl = parse(wrapped_tl);
 | |
|         wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
 | |
|             if (node instanceof AST_Directive && node.value == "$ORIG") {
 | |
|                 return MAP.splice(self.body);
 | |
|             }
 | |
|         }));
 | |
|         return wrapped_tl;
 | |
|     },
 | |
|     wrap_commonjs: function(name, export_all) {
 | |
|         var self = this;
 | |
|         var to_export = [];
 | |
|         if (export_all) {
 | |
|             self.figure_out_scope();
 | |
|             self.walk(new TreeWalker(function(node){
 | |
|                 if (node instanceof AST_SymbolDeclaration && node.definition().global) {
 | |
|                     if (!find_if(function(n){ return n.name == node.name }, to_export))
 | |
|                         to_export.push(node);
 | |
|                 }
 | |
|             }));
 | |
|         }
 | |
|         var wrapped_tl = "(function(exports, global){ global['" + name + "'] = exports; '$ORIG'; '$EXPORTS'; }({}, (function(){return this}())))";
 | |
|         wrapped_tl = parse(wrapped_tl);
 | |
|         wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
 | |
|             if (node instanceof AST_SimpleStatement) {
 | |
|                 node = node.body;
 | |
|                 if (node instanceof AST_String) switch (node.getValue()) {
 | |
|                   case "$ORIG":
 | |
|                     return MAP.splice(self.body);
 | |
|                   case "$EXPORTS":
 | |
|                     var body = [];
 | |
|                     to_export.forEach(function(sym){
 | |
|                         body.push(new AST_SimpleStatement({
 | |
|                             body: new AST_Assign({
 | |
|                                 left: new AST_Sub({
 | |
|                                     expression: new AST_SymbolRef({ name: "exports" }),
 | |
|                                     property: new AST_String({ value: sym.name }),
 | |
|                                 }),
 | |
|                                 operator: "=",
 | |
|                                 right: new AST_SymbolRef(sym),
 | |
|                             }),
 | |
|                         }));
 | |
|                     });
 | |
|                     return MAP.splice(body);
 | |
|                 }
 | |
|             }
 | |
|         }));
 | |
|         return wrapped_tl;
 | |
|     }
 | |
| }, AST_Scope);
 | |
| 
 | |
| var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
 | |
|     $documentation: "Base class for functions",
 | |
|     $propdoc: {
 | |
|         name: "[AST_SymbolDeclaration?] the name of this function",
 | |
|         argnames: "[AST_SymbolFunarg*] array of function arguments",
 | |
|         uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             if (this.name) this.name._walk(visitor);
 | |
|             this.argnames.forEach(function(arg){
 | |
|                 arg._walk(visitor);
 | |
|             });
 | |
|             walk_body(this, visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Scope);
 | |
| 
 | |
| var AST_Accessor = DEFNODE("Accessor", null, {
 | |
|     $documentation: "A setter/getter function"
 | |
| }, AST_Lambda);
 | |
| 
 | |
| var AST_Function = DEFNODE("Function", null, {
 | |
|     $documentation: "A function expression"
 | |
| }, AST_Lambda);
 | |
| 
 | |
| var AST_Defun = DEFNODE("Defun", null, {
 | |
|     $documentation: "A function definition"
 | |
| }, AST_Lambda);
 | |
| 
 | |
| /* -----[ JUMPS ]----- */
 | |
| 
 | |
| var AST_Jump = DEFNODE("Jump", null, {
 | |
|     $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_Exit = DEFNODE("Exit", "value", {
 | |
|     $documentation: "Base class for “exits” (`return` and `throw`)",
 | |
|     $propdoc: {
 | |
|         value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, this.value && function(){
 | |
|             this.value._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Jump);
 | |
| 
 | |
| var AST_Return = DEFNODE("Return", null, {
 | |
|     $documentation: "A `return` statement"
 | |
| }, AST_Exit);
 | |
| 
 | |
| var AST_Throw = DEFNODE("Throw", null, {
 | |
|     $documentation: "A `throw` statement"
 | |
| }, AST_Exit);
 | |
| 
 | |
| var AST_LoopControl = DEFNODE("LoopControl", "label", {
 | |
|     $documentation: "Base class for loop control statements (`break` and `continue`)",
 | |
|     $propdoc: {
 | |
|         label: "[AST_LabelRef?] the label, or null if none",
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, this.label && function(){
 | |
|             this.label._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Jump);
 | |
| 
 | |
| var AST_Break = DEFNODE("Break", null, {
 | |
|     $documentation: "A `break` statement"
 | |
| }, AST_LoopControl);
 | |
| 
 | |
| var AST_Continue = DEFNODE("Continue", null, {
 | |
|     $documentation: "A `continue` statement"
 | |
| }, AST_LoopControl);
 | |
| 
 | |
| /* -----[ IF ]----- */
 | |
| 
 | |
| var AST_If = DEFNODE("If", "condition alternative", {
 | |
|     $documentation: "A `if` statement",
 | |
|     $propdoc: {
 | |
|         condition: "[AST_Node] the `if` condition",
 | |
|         alternative: "[AST_Statement?] the `else` part, or null if not present"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.condition._walk(visitor);
 | |
|             this.body._walk(visitor);
 | |
|             if (this.alternative) this.alternative._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_StatementWithBody);
 | |
| 
 | |
| /* -----[ SWITCH ]----- */
 | |
| 
 | |
| var AST_Switch = DEFNODE("Switch", "expression", {
 | |
|     $documentation: "A `switch` statement",
 | |
|     $propdoc: {
 | |
|         expression: "[AST_Node] the `switch` “discriminant”"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|             walk_body(this, visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Block);
 | |
| 
 | |
| var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
 | |
|     $documentation: "Base class for `switch` branches",
 | |
| }, AST_Block);
 | |
| 
 | |
| var AST_Default = DEFNODE("Default", null, {
 | |
|     $documentation: "A `default` switch branch",
 | |
| }, AST_SwitchBranch);
 | |
| 
 | |
| var AST_Case = DEFNODE("Case", "expression", {
 | |
|     $documentation: "A `case` switch branch",
 | |
|     $propdoc: {
 | |
|         expression: "[AST_Node] the `case` expression"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|             walk_body(this, visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_SwitchBranch);
 | |
| 
 | |
| /* -----[ EXCEPTIONS ]----- */
 | |
| 
 | |
| var AST_Try = DEFNODE("Try", "bcatch bfinally", {
 | |
|     $documentation: "A `try` statement",
 | |
|     $propdoc: {
 | |
|         bcatch: "[AST_Catch?] the catch block, or null if not present",
 | |
|         bfinally: "[AST_Finally?] the finally block, or null if not present"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             walk_body(this, visitor);
 | |
|             if (this.bcatch) this.bcatch._walk(visitor);
 | |
|             if (this.bfinally) this.bfinally._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Block);
 | |
| 
 | |
| // XXX: this is wrong according to ECMA-262 (12.4).  the catch block
 | |
| // should introduce another scope, as the argname should be visible
 | |
| // only inside the catch block.  However, doing it this way because of
 | |
| // IE which simply introduces the name in the surrounding scope.  If
 | |
| // we ever want to fix this then AST_Catch should inherit from
 | |
| // AST_Scope.
 | |
| var AST_Catch = DEFNODE("Catch", "argname", {
 | |
|     $documentation: "A `catch` node; only makes sense as part of a `try` statement",
 | |
|     $propdoc: {
 | |
|         argname: "[AST_SymbolCatch] symbol for the exception"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.argname._walk(visitor);
 | |
|             walk_body(this, visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_Block);
 | |
| 
 | |
| var AST_Finally = DEFNODE("Finally", null, {
 | |
|     $documentation: "A `finally` node; only makes sense as part of a `try` statement"
 | |
| }, AST_Block);
 | |
| 
 | |
| /* -----[ VAR/CONST ]----- */
 | |
| 
 | |
| var AST_Definitions = DEFNODE("Definitions", "definitions", {
 | |
|     $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
 | |
|     $propdoc: {
 | |
|         definitions: "[AST_VarDef*] array of variable definitions"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.definitions.forEach(function(def){
 | |
|                 def._walk(visitor);
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| }, AST_Statement);
 | |
| 
 | |
| var AST_Var = DEFNODE("Var", null, {
 | |
|     $documentation: "A `var` statement"
 | |
| }, AST_Definitions);
 | |
| 
 | |
| var AST_Const = DEFNODE("Const", null, {
 | |
|     $documentation: "A `const` statement"
 | |
| }, AST_Definitions);
 | |
| 
 | |
| var AST_VarDef = DEFNODE("VarDef", "name value", {
 | |
|     $documentation: "A variable declaration; only appears in a AST_Definitions node",
 | |
|     $propdoc: {
 | |
|         name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
 | |
|         value: "[AST_Node?] initializer, or null of there's no initializer"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.name._walk(visitor);
 | |
|             if (this.value) this.value._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| /* -----[ OTHER ]----- */
 | |
| 
 | |
| var AST_Call = DEFNODE("Call", "expression args", {
 | |
|     $documentation: "A function call expression",
 | |
|     $propdoc: {
 | |
|         expression: "[AST_Node] expression to invoke as function",
 | |
|         args: "[AST_Node*] array of arguments"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|             this.args.forEach(function(arg){
 | |
|                 arg._walk(visitor);
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_New = DEFNODE("New", null, {
 | |
|     $documentation: "An object instantiation.  Derives from a function call since it has exactly the same properties"
 | |
| }, AST_Call);
 | |
| 
 | |
| var AST_Seq = DEFNODE("Seq", "car cdr", {
 | |
|     $documentation: "A sequence expression (two comma-separated expressions)",
 | |
|     $propdoc: {
 | |
|         car: "[AST_Node] first element in sequence",
 | |
|         cdr: "[AST_Node] second element in sequence"
 | |
|     },
 | |
|     $cons: function(x, y) {
 | |
|         var seq = new AST_Seq(x);
 | |
|         seq.car = x;
 | |
|         seq.cdr = y;
 | |
|         return seq;
 | |
|     },
 | |
|     $from_array: function(array) {
 | |
|         if (array.length == 0) return null;
 | |
|         if (array.length == 1) return array[0].clone();
 | |
|         var list = null;
 | |
|         for (var i = array.length; --i >= 0;) {
 | |
|             list = AST_Seq.cons(array[i], list);
 | |
|         }
 | |
|         var p = list;
 | |
|         while (p) {
 | |
|             if (p.cdr && !p.cdr.cdr) {
 | |
|                 p.cdr = p.cdr.car;
 | |
|                 break;
 | |
|             }
 | |
|             p = p.cdr;
 | |
|         }
 | |
|         return list;
 | |
|     },
 | |
|     to_array: function() {
 | |
|         var p = this, a = [];
 | |
|         while (p) {
 | |
|             a.push(p.car);
 | |
|             if (p.cdr && !(p.cdr instanceof AST_Seq)) {
 | |
|                 a.push(p.cdr);
 | |
|                 break;
 | |
|             }
 | |
|             p = p.cdr;
 | |
|         }
 | |
|         return a;
 | |
|     },
 | |
|     add: function(node) {
 | |
|         var p = this;
 | |
|         while (p) {
 | |
|             if (!(p.cdr instanceof AST_Seq)) {
 | |
|                 var cell = AST_Seq.cons(p.cdr, node);
 | |
|                 return p.cdr = cell;
 | |
|             }
 | |
|             p = p.cdr;
 | |
|         }
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.car._walk(visitor);
 | |
|             if (this.cdr) this.cdr._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
 | |
|     $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
 | |
|     $propdoc: {
 | |
|         expression: "[AST_Node] the “container” expression",
 | |
|         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"
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_Dot = DEFNODE("Dot", null, {
 | |
|     $documentation: "A dotted property access expression",
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_PropAccess);
 | |
| 
 | |
| var AST_Sub = DEFNODE("Sub", null, {
 | |
|     $documentation: "Index-style property access, i.e. `a[\"foo\"]`",
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|             this.property._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| }, AST_PropAccess);
 | |
| 
 | |
| var AST_Unary = DEFNODE("Unary", "operator expression", {
 | |
|     $documentation: "Base class for unary expressions",
 | |
|     $propdoc: {
 | |
|         operator: "[string] the operator",
 | |
|         expression: "[AST_Node] expression that this unary operator applies to"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.expression._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, {
 | |
|     $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`"
 | |
| }, AST_Unary);
 | |
| 
 | |
| var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
 | |
|     $documentation: "Unary postfix expression, i.e. `i++`"
 | |
| }, AST_Unary);
 | |
| 
 | |
| var AST_Binary = DEFNODE("Binary", "left operator right", {
 | |
|     $documentation: "Binary expression, i.e. `a + b`",
 | |
|     $propdoc: {
 | |
|         left: "[AST_Node] left-hand side expression",
 | |
|         operator: "[string] the operator",
 | |
|         right: "[AST_Node] right-hand side expression"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.left._walk(visitor);
 | |
|             this.right._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
 | |
|     $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
 | |
|     $propdoc: {
 | |
|         condition: "[AST_Node]",
 | |
|         consequent: "[AST_Node]",
 | |
|         alternative: "[AST_Node]"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.condition._walk(visitor);
 | |
|             this.consequent._walk(visitor);
 | |
|             this.alternative._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_Assign = DEFNODE("Assign", null, {
 | |
|     $documentation: "An assignment expression — `a = b + 5`",
 | |
| }, AST_Binary);
 | |
| 
 | |
| /* -----[ LITERALS ]----- */
 | |
| 
 | |
| var AST_Array = DEFNODE("Array", "elements", {
 | |
|     $documentation: "An array literal",
 | |
|     $propdoc: {
 | |
|         elements: "[AST_Node*] array of elements"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.elements.forEach(function(el){
 | |
|                 el._walk(visitor);
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_Object = DEFNODE("Object", "properties", {
 | |
|     $documentation: "An object literal",
 | |
|     $propdoc: {
 | |
|         properties: "[AST_ObjectProperty*] array of properties"
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.properties.forEach(function(prop){
 | |
|                 prop._walk(visitor);
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
 | |
|     $documentation: "Base class for literal object properties",
 | |
|     $propdoc: {
 | |
|         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",
 | |
|         value: "[AST_Node] property value.  For setters and getters this is an AST_Function."
 | |
|     },
 | |
|     _walk: function(visitor) {
 | |
|         return visitor._visit(this, function(){
 | |
|             this.value._walk(visitor);
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
 | |
|     $documentation: "A key: value object property",
 | |
| }, AST_ObjectProperty);
 | |
| 
 | |
| var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
 | |
|     $documentation: "An object setter property",
 | |
| }, AST_ObjectProperty);
 | |
| 
 | |
| var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
 | |
|     $documentation: "An object getter property",
 | |
| }, AST_ObjectProperty);
 | |
| 
 | |
| var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
 | |
|     $propdoc: {
 | |
|         name: "[string] name of this symbol",
 | |
|         scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
 | |
|         thedef: "[SymbolDef/S] the definition of this symbol"
 | |
|     },
 | |
|     $documentation: "Base class for all symbols",
 | |
| });
 | |
| 
 | |
| var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
 | |
|     $documentation: "The name of a property accessor (setter/getter function)"
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
 | |
|     $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
 | |
|     $propdoc: {
 | |
|         init: "[AST_Node*/S] array of initializers for this declaration."
 | |
|     }
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_SymbolVar = DEFNODE("SymbolVar", null, {
 | |
|     $documentation: "Symbol defining a variable",
 | |
| }, AST_SymbolDeclaration);
 | |
| 
 | |
| var AST_SymbolConst = DEFNODE("SymbolConst", null, {
 | |
|     $documentation: "A constant declaration"
 | |
| }, AST_SymbolDeclaration);
 | |
| 
 | |
| var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
 | |
|     $documentation: "Symbol naming a function argument",
 | |
| }, AST_SymbolVar);
 | |
| 
 | |
| var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
 | |
|     $documentation: "Symbol defining a function",
 | |
| }, AST_SymbolDeclaration);
 | |
| 
 | |
| var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
 | |
|     $documentation: "Symbol naming a function expression",
 | |
| }, AST_SymbolDeclaration);
 | |
| 
 | |
| var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
 | |
|     $documentation: "Symbol naming the exception in catch",
 | |
| }, AST_SymbolDeclaration);
 | |
| 
 | |
| var AST_Label = DEFNODE("Label", "references", {
 | |
|     $documentation: "Symbol naming a label (declaration)",
 | |
|     $propdoc: {
 | |
|         references: "[AST_LabelRef*] a list of nodes referring to this label"
 | |
|     }
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_SymbolRef = DEFNODE("SymbolRef", null, {
 | |
|     $documentation: "Reference to some symbol (not definition/declaration)",
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_LabelRef = DEFNODE("LabelRef", null, {
 | |
|     $documentation: "Reference to a label symbol",
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_This = DEFNODE("This", null, {
 | |
|     $documentation: "The `this` symbol",
 | |
| }, AST_Symbol);
 | |
| 
 | |
| var AST_Constant = DEFNODE("Constant", null, {
 | |
|     $documentation: "Base class for all constants",
 | |
|     getValue: function() {
 | |
|         return this.value;
 | |
|     }
 | |
| });
 | |
| 
 | |
| var AST_String = DEFNODE("String", "value", {
 | |
|     $documentation: "A string literal",
 | |
|     $propdoc: {
 | |
|         value: "[string] the contents of this string"
 | |
|     }
 | |
| }, AST_Constant);
 | |
| 
 | |
| var AST_Number = DEFNODE("Number", "value", {
 | |
|     $documentation: "A number literal",
 | |
|     $propdoc: {
 | |
|         value: "[number] the numeric value"
 | |
|     }
 | |
| }, AST_Constant);
 | |
| 
 | |
| var AST_RegExp = DEFNODE("RegExp", "value", {
 | |
|     $documentation: "A regexp literal",
 | |
|     $propdoc: {
 | |
|         value: "[RegExp] the actual regexp"
 | |
|     }
 | |
| }, AST_Constant);
 | |
| 
 | |
| var AST_Atom = DEFNODE("Atom", null, {
 | |
|     $documentation: "Base class for atoms",
 | |
| }, AST_Constant);
 | |
| 
 | |
| var AST_Null = DEFNODE("Null", null, {
 | |
|     $documentation: "The `null` atom",
 | |
|     value: null
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_NaN = DEFNODE("NaN", null, {
 | |
|     $documentation: "The impossible value",
 | |
|     value: 0/0
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_Undefined = DEFNODE("Undefined", null, {
 | |
|     $documentation: "The `undefined` value",
 | |
|     value: (function(){}())
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_Hole = DEFNODE("Hole", null, {
 | |
|     $documentation: "A hole in an array",
 | |
|     value: (function(){}())
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_Infinity = DEFNODE("Infinity", null, {
 | |
|     $documentation: "The `Infinity` value",
 | |
|     value: 1/0
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_Boolean = DEFNODE("Boolean", null, {
 | |
|     $documentation: "Base class for booleans",
 | |
| }, AST_Atom);
 | |
| 
 | |
| var AST_False = DEFNODE("False", null, {
 | |
|     $documentation: "The `false` atom",
 | |
|     value: false
 | |
| }, AST_Boolean);
 | |
| 
 | |
| var AST_True = DEFNODE("True", null, {
 | |
|     $documentation: "The `true` atom",
 | |
|     value: true
 | |
| }, AST_Boolean);
 | |
| 
 | |
| /* -----[ TreeWalker ]----- */
 | |
| 
 | |
| function TreeWalker(callback) {
 | |
|     this.visit = callback;
 | |
|     this.stack = [];
 | |
| };
 | |
| TreeWalker.prototype = {
 | |
|     _visit: function(node, descend) {
 | |
|         this.stack.push(node);
 | |
|         var ret = this.visit(node, descend ? function(){
 | |
|             descend.call(node);
 | |
|         } : noop);
 | |
|         if (!ret && descend) {
 | |
|             descend.call(node);
 | |
|         }
 | |
|         this.stack.pop();
 | |
|         return ret;
 | |
|     },
 | |
|     parent: function(n) {
 | |
|         return this.stack[this.stack.length - 2 - (n || 0)];
 | |
|     },
 | |
|     push: function (node) {
 | |
|         this.stack.push(node);
 | |
|     },
 | |
|     pop: function() {
 | |
|         return this.stack.pop();
 | |
|     },
 | |
|     self: function() {
 | |
|         return this.stack[this.stack.length - 1];
 | |
|     },
 | |
|     find_parent: function(type) {
 | |
|         var stack = this.stack;
 | |
|         for (var i = stack.length; --i >= 0;) {
 | |
|             var x = stack[i];
 | |
|             if (x instanceof type) return x;
 | |
|         }
 | |
|     },
 | |
|     has_directive: function(type) {
 | |
|         return this.find_parent(AST_Scope).has_directive(type);
 | |
|     },
 | |
|     in_boolean_context: function() {
 | |
|         var stack = this.stack;
 | |
|         var i = stack.length, self = stack[--i];
 | |
|         while (i > 0) {
 | |
|             var p = stack[--i];
 | |
|             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))
 | |
|             {
 | |
|                 return true;
 | |
|             }
 | |
|             if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")))
 | |
|                 return false;
 | |
|             self = p;
 | |
|         }
 | |
|     },
 | |
|     loopcontrol_target: function(label) {
 | |
|         var stack = this.stack;
 | |
|         if (label) {
 | |
|             for (var i = stack.length; --i >= 0;) {
 | |
|                 var x = stack[i];
 | |
|                 if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
 | |
|                     return x.body;
 | |
|                 }
 | |
|             }
 | |
|         } else {
 | |
|             for (var i = stack.length; --i >= 0;) {
 | |
|                 var x = stack[i];
 | |
|                 if (x instanceof AST_Switch
 | |
|                     || x instanceof AST_For
 | |
|                     || x instanceof AST_ForIn
 | |
|                     || x instanceof AST_DWLoop) return x;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
|     Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/).
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| 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';
 | |
| var KEYWORDS_ATOM = 'false null true';
 | |
| 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;
 | |
| var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
 | |
| 
 | |
| KEYWORDS = makePredicate(KEYWORDS);
 | |
| RESERVED_WORDS = makePredicate(RESERVED_WORDS);
 | |
| KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION);
 | |
| KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
 | |
| 
 | |
| var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
 | |
| 
 | |
| var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
 | |
| var RE_OCT_NUMBER = /^0[0-7]+$/;
 | |
| var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
 | |
| 
 | |
| var OPERATORS = makePredicate([
 | |
|     "in",
 | |
|     "instanceof",
 | |
|     "typeof",
 | |
|     "new",
 | |
|     "void",
 | |
|     "delete",
 | |
|     "++",
 | |
|     "--",
 | |
|     "+",
 | |
|     "-",
 | |
|     "!",
 | |
|     "~",
 | |
|     "&",
 | |
|     "|",
 | |
|     "^",
 | |
|     "*",
 | |
|     "/",
 | |
|     "%",
 | |
|     ">>",
 | |
|     "<<",
 | |
|     ">>>",
 | |
|     "<",
 | |
|     ">",
 | |
|     "<=",
 | |
|     ">=",
 | |
|     "==",
 | |
|     "===",
 | |
|     "!=",
 | |
|     "!==",
 | |
|     "?",
 | |
|     "=",
 | |
|     "+=",
 | |
|     "-=",
 | |
|     "/=",
 | |
|     "*=",
 | |
|     "%=",
 | |
|     ">>=",
 | |
|     "<<=",
 | |
|     ">>>=",
 | |
|     "|=",
 | |
|     "^=",
 | |
|     "&=",
 | |
|     "&&",
 | |
|     "||"
 | |
| ]);
 | |
| 
 | |
| 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"));
 | |
| 
 | |
| var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
 | |
| 
 | |
| var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
 | |
| 
 | |
| var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
 | |
| 
 | |
| /* -----[ Tokenizer ]----- */
 | |
| 
 | |
| // regexps adapted from http://xregexp.com/plugins/#unicode
 | |
| var UNICODE = {
 | |
|     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]"),
 | |
|     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]"),
 | |
|     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]"),
 | |
|     connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
 | |
| };
 | |
| 
 | |
| function is_letter(code) {
 | |
|     return (code >= 97 && code <= 122)
 | |
|         || (code >= 65 && code <= 90)
 | |
|         || (code >= 0xaa && UNICODE.letter.test(String.fromCharCode(code)));
 | |
| };
 | |
| 
 | |
| function is_digit(code) {
 | |
|     return code >= 48 && code <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9
 | |
| };
 | |
| 
 | |
| function is_alphanumeric_char(code) {
 | |
|     return is_digit(code) || is_letter(code);
 | |
| };
 | |
| 
 | |
| function is_unicode_combining_mark(ch) {
 | |
|     return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
 | |
| };
 | |
| 
 | |
| function is_unicode_connector_punctuation(ch) {
 | |
|     return UNICODE.connector_punctuation.test(ch);
 | |
| };
 | |
| 
 | |
| function is_identifier(name) {
 | |
|     return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name);
 | |
| };
 | |
| 
 | |
| function is_identifier_start(code) {
 | |
|     return code == 36 || code == 95 || is_letter(code);
 | |
| };
 | |
| 
 | |
| function is_identifier_char(ch) {
 | |
|     var code = ch.charCodeAt(0);
 | |
|     return is_identifier_start(code)
 | |
|         || is_digit(code)
 | |
|         || code == 8204 // \u200c: zero-width non-joiner <ZWNJ>
 | |
|         || code == 8205 // \u200d: zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
 | |
|         || is_unicode_combining_mark(ch)
 | |
|         || is_unicode_connector_punctuation(ch)
 | |
|     ;
 | |
| };
 | |
| 
 | |
| function is_identifier_string(str){
 | |
|     var i = str.length;
 | |
|     if (i == 0) return false;
 | |
|     if (is_digit(str.charCodeAt(0))) return false;
 | |
|     while (--i >= 0) {
 | |
|         if (!is_identifier_char(str.charAt(i)))
 | |
|             return false;
 | |
|     }
 | |
|     return true;
 | |
| };
 | |
| 
 | |
| function parse_js_number(num) {
 | |
|     if (RE_HEX_NUMBER.test(num)) {
 | |
|         return parseInt(num.substr(2), 16);
 | |
|     } else if (RE_OCT_NUMBER.test(num)) {
 | |
|         return parseInt(num.substr(1), 8);
 | |
|     } else if (RE_DEC_NUMBER.test(num)) {
 | |
|         return parseFloat(num);
 | |
|     }
 | |
| };
 | |
| 
 | |
| function JS_Parse_Error(message, line, col, pos) {
 | |
|     this.message = message;
 | |
|     this.line = line;
 | |
|     this.col = col;
 | |
|     this.pos = pos;
 | |
|     this.stack = new Error().stack;
 | |
| };
 | |
| 
 | |
| JS_Parse_Error.prototype.toString = function() {
 | |
|     return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
 | |
| };
 | |
| 
 | |
| function js_error(message, filename, line, col, pos) {
 | |
|     throw new JS_Parse_Error(message, line, col, pos);
 | |
| };
 | |
| 
 | |
| function is_token(token, type, val) {
 | |
|     return token.type == type && (val == null || token.value == val);
 | |
| };
 | |
| 
 | |
| var EX_EOF = {};
 | |
| 
 | |
| function tokenizer($TEXT, filename) {
 | |
| 
 | |
|     var S = {
 | |
|         text            : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ''),
 | |
|         filename        : filename,
 | |
|         pos             : 0,
 | |
|         tokpos          : 0,
 | |
|         line            : 1,
 | |
|         tokline         : 0,
 | |
|         col             : 0,
 | |
|         tokcol          : 0,
 | |
|         newline_before  : false,
 | |
|         regex_allowed   : false,
 | |
|         comments_before : []
 | |
|     };
 | |
| 
 | |
|     function peek() { return S.text.charAt(S.pos); };
 | |
| 
 | |
|     function next(signal_eof, in_string) {
 | |
|         var ch = S.text.charAt(S.pos++);
 | |
|         if (signal_eof && !ch)
 | |
|             throw EX_EOF;
 | |
|         if (ch == "\n") {
 | |
|             S.newline_before = S.newline_before || !in_string;
 | |
|             ++S.line;
 | |
|             S.col = 0;
 | |
|         } else {
 | |
|             ++S.col;
 | |
|         }
 | |
|         return ch;
 | |
|     };
 | |
| 
 | |
|     function find(what, signal_eof) {
 | |
|         var pos = S.text.indexOf(what, S.pos);
 | |
|         if (signal_eof && pos == -1) throw EX_EOF;
 | |
|         return pos;
 | |
|     };
 | |
| 
 | |
|     function start_token() {
 | |
|         S.tokline = S.line;
 | |
|         S.tokcol = S.col;
 | |
|         S.tokpos = S.pos;
 | |
|     };
 | |
| 
 | |
|     function token(type, value, is_comment) {
 | |
|         S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) ||
 | |
|                            (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
 | |
|                            (type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
 | |
|         var ret = {
 | |
|             type   : type,
 | |
|             value  : value,
 | |
|             line   : S.tokline,
 | |
|             col    : S.tokcol,
 | |
|             pos    : S.tokpos,
 | |
|             endpos : S.pos,
 | |
|             nlb    : S.newline_before,
 | |
|             file   : filename
 | |
|         };
 | |
|         if (!is_comment) {
 | |
|             ret.comments_before = S.comments_before;
 | |
|             S.comments_before = [];
 | |
|             // make note of any newlines in the comments that came before
 | |
|             for (var i = 0, len = ret.comments_before.length; i < len; i++) {
 | |
|                 ret.nlb = ret.nlb || ret.comments_before[i].nlb;
 | |
|             }
 | |
|         }
 | |
|         S.newline_before = false;
 | |
|         return new AST_Token(ret);
 | |
|     };
 | |
| 
 | |
|     function skip_whitespace() {
 | |
|         while (WHITESPACE_CHARS(peek()))
 | |
|             next();
 | |
|     };
 | |
| 
 | |
|     function read_while(pred) {
 | |
|         var ret = "", ch, i = 0;
 | |
|         while ((ch = peek()) && pred(ch, i++))
 | |
|             ret += next();
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     function parse_error(err) {
 | |
|         js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
 | |
|     };
 | |
| 
 | |
|     function read_num(prefix) {
 | |
|         var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
 | |
|         var num = read_while(function(ch, i){
 | |
|             var code = ch.charCodeAt(0);
 | |
|             switch (code) {
 | |
|               case 120: case 88: // xX
 | |
|                 return has_x ? false : (has_x = true);
 | |
|               case 101: case 69: // eE
 | |
|                 return has_x ? true : has_e ? false : (has_e = after_e = true);
 | |
|               case 45: // -
 | |
|                 return after_e || (i == 0 && !prefix);
 | |
|               case 43: // +
 | |
|                 return after_e;
 | |
|               case (after_e = false, 46): // .
 | |
|                 return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
 | |
|             }
 | |
|             return is_alphanumeric_char(code);
 | |
|         });
 | |
|         if (prefix) num = prefix + num;
 | |
|         var valid = parse_js_number(num);
 | |
|         if (!isNaN(valid)) {
 | |
|             return token("num", valid);
 | |
|         } else {
 | |
|             parse_error("Invalid syntax: " + num);
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function read_escaped_char(in_string) {
 | |
|         var ch = next(true, in_string);
 | |
|         switch (ch.charCodeAt(0)) {
 | |
|           case 110 : return "\n";
 | |
|           case 114 : return "\r";
 | |
|           case 116 : return "\t";
 | |
|           case 98  : return "\b";
 | |
|           case 118 : return "\u000b"; // \v
 | |
|           case 102 : return "\f";
 | |
|           case 48  : return "\0";
 | |
|           case 120 : return String.fromCharCode(hex_bytes(2)); // \x
 | |
|           case 117 : return String.fromCharCode(hex_bytes(4)); // \u
 | |
|           case 10  : return ""; // newline
 | |
|           default  : return ch;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function hex_bytes(n) {
 | |
|         var num = 0;
 | |
|         for (; n > 0; --n) {
 | |
|             var digit = parseInt(next(true), 16);
 | |
|             if (isNaN(digit))
 | |
|                 parse_error("Invalid hex-character pattern in string");
 | |
|             num = (num << 4) | digit;
 | |
|         }
 | |
|         return num;
 | |
|     };
 | |
| 
 | |
|     var read_string = with_eof_error("Unterminated string constant", function(){
 | |
|         var quote = next(), ret = "";
 | |
|         for (;;) {
 | |
|             var ch = next(true);
 | |
|             if (ch == "\\") {
 | |
|                 // read OctalEscapeSequence (XXX: deprecated if "strict mode")
 | |
|                 // https://github.com/mishoo/UglifyJS/issues/178
 | |
|                 var octal_len = 0, first = null;
 | |
|                 ch = read_while(function(ch){
 | |
|                     if (ch >= "0" && ch <= "7") {
 | |
|                         if (!first) {
 | |
|                             first = ch;
 | |
|                             return ++octal_len;
 | |
|                         }
 | |
|                         else if (first <= "3" && octal_len <= 2) return ++octal_len;
 | |
|                         else if (first >= "4" && octal_len <= 1) return ++octal_len;
 | |
|                     }
 | |
|                     return false;
 | |
|                 });
 | |
|                 if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
 | |
|                 else ch = read_escaped_char(true);
 | |
|             }
 | |
|             else if (ch == quote) break;
 | |
|             ret += ch;
 | |
|         }
 | |
|         return token("string", ret);
 | |
|     });
 | |
| 
 | |
|     function read_line_comment() {
 | |
|         next();
 | |
|         var i = find("\n"), ret;
 | |
|         if (i == -1) {
 | |
|             ret = S.text.substr(S.pos);
 | |
|             S.pos = S.text.length;
 | |
|         } else {
 | |
|             ret = S.text.substring(S.pos, i);
 | |
|             S.pos = i;
 | |
|         }
 | |
|         return token("comment1", ret, true);
 | |
|     };
 | |
| 
 | |
|     var read_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
 | |
|         next();
 | |
|         var i = find("*/", true);
 | |
|         var text = S.text.substring(S.pos, i);
 | |
|         var a = text.split("\n"), n = a.length;
 | |
|         // update stream position
 | |
|         S.pos = i + 2;
 | |
|         S.line += n - 1;
 | |
|         if (n > 1) S.col = a[n - 1].length;
 | |
|         else S.col += a[n - 1].length;
 | |
|         S.col += 2;
 | |
|         S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
 | |
|         return token("comment2", text, true);
 | |
|     });
 | |
| 
 | |
|     function read_name() {
 | |
|         var backslash = false, name = "", ch, escaped = false, hex;
 | |
|         while ((ch = peek()) != null) {
 | |
|             if (!backslash) {
 | |
|                 if (ch == "\\") escaped = backslash = true, next();
 | |
|                 else if (is_identifier_char(ch)) name += next();
 | |
|                 else break;
 | |
|             }
 | |
|             else {
 | |
|                 if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
 | |
|                 ch = read_escaped_char();
 | |
|                 if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
 | |
|                 name += ch;
 | |
|                 backslash = false;
 | |
|             }
 | |
|         }
 | |
|         if (KEYWORDS(name) && escaped) {
 | |
|             hex = name.charCodeAt(0).toString(16).toUpperCase();
 | |
|             name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
 | |
|         }
 | |
|         return name;
 | |
|     };
 | |
| 
 | |
|     var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
 | |
|         var prev_backslash = false, ch, in_class = false;
 | |
|         while ((ch = next(true))) if (prev_backslash) {
 | |
|             regexp += "\\" + ch;
 | |
|             prev_backslash = false;
 | |
|         } else if (ch == "[") {
 | |
|             in_class = true;
 | |
|             regexp += ch;
 | |
|         } else if (ch == "]" && in_class) {
 | |
|             in_class = false;
 | |
|             regexp += ch;
 | |
|         } else if (ch == "/" && !in_class) {
 | |
|             break;
 | |
|         } else if (ch == "\\") {
 | |
|             prev_backslash = true;
 | |
|         } else {
 | |
|             regexp += ch;
 | |
|         }
 | |
|         var mods = read_name();
 | |
|         return token("regexp", new RegExp(regexp, mods));
 | |
|     });
 | |
| 
 | |
|     function read_operator(prefix) {
 | |
|         function grow(op) {
 | |
|             if (!peek()) return op;
 | |
|             var bigger = op + peek();
 | |
|             if (OPERATORS(bigger)) {
 | |
|                 next();
 | |
|                 return grow(bigger);
 | |
|             } else {
 | |
|                 return op;
 | |
|             }
 | |
|         };
 | |
|         return token("operator", grow(prefix || next()));
 | |
|     };
 | |
| 
 | |
|     function handle_slash() {
 | |
|         next();
 | |
|         var regex_allowed = S.regex_allowed;
 | |
|         switch (peek()) {
 | |
|           case "/":
 | |
|             S.comments_before.push(read_line_comment());
 | |
|             S.regex_allowed = regex_allowed;
 | |
|             return next_token();
 | |
|           case "*":
 | |
|             S.comments_before.push(read_multiline_comment());
 | |
|             S.regex_allowed = regex_allowed;
 | |
|             return next_token();
 | |
|         }
 | |
|         return S.regex_allowed ? read_regexp("") : read_operator("/");
 | |
|     };
 | |
| 
 | |
|     function handle_dot() {
 | |
|         next();
 | |
|         return is_digit(peek().charCodeAt(0))
 | |
|             ? read_num(".")
 | |
|             : token("punc", ".");
 | |
|     };
 | |
| 
 | |
|     function read_word() {
 | |
|         var word = read_name();
 | |
|         return KEYWORDS_ATOM(word) ? token("atom", word)
 | |
|             : !KEYWORDS(word) ? token("name", word)
 | |
|             : OPERATORS(word) ? token("operator", word)
 | |
|             : token("keyword", word);
 | |
|     };
 | |
| 
 | |
|     function with_eof_error(eof_error, cont) {
 | |
|         return function(x) {
 | |
|             try {
 | |
|                 return cont(x);
 | |
|             } catch(ex) {
 | |
|                 if (ex === EX_EOF) parse_error(eof_error);
 | |
|                 else throw ex;
 | |
|             }
 | |
|         };
 | |
|     };
 | |
| 
 | |
|     function next_token(force_regexp) {
 | |
|         if (force_regexp != null)
 | |
|             return read_regexp(force_regexp);
 | |
|         skip_whitespace();
 | |
|         start_token();
 | |
|         var ch = peek();
 | |
|         if (!ch) return token("eof");
 | |
|         var code = ch.charCodeAt(0);
 | |
|         switch (code) {
 | |
|           case 34: case 39: return read_string();
 | |
|           case 46: return handle_dot();
 | |
|           case 47: return handle_slash();
 | |
|         }
 | |
|         if (is_digit(code)) return read_num();
 | |
|         if (PUNC_CHARS(ch)) return token("punc", next());
 | |
|         if (OPERATOR_CHARS(ch)) return read_operator();
 | |
|         if (code == 92 || is_identifier_start(code)) return read_word();
 | |
|         parse_error("Unexpected character '" + ch + "'");
 | |
|     };
 | |
| 
 | |
|     next_token.context = function(nc) {
 | |
|         if (nc) S = nc;
 | |
|         return S;
 | |
|     };
 | |
| 
 | |
|     return next_token;
 | |
| 
 | |
| };
 | |
| 
 | |
| /* -----[ Parser (constants) ]----- */
 | |
| 
 | |
| var UNARY_PREFIX = makePredicate([
 | |
|     "typeof",
 | |
|     "void",
 | |
|     "delete",
 | |
|     "--",
 | |
|     "++",
 | |
|     "!",
 | |
|     "~",
 | |
|     "-",
 | |
|     "+"
 | |
| ]);
 | |
| 
 | |
| var UNARY_POSTFIX = makePredicate([ "--", "++" ]);
 | |
| 
 | |
| var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
 | |
| 
 | |
| var PRECEDENCE = (function(a, ret){
 | |
|     for (var i = 0, n = 1; i < a.length; ++i, ++n) {
 | |
|         var b = a[i];
 | |
|         for (var j = 0; j < b.length; ++j) {
 | |
|             ret[b[j]] = n;
 | |
|         }
 | |
|     }
 | |
|     return ret;
 | |
| })(
 | |
|     [
 | |
|         ["||"],
 | |
|         ["&&"],
 | |
|         ["|"],
 | |
|         ["^"],
 | |
|         ["&"],
 | |
|         ["==", "===", "!=", "!=="],
 | |
|         ["<", ">", "<=", ">=", "in", "instanceof"],
 | |
|         [">>", "<<", ">>>"],
 | |
|         ["+", "-"],
 | |
|         ["*", "/", "%"]
 | |
|     ],
 | |
|     {}
 | |
| );
 | |
| 
 | |
| var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
 | |
| 
 | |
| var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
 | |
| 
 | |
| /* -----[ Parser ]----- */
 | |
| 
 | |
| function parse($TEXT, options) {
 | |
| 
 | |
|     options = defaults(options, {
 | |
|         strict     : false,
 | |
|         filename   : null,
 | |
|         toplevel   : null,
 | |
|         expression : false
 | |
|     });
 | |
| 
 | |
|     var S = {
 | |
|         input         : typeof $TEXT == "string" ? tokenizer($TEXT, options.filename) : $TEXT,
 | |
|         token         : null,
 | |
|         prev          : null,
 | |
|         peeked        : null,
 | |
|         in_function   : 0,
 | |
|         in_directives : true,
 | |
|         in_loop       : 0,
 | |
|         labels        : []
 | |
|     };
 | |
| 
 | |
|     S.token = next();
 | |
| 
 | |
|     function is(type, value) {
 | |
|         return is_token(S.token, type, value);
 | |
|     };
 | |
| 
 | |
|     function peek() { return S.peeked || (S.peeked = S.input()); };
 | |
| 
 | |
|     function next() {
 | |
|         S.prev = S.token;
 | |
|         if (S.peeked) {
 | |
|             S.token = S.peeked;
 | |
|             S.peeked = null;
 | |
|         } else {
 | |
|             S.token = S.input();
 | |
|         }
 | |
|         S.in_directives = S.in_directives && (
 | |
|             S.token.type == "string" || is("punc", ";")
 | |
|         );
 | |
|         return S.token;
 | |
|     };
 | |
| 
 | |
|     function prev() {
 | |
|         return S.prev;
 | |
|     };
 | |
| 
 | |
|     function croak(msg, line, col, pos) {
 | |
|         var ctx = S.input.context();
 | |
|         js_error(msg,
 | |
|                  ctx.filename,
 | |
|                  line != null ? line : ctx.tokline,
 | |
|                  col != null ? col : ctx.tokcol,
 | |
|                  pos != null ? pos : ctx.tokpos);
 | |
|     };
 | |
| 
 | |
|     function token_error(token, msg) {
 | |
|         croak(msg, token.line, token.col);
 | |
|     };
 | |
| 
 | |
|     function unexpected(token) {
 | |
|         if (token == null)
 | |
|             token = S.token;
 | |
|         token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
 | |
|     };
 | |
| 
 | |
|     function expect_token(type, val) {
 | |
|         if (is(type, val)) {
 | |
|             return next();
 | |
|         }
 | |
|         token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
 | |
|     };
 | |
| 
 | |
|     function expect(punc) { return expect_token("punc", punc); };
 | |
| 
 | |
|     function can_insert_semicolon() {
 | |
|         return !options.strict && (
 | |
|             S.token.nlb || is("eof") || is("punc", "}")
 | |
|         );
 | |
|     };
 | |
| 
 | |
|     function semicolon() {
 | |
|         if (is("punc", ";")) next();
 | |
|         else if (!can_insert_semicolon()) unexpected();
 | |
|     };
 | |
| 
 | |
|     function parenthesised() {
 | |
|         expect("(");
 | |
|         var exp = expression(true);
 | |
|         expect(")");
 | |
|         return exp;
 | |
|     };
 | |
| 
 | |
|     function embed_tokens(parser) {
 | |
|         return function() {
 | |
|             var start = S.token;
 | |
|             var expr = parser();
 | |
|             var end = prev();
 | |
|             expr.start = start;
 | |
|             expr.end = end;
 | |
|             return expr;
 | |
|         };
 | |
|     };
 | |
| 
 | |
|     var statement = embed_tokens(function() {
 | |
|         var tmp;
 | |
|         if (is("operator", "/") || is("operator", "/=")) {
 | |
|             S.peeked = null;
 | |
|             S.token = S.input(S.token.value.substr(1)); // force regexp
 | |
|         }
 | |
|         switch (S.token.type) {
 | |
|           case "string":
 | |
|             var dir = S.in_directives, stat = simple_statement();
 | |
|             // XXXv2: decide how to fix directives
 | |
|             if (dir && stat.body instanceof AST_String && !is("punc", ","))
 | |
|                 return new AST_Directive({ value: stat.body.value });
 | |
|             return stat;
 | |
|           case "num":
 | |
|           case "regexp":
 | |
|           case "operator":
 | |
|           case "atom":
 | |
|             return simple_statement();
 | |
| 
 | |
|           case "name":
 | |
|             return is_token(peek(), "punc", ":")
 | |
|                 ? labeled_statement()
 | |
|                 : simple_statement();
 | |
| 
 | |
|           case "punc":
 | |
|             switch (S.token.value) {
 | |
|               case "{":
 | |
|                 return new AST_BlockStatement({
 | |
|                     start : S.token,
 | |
|                     body  : block_(),
 | |
|                     end   : prev()
 | |
|                 });
 | |
|               case "[":
 | |
|               case "(":
 | |
|                 return simple_statement();
 | |
|               case ";":
 | |
|                 next();
 | |
|                 return new AST_EmptyStatement();
 | |
|               default:
 | |
|                 unexpected();
 | |
|             }
 | |
| 
 | |
|           case "keyword":
 | |
|             switch (tmp = S.token.value, next(), tmp) {
 | |
|               case "break":
 | |
|                 return break_cont(AST_Break);
 | |
| 
 | |
|               case "continue":
 | |
|                 return break_cont(AST_Continue);
 | |
| 
 | |
|               case "debugger":
 | |
|                 semicolon();
 | |
|                 return new AST_Debugger();
 | |
| 
 | |
|               case "do":
 | |
|                 return new AST_Do({
 | |
|                     body      : in_loop(statement),
 | |
|                     condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), tmp)
 | |
|                 });
 | |
| 
 | |
|               case "while":
 | |
|                 return new AST_While({
 | |
|                     condition : parenthesised(),
 | |
|                     body      : in_loop(statement)
 | |
|                 });
 | |
| 
 | |
|               case "for":
 | |
|                 return for_();
 | |
| 
 | |
|               case "function":
 | |
|                 return function_(true);
 | |
| 
 | |
|               case "if":
 | |
|                 return if_();
 | |
| 
 | |
|               case "return":
 | |
|                 if (S.in_function == 0)
 | |
|                     croak("'return' outside of function");
 | |
|                 return new AST_Return({
 | |
|                     value: ( is("punc", ";")
 | |
|                              ? (next(), null)
 | |
|                              : can_insert_semicolon()
 | |
|                              ? null
 | |
|                              : (tmp = expression(true), semicolon(), tmp) )
 | |
|                 });
 | |
| 
 | |
|               case "switch":
 | |
|                 return new AST_Switch({
 | |
|                     expression : parenthesised(),
 | |
|                     body       : in_loop(switch_body_)
 | |
|                 });
 | |
| 
 | |
|               case "throw":
 | |
|                 if (S.token.nlb)
 | |
|                     croak("Illegal newline after 'throw'");
 | |
|                 return new AST_Throw({
 | |
|                     value: (tmp = expression(true), semicolon(), tmp)
 | |
|                 });
 | |
| 
 | |
|               case "try":
 | |
|                 return try_();
 | |
| 
 | |
|               case "var":
 | |
|                 return tmp = var_(), semicolon(), tmp;
 | |
| 
 | |
|               case "const":
 | |
|                 return tmp = const_(), semicolon(), tmp;
 | |
| 
 | |
|               case "with":
 | |
|                 return new AST_With({
 | |
|                     expression : parenthesised(),
 | |
|                     body       : statement()
 | |
|                 });
 | |
| 
 | |
|               default:
 | |
|                 unexpected();
 | |
|             }
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     function labeled_statement() {
 | |
|         var label = as_symbol(AST_Label);
 | |
|         if (find_if(function(l){ return l.name == label.name }, S.labels)) {
 | |
|             // ECMA-262, 12.12: An ECMAScript program is considered
 | |
|             // syntactically incorrect if it contains a
 | |
|             // LabelledStatement that is enclosed by a
 | |
|             // LabelledStatement with the same Identifier as label.
 | |
|             croak("Label " + label.name + " defined twice");
 | |
|         }
 | |
|         expect(":");
 | |
|         S.labels.push(label);
 | |
|         var stat = statement();
 | |
|         S.labels.pop();
 | |
|         return new AST_LabeledStatement({ body: stat, label: label });
 | |
|     };
 | |
| 
 | |
|     function simple_statement(tmp) {
 | |
|         return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) });
 | |
|     };
 | |
| 
 | |
|     function break_cont(type) {
 | |
|         var label = null;
 | |
|         if (!can_insert_semicolon()) {
 | |
|             label = as_symbol(AST_LabelRef, true);
 | |
|         }
 | |
|         if (label != null) {
 | |
|             if (!find_if(function(l){ return l.name == label.name }, S.labels))
 | |
|                 croak("Undefined label " + label.name);
 | |
|         }
 | |
|         else if (S.in_loop == 0)
 | |
|             croak(type.TYPE + " not inside a loop or switch");
 | |
|         semicolon();
 | |
|         return new type({ label: label });
 | |
|     };
 | |
| 
 | |
|     function for_() {
 | |
|         expect("(");
 | |
|         var init = null;
 | |
|         if (!is("punc", ";")) {
 | |
|             init = is("keyword", "var")
 | |
|                 ? (next(), var_(true))
 | |
|                 : expression(true, true);
 | |
|             if (is("operator", "in")) {
 | |
|                 if (init instanceof AST_Var && init.definitions.length > 1)
 | |
|                     croak("Only one variable declaration allowed in for..in loop");
 | |
|                 next();
 | |
|                 return for_in(init);
 | |
|             }
 | |
|         }
 | |
|         return regular_for(init);
 | |
|     };
 | |
| 
 | |
|     function regular_for(init) {
 | |
|         expect(";");
 | |
|         var test = is("punc", ";") ? null : expression(true);
 | |
|         expect(";");
 | |
|         var step = is("punc", ")") ? null : expression(true);
 | |
|         expect(")");
 | |
|         return new AST_For({
 | |
|             init      : init,
 | |
|             condition : test,
 | |
|             step      : step,
 | |
|             body      : in_loop(statement)
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function for_in(init) {
 | |
|         var lhs = init instanceof AST_Var ? init.definitions[0].name : null;
 | |
|         var obj = expression(true);
 | |
|         expect(")");
 | |
|         return new AST_ForIn({
 | |
|             init   : init,
 | |
|             name   : lhs,
 | |
|             object : obj,
 | |
|             body   : in_loop(statement)
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     var function_ = function(in_statement, ctor) {
 | |
|         var is_accessor = ctor === AST_Accessor;
 | |
|         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);
 | |
|         if (in_statement && !name)
 | |
|             unexpected();
 | |
|         expect("(");
 | |
|         if (!ctor) ctor = in_statement ? AST_Defun : AST_Function;
 | |
|         return new ctor({
 | |
|             name: name,
 | |
|             argnames: (function(first, a){
 | |
|                 while (!is("punc", ")")) {
 | |
|                     if (first) first = false; else expect(",");
 | |
|                     a.push(as_symbol(AST_SymbolFunarg));
 | |
|                 }
 | |
|                 next();
 | |
|                 return a;
 | |
|             })(true, []),
 | |
|             body: (function(loop, labels){
 | |
|                 ++S.in_function;
 | |
|                 S.in_directives = true;
 | |
|                 S.in_loop = 0;
 | |
|                 S.labels = [];
 | |
|                 var a = block_();
 | |
|                 --S.in_function;
 | |
|                 S.in_loop = loop;
 | |
|                 S.labels = labels;
 | |
|                 return a;
 | |
|             })(S.in_loop, S.labels)
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function if_() {
 | |
|         var cond = parenthesised(), body = statement(), belse = null;
 | |
|         if (is("keyword", "else")) {
 | |
|             next();
 | |
|             belse = statement();
 | |
|         }
 | |
|         return new AST_If({
 | |
|             condition   : cond,
 | |
|             body        : body,
 | |
|             alternative : belse
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function block_() {
 | |
|         expect("{");
 | |
|         var a = [];
 | |
|         while (!is("punc", "}")) {
 | |
|             if (is("eof")) unexpected();
 | |
|             a.push(statement());
 | |
|         }
 | |
|         next();
 | |
|         return a;
 | |
|     };
 | |
| 
 | |
|     function switch_body_() {
 | |
|         expect("{");
 | |
|         var a = [], cur = null, branch = null, tmp;
 | |
|         while (!is("punc", "}")) {
 | |
|             if (is("eof")) unexpected();
 | |
|             if (is("keyword", "case")) {
 | |
|                 if (branch) branch.end = prev();
 | |
|                 cur = [];
 | |
|                 branch = new AST_Case({
 | |
|                     start      : (tmp = S.token, next(), tmp),
 | |
|                     expression : expression(true),
 | |
|                     body       : cur
 | |
|                 });
 | |
|                 a.push(branch);
 | |
|                 expect(":");
 | |
|             }
 | |
|             else if (is("keyword", "default")) {
 | |
|                 if (branch) branch.end = prev();
 | |
|                 cur = [];
 | |
|                 branch = new AST_Default({
 | |
|                     start : (tmp = S.token, next(), expect(":"), tmp),
 | |
|                     body  : cur
 | |
|                 });
 | |
|                 a.push(branch);
 | |
|             }
 | |
|             else {
 | |
|                 if (!cur) unexpected();
 | |
|                 cur.push(statement());
 | |
|             }
 | |
|         }
 | |
|         if (branch) branch.end = prev();
 | |
|         next();
 | |
|         return a;
 | |
|     };
 | |
| 
 | |
|     function try_() {
 | |
|         var body = block_(), bcatch = null, bfinally = null;
 | |
|         if (is("keyword", "catch")) {
 | |
|             var start = S.token;
 | |
|             next();
 | |
|             expect("(");
 | |
|             var name = as_symbol(AST_SymbolCatch);
 | |
|             expect(")");
 | |
|             bcatch = new AST_Catch({
 | |
|                 start   : start,
 | |
|                 argname : name,
 | |
|                 body    : block_(),
 | |
|                 end     : prev()
 | |
|             });
 | |
|         }
 | |
|         if (is("keyword", "finally")) {
 | |
|             var start = S.token;
 | |
|             next();
 | |
|             bfinally = new AST_Finally({
 | |
|                 start : start,
 | |
|                 body  : block_(),
 | |
|                 end   : prev()
 | |
|             });
 | |
|         }
 | |
|         if (!bcatch && !bfinally)
 | |
|             croak("Missing catch/finally blocks");
 | |
|         return new AST_Try({
 | |
|             body     : body,
 | |
|             bcatch   : bcatch,
 | |
|             bfinally : bfinally
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function vardefs(no_in, in_const) {
 | |
|         var a = [];
 | |
|         for (;;) {
 | |
|             a.push(new AST_VarDef({
 | |
|                 start : S.token,
 | |
|                 name  : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
 | |
|                 value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
 | |
|                 end   : prev()
 | |
|             }));
 | |
|             if (!is("punc", ","))
 | |
|                 break;
 | |
|             next();
 | |
|         }
 | |
|         return a;
 | |
|     };
 | |
| 
 | |
|     var var_ = function(no_in) {
 | |
|         return new AST_Var({
 | |
|             start       : prev(),
 | |
|             definitions : vardefs(no_in, false),
 | |
|             end         : prev()
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     var const_ = function() {
 | |
|         return new AST_Const({
 | |
|             start       : prev(),
 | |
|             definitions : vardefs(false, true),
 | |
|             end         : prev()
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     var new_ = function() {
 | |
|         var start = S.token;
 | |
|         expect_token("operator", "new");
 | |
|         var newexp = expr_atom(false), args;
 | |
|         if (is("punc", "(")) {
 | |
|             next();
 | |
|             args = expr_list(")");
 | |
|         } else {
 | |
|             args = [];
 | |
|         }
 | |
|         return subscripts(new AST_New({
 | |
|             start      : start,
 | |
|             expression : newexp,
 | |
|             args       : args,
 | |
|             end        : prev()
 | |
|         }), true);
 | |
|     };
 | |
| 
 | |
|     function as_atom_node() {
 | |
|         var tok = S.token, ret;
 | |
|         switch (tok.type) {
 | |
|           case "name":
 | |
|             return as_symbol(AST_SymbolRef);
 | |
|           case "num":
 | |
|             ret = new AST_Number({ start: tok, end: tok, value: tok.value });
 | |
|             break;
 | |
|           case "string":
 | |
|             ret = new AST_String({ start: tok, end: tok, value: tok.value });
 | |
|             break;
 | |
|           case "regexp":
 | |
|             ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
 | |
|             break;
 | |
|           case "atom":
 | |
|             switch (tok.value) {
 | |
|               case "false":
 | |
|                 ret = new AST_False({ start: tok, end: tok });
 | |
|                 break;
 | |
|               case "true":
 | |
|                 ret = new AST_True({ start: tok, end: tok });
 | |
|                 break;
 | |
|               case "null":
 | |
|                 ret = new AST_Null({ start: tok, end: tok });
 | |
|                 break;
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         next();
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     var expr_atom = function(allow_calls) {
 | |
|         if (is("operator", "new")) {
 | |
|             return new_();
 | |
|         }
 | |
|         var start = S.token;
 | |
|         if (is("punc")) {
 | |
|             switch (start.value) {
 | |
|               case "(":
 | |
|                 next();
 | |
|                 var ex = expression(true);
 | |
|                 ex.start = start;
 | |
|                 ex.end = S.token;
 | |
|                 expect(")");
 | |
|                 return subscripts(ex, allow_calls);
 | |
|               case "[":
 | |
|                 return subscripts(array_(), allow_calls);
 | |
|               case "{":
 | |
|                 return subscripts(object_(), allow_calls);
 | |
|             }
 | |
|             unexpected();
 | |
|         }
 | |
|         if (is("keyword", "function")) {
 | |
|             next();
 | |
|             var func = function_(false);
 | |
|             func.start = start;
 | |
|             func.end = prev();
 | |
|             return subscripts(func, allow_calls);
 | |
|         }
 | |
|         if (ATOMIC_START_TOKEN[S.token.type]) {
 | |
|             return subscripts(as_atom_node(), allow_calls);
 | |
|         }
 | |
|         unexpected();
 | |
|     };
 | |
| 
 | |
|     function expr_list(closing, allow_trailing_comma, allow_empty) {
 | |
|         var first = true, a = [];
 | |
|         while (!is("punc", closing)) {
 | |
|             if (first) first = false; else expect(",");
 | |
|             if (allow_trailing_comma && is("punc", closing)) break;
 | |
|             if (is("punc", ",") && allow_empty) {
 | |
|                 a.push(new AST_Hole({ start: S.token, end: S.token }));
 | |
|             } else {
 | |
|                 a.push(expression(false));
 | |
|             }
 | |
|         }
 | |
|         next();
 | |
|         return a;
 | |
|     };
 | |
| 
 | |
|     var array_ = embed_tokens(function() {
 | |
|         expect("[");
 | |
|         return new AST_Array({
 | |
|             elements: expr_list("]", !options.strict, true)
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     var object_ = embed_tokens(function() {
 | |
|         expect("{");
 | |
|         var first = true, a = [];
 | |
|         while (!is("punc", "}")) {
 | |
|             if (first) first = false; else expect(",");
 | |
|             if (!options.strict && is("punc", "}"))
 | |
|                 // allow trailing comma
 | |
|                 break;
 | |
|             var start = S.token;
 | |
|             var type = start.type;
 | |
|             var name = as_property_name();
 | |
|             if (type == "name" && !is("punc", ":")) {
 | |
|                 if (name == "get") {
 | |
|                     a.push(new AST_ObjectGetter({
 | |
|                         start : start,
 | |
|                         key   : name,
 | |
|                         value : function_(false, AST_Accessor),
 | |
|                         end   : prev()
 | |
|                     }));
 | |
|                     continue;
 | |
|                 }
 | |
|                 if (name == "set") {
 | |
|                     a.push(new AST_ObjectSetter({
 | |
|                         start : start,
 | |
|                         key   : name,
 | |
|                         value : function_(false, AST_Accessor),
 | |
|                         end   : prev()
 | |
|                     }));
 | |
|                     continue;
 | |
|                 }
 | |
|             }
 | |
|             expect(":");
 | |
|             a.push(new AST_ObjectKeyVal({
 | |
|                 start : start,
 | |
|                 key   : name,
 | |
|                 value : expression(false),
 | |
|                 end   : prev()
 | |
|             }));
 | |
|         }
 | |
|         next();
 | |
|         return new AST_Object({ properties: a });
 | |
|     });
 | |
| 
 | |
|     function as_property_name() {
 | |
|         var tmp = S.token;
 | |
|         next();
 | |
|         switch (tmp.type) {
 | |
|           case "num":
 | |
|           case "string":
 | |
|           case "name":
 | |
|           case "operator":
 | |
|           case "keyword":
 | |
|           case "atom":
 | |
|             return tmp.value;
 | |
|           default:
 | |
|             unexpected();
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function as_name() {
 | |
|         var tmp = S.token;
 | |
|         next();
 | |
|         switch (tmp.type) {
 | |
|           case "name":
 | |
|           case "operator":
 | |
|           case "keyword":
 | |
|           case "atom":
 | |
|             return tmp.value;
 | |
|           default:
 | |
|             unexpected();
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function as_symbol(type, noerror) {
 | |
|         if (!is("name")) {
 | |
|             if (!noerror) croak("Name expected");
 | |
|             return null;
 | |
|         }
 | |
|         var name = S.token.value;
 | |
|         var sym = new (name == "this" ? AST_This : type)({
 | |
|             name  : String(S.token.value),
 | |
|             start : S.token,
 | |
|             end   : S.token
 | |
|         });
 | |
|         next();
 | |
|         return sym;
 | |
|     };
 | |
| 
 | |
|     var subscripts = function(expr, allow_calls) {
 | |
|         var start = expr.start;
 | |
|         if (is("punc", ".")) {
 | |
|             next();
 | |
|             return subscripts(new AST_Dot({
 | |
|                 start      : start,
 | |
|                 expression : expr,
 | |
|                 property   : as_name(),
 | |
|                 end        : prev()
 | |
|             }), allow_calls);
 | |
|         }
 | |
|         if (is("punc", "[")) {
 | |
|             next();
 | |
|             var prop = expression(true);
 | |
|             expect("]");
 | |
|             return subscripts(new AST_Sub({
 | |
|                 start      : start,
 | |
|                 expression : expr,
 | |
|                 property   : prop,
 | |
|                 end        : prev()
 | |
|             }), allow_calls);
 | |
|         }
 | |
|         if (allow_calls && is("punc", "(")) {
 | |
|             next();
 | |
|             return subscripts(new AST_Call({
 | |
|                 start      : start,
 | |
|                 expression : expr,
 | |
|                 args       : expr_list(")"),
 | |
|                 end        : prev()
 | |
|             }), true);
 | |
|         }
 | |
|         return expr;
 | |
|     };
 | |
| 
 | |
|     var maybe_unary = function(allow_calls) {
 | |
|         var start = S.token;
 | |
|         if (is("operator") && UNARY_PREFIX(start.value)) {
 | |
|             next();
 | |
|             var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls));
 | |
|             ex.start = start;
 | |
|             ex.end = prev();
 | |
|             return ex;
 | |
|         }
 | |
|         var val = expr_atom(allow_calls);
 | |
|         while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
 | |
|             val = make_unary(AST_UnaryPostfix, S.token.value, val);
 | |
|             val.start = start;
 | |
|             val.end = S.token;
 | |
|             next();
 | |
|         }
 | |
|         return val;
 | |
|     };
 | |
| 
 | |
|     function make_unary(ctor, op, expr) {
 | |
|         if ((op == "++" || op == "--") && !is_assignable(expr))
 | |
|             croak("Invalid use of " + op + " operator");
 | |
|         return new ctor({ operator: op, expression: expr });
 | |
|     };
 | |
| 
 | |
|     var expr_op = function(left, min_prec, no_in) {
 | |
|         var op = is("operator") ? S.token.value : null;
 | |
|         if (op == "in" && no_in) op = null;
 | |
|         var prec = op != null ? PRECEDENCE[op] : null;
 | |
|         if (prec != null && prec > min_prec) {
 | |
|             next();
 | |
|             var right = expr_op(maybe_unary(true), prec, no_in);
 | |
|             return expr_op(new AST_Binary({
 | |
|                 start    : left.start,
 | |
|                 left     : left,
 | |
|                 operator : op,
 | |
|                 right    : right,
 | |
|                 end      : right.end
 | |
|             }), min_prec, no_in);
 | |
|         }
 | |
|         return left;
 | |
|     };
 | |
| 
 | |
|     function expr_ops(no_in) {
 | |
|         return expr_op(maybe_unary(true), 0, no_in);
 | |
|     };
 | |
| 
 | |
|     var maybe_conditional = function(no_in) {
 | |
|         var start = S.token;
 | |
|         var expr = expr_ops(no_in);
 | |
|         if (is("operator", "?")) {
 | |
|             next();
 | |
|             var yes = expression(false);
 | |
|             expect(":");
 | |
|             return new AST_Conditional({
 | |
|                 start       : start,
 | |
|                 condition   : expr,
 | |
|                 consequent  : yes,
 | |
|                 alternative : expression(false, no_in),
 | |
|                 end         : peek()
 | |
|             });
 | |
|         }
 | |
|         return expr;
 | |
|     };
 | |
| 
 | |
|     function is_assignable(expr) {
 | |
|         if (!options.strict) return true;
 | |
|         if (expr instanceof AST_This) return false;
 | |
|         return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
 | |
|     };
 | |
| 
 | |
|     var maybe_assign = function(no_in) {
 | |
|         var start = S.token;
 | |
|         var left = maybe_conditional(no_in), val = S.token.value;
 | |
|         if (is("operator") && ASSIGNMENT(val)) {
 | |
|             if (is_assignable(left)) {
 | |
|                 next();
 | |
|                 return new AST_Assign({
 | |
|                     start    : start,
 | |
|                     left     : left,
 | |
|                     operator : val,
 | |
|                     right    : maybe_assign(no_in),
 | |
|                     end      : prev()
 | |
|                 });
 | |
|             }
 | |
|             croak("Invalid assignment");
 | |
|         }
 | |
|         return left;
 | |
|     };
 | |
| 
 | |
|     var expression = function(commas, no_in) {
 | |
|         var start = S.token;
 | |
|         var expr = maybe_assign(no_in);
 | |
|         if (commas && is("punc", ",")) {
 | |
|             next();
 | |
|             return new AST_Seq({
 | |
|                 start  : start,
 | |
|                 car    : expr,
 | |
|                 cdr    : expression(true, no_in),
 | |
|                 end    : peek()
 | |
|             });
 | |
|         }
 | |
|         return expr;
 | |
|     };
 | |
| 
 | |
|     function in_loop(cont) {
 | |
|         ++S.in_loop;
 | |
|         var ret = cont();
 | |
|         --S.in_loop;
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     if (options.expression) {
 | |
|         return expression(true);
 | |
|     }
 | |
| 
 | |
|     return (function(){
 | |
|         var start = S.token;
 | |
|         var body = [];
 | |
|         while (!is("eof"))
 | |
|             body.push(statement());
 | |
|         var end = prev();
 | |
|         var toplevel = options.toplevel;
 | |
|         if (toplevel) {
 | |
|             toplevel.body = toplevel.body.concat(body);
 | |
|             toplevel.end = end;
 | |
|         } else {
 | |
|             toplevel = new AST_Toplevel({ start: start, body: body, end: end });
 | |
|         }
 | |
|         return toplevel;
 | |
|     })();
 | |
| 
 | |
| };
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| // Tree transformer helpers.
 | |
| 
 | |
| function TreeTransformer(before, after) {
 | |
|     TreeWalker.call(this);
 | |
|     this.before = before;
 | |
|     this.after = after;
 | |
| }
 | |
| TreeTransformer.prototype = new TreeWalker;
 | |
| 
 | |
| (function(undefined){
 | |
| 
 | |
|     function _(node, descend) {
 | |
|         node.DEFMETHOD("transform", function(tw, in_list){
 | |
|             var x, y;
 | |
|             tw.push(this);
 | |
|             if (tw.before) x = tw.before(this, descend, in_list);
 | |
|             if (x === undefined) {
 | |
|                 if (!tw.after) {
 | |
|                     x = this;
 | |
|                     descend(x, tw);
 | |
|                 } else {
 | |
|                     tw.stack[tw.stack.length - 1] = x = this.clone();
 | |
|                     descend(x, tw);
 | |
|                     y = tw.after(x, in_list);
 | |
|                     if (y !== undefined) x = y;
 | |
|                 }
 | |
|             }
 | |
|             tw.pop();
 | |
|             return x;
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function do_list(list, tw) {
 | |
|         return MAP(list, function(node){
 | |
|             return node.transform(tw, true);
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     _(AST_Node, noop);
 | |
| 
 | |
|     _(AST_LabeledStatement, function(self, tw){
 | |
|         self.label = self.label.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_SimpleStatement, function(self, tw){
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Block, function(self, tw){
 | |
|         self.body = do_list(self.body, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_DWLoop, function(self, tw){
 | |
|         self.condition = self.condition.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_For, function(self, tw){
 | |
|         if (self.init) self.init = self.init.transform(tw);
 | |
|         if (self.condition) self.condition = self.condition.transform(tw);
 | |
|         if (self.step) self.step = self.step.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_ForIn, function(self, tw){
 | |
|         self.init = self.init.transform(tw);
 | |
|         self.object = self.object.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_With, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Exit, function(self, tw){
 | |
|         if (self.value) self.value = self.value.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_LoopControl, function(self, tw){
 | |
|         if (self.label) self.label = self.label.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_If, function(self, tw){
 | |
|         self.condition = self.condition.transform(tw);
 | |
|         self.body = self.body.transform(tw);
 | |
|         if (self.alternative) self.alternative = self.alternative.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Switch, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|         self.body = do_list(self.body, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Case, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|         self.body = do_list(self.body, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Try, function(self, tw){
 | |
|         self.body = do_list(self.body, tw);
 | |
|         if (self.bcatch) self.bcatch = self.bcatch.transform(tw);
 | |
|         if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Catch, function(self, tw){
 | |
|         self.argname = self.argname.transform(tw);
 | |
|         self.body = do_list(self.body, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Definitions, function(self, tw){
 | |
|         self.definitions = do_list(self.definitions, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_VarDef, function(self, tw){
 | |
|         self.name = self.name.transform(tw);
 | |
|         if (self.value) self.value = self.value.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Lambda, function(self, tw){
 | |
|         if (self.name) self.name = self.name.transform(tw);
 | |
|         self.argnames = do_list(self.argnames, tw);
 | |
|         self.body = do_list(self.body, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Call, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|         self.args = do_list(self.args, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Seq, function(self, tw){
 | |
|         self.car = self.car.transform(tw);
 | |
|         self.cdr = self.cdr.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Dot, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Sub, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|         self.property = self.property.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Unary, function(self, tw){
 | |
|         self.expression = self.expression.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Binary, function(self, tw){
 | |
|         self.left = self.left.transform(tw);
 | |
|         self.right = self.right.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Conditional, function(self, tw){
 | |
|         self.condition = self.condition.transform(tw);
 | |
|         self.consequent = self.consequent.transform(tw);
 | |
|         self.alternative = self.alternative.transform(tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Array, function(self, tw){
 | |
|         self.elements = do_list(self.elements, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_Object, function(self, tw){
 | |
|         self.properties = do_list(self.properties, tw);
 | |
|     });
 | |
| 
 | |
|     _(AST_ObjectProperty, function(self, tw){
 | |
|         self.value = self.value.transform(tw);
 | |
|     });
 | |
| 
 | |
| })();
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| function SymbolDef(scope, index, orig) {
 | |
|     this.name = orig.name;
 | |
|     this.orig = [ orig ];
 | |
|     this.scope = scope;
 | |
|     this.references = [];
 | |
|     this.global = false;
 | |
|     this.mangled_name = null;
 | |
|     this.undeclared = false;
 | |
|     this.constant = false;
 | |
|     this.index = index;
 | |
| };
 | |
| 
 | |
| SymbolDef.prototype = {
 | |
|     unmangleable: function(options) {
 | |
|         return (this.global && !(options && options.toplevel))
 | |
|             || this.undeclared
 | |
|             || (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with));
 | |
|     },
 | |
|     mangle: function(options) {
 | |
|         if (!this.mangled_name && !this.unmangleable(options)) {
 | |
|             var s = this.scope;
 | |
|             if (this.orig[0] instanceof AST_SymbolLambda && !options.screw_ie8)
 | |
|                 s = s.parent_scope;
 | |
|             this.mangled_name = s.next_mangled(options);
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
 | |
|     // This does what ast_add_scope did in UglifyJS v1.
 | |
|     //
 | |
|     // Part of it could be done at parse time, but it would complicate
 | |
|     // the parser (and it's already kinda complex).  It's also worth
 | |
|     // having it separated because we might need to call it multiple
 | |
|     // times on the same tree.
 | |
| 
 | |
|     // pass 1: setup scope chaining and handle definitions
 | |
|     var self = this;
 | |
|     var scope = self.parent_scope = null;
 | |
|     var labels = new Dictionary();
 | |
|     var nesting = 0;
 | |
|     var tw = new TreeWalker(function(node, descend){
 | |
|         if (node instanceof AST_Scope) {
 | |
|             node.init_scope_vars(nesting);
 | |
|             var save_scope = node.parent_scope = scope;
 | |
|             var save_labels = labels;
 | |
|             ++nesting;
 | |
|             scope = node;
 | |
|             labels = new Dictionary();
 | |
|             descend();
 | |
|             labels = save_labels;
 | |
|             scope = save_scope;
 | |
|             --nesting;
 | |
|             return true;        // don't descend again in TreeWalker
 | |
|         }
 | |
|         if (node instanceof AST_Directive) {
 | |
|             node.scope = scope;
 | |
|             push_uniq(scope.directives, node.value);
 | |
|             return true;
 | |
|         }
 | |
|         if (node instanceof AST_With) {
 | |
|             for (var s = scope; s; s = s.parent_scope)
 | |
|                 s.uses_with = true;
 | |
|             return;
 | |
|         }
 | |
|         if (node instanceof AST_LabeledStatement) {
 | |
|             var l = node.label;
 | |
|             if (labels.has(l.name))
 | |
|                 throw new Error(string_template("Label {name} defined twice", l));
 | |
|             labels.set(l.name, l);
 | |
|             descend();
 | |
|             labels.del(l.name);
 | |
|             return true;        // no descend again
 | |
|         }
 | |
|         if (node instanceof AST_Symbol) {
 | |
|             node.scope = scope;
 | |
|         }
 | |
|         if (node instanceof AST_Label) {
 | |
|             node.thedef = node;
 | |
|             node.init_scope_vars();
 | |
|         }
 | |
|         if (node instanceof AST_SymbolLambda) {
 | |
|             scope.def_function(node);
 | |
|         }
 | |
|         else if (node instanceof AST_SymbolDefun) {
 | |
|             // Careful here, the scope where this should be defined is
 | |
|             // the parent scope.  The reason is that we enter a new
 | |
|             // scope when we encounter the AST_Defun node (which is
 | |
|             // instanceof AST_Scope) but we get to the symbol a bit
 | |
|             // later.
 | |
|             (node.scope = scope.parent_scope).def_function(node);
 | |
|         }
 | |
|         else if (node instanceof AST_SymbolVar
 | |
|                  || node instanceof AST_SymbolConst) {
 | |
|             var def = scope.def_variable(node);
 | |
|             def.constant = node instanceof AST_SymbolConst;
 | |
|             def.init = tw.parent().value;
 | |
|         }
 | |
|         else if (node instanceof AST_SymbolCatch) {
 | |
|             // XXX: this is wrong according to ECMA-262 (12.4).  the
 | |
|             // `catch` argument name should be visible only inside the
 | |
|             // catch block.  For a quick fix AST_Catch should inherit
 | |
|             // from AST_Scope.  Keeping it this way because of IE,
 | |
|             // which doesn't obey the standard. (it introduces the
 | |
|             // identifier in the enclosing scope)
 | |
|             scope.def_variable(node);
 | |
|         }
 | |
|         if (node instanceof AST_LabelRef) {
 | |
|             var sym = labels.get(node.name);
 | |
|             if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
 | |
|                 name: node.name,
 | |
|                 line: node.start.line,
 | |
|                 col: node.start.col
 | |
|             }));
 | |
|             node.thedef = sym;
 | |
|         }
 | |
|     });
 | |
|     self.walk(tw);
 | |
| 
 | |
|     // pass 2: find back references and eval
 | |
|     var func = null;
 | |
|     var globals = self.globals = new Dictionary();
 | |
|     var tw = new TreeWalker(function(node, descend){
 | |
|         if (node instanceof AST_Lambda) {
 | |
|             var prev_func = func;
 | |
|             func = node;
 | |
|             descend();
 | |
|             func = prev_func;
 | |
|             return true;
 | |
|         }
 | |
|         if (node instanceof AST_LabelRef) {
 | |
|             node.reference();
 | |
|             return true;
 | |
|         }
 | |
|         if (node instanceof AST_SymbolRef) {
 | |
|             var name = node.name;
 | |
|             var sym = node.scope.find_variable(name);
 | |
|             if (!sym) {
 | |
|                 var g;
 | |
|                 if (globals.has(name)) {
 | |
|                     g = globals.get(name);
 | |
|                 } else {
 | |
|                     g = new SymbolDef(self, globals.size(), node);
 | |
|                     g.undeclared = true;
 | |
|                     g.global = true;
 | |
|                     globals.set(name, g);
 | |
|                 }
 | |
|                 node.thedef = g;
 | |
|                 if (name == "eval" && tw.parent() instanceof AST_Call) {
 | |
|                     for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)
 | |
|                         s.uses_eval = true;
 | |
|                 }
 | |
|                 if (name == "arguments") {
 | |
|                     func.uses_arguments = true;
 | |
|                 }
 | |
|             } else {
 | |
|                 node.thedef = sym;
 | |
|             }
 | |
|             node.reference();
 | |
|             return true;
 | |
|         }
 | |
|     });
 | |
|     self.walk(tw);
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
 | |
|     this.directives = [];     // contains the directives defined in this scope, i.e. "use strict"
 | |
|     this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
 | |
|     this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
 | |
|     this.uses_with = false;   // will be set to true if this or some nested scope uses the `with` statement
 | |
|     this.uses_eval = false;   // will be set to true if this or nested scope uses the global `eval`
 | |
|     this.parent_scope = null; // the parent scope
 | |
|     this.enclosed = [];       // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
 | |
|     this.cname = -1;          // the current index for mangling functions/variables
 | |
|     this.nesting = nesting;   // the nesting level of this scope (0 means toplevel)
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("strict", function(){
 | |
|     return this.has_directive("use strict");
 | |
| });
 | |
| 
 | |
| AST_Lambda.DEFMETHOD("init_scope_vars", function(){
 | |
|     AST_Scope.prototype.init_scope_vars.apply(this, arguments);
 | |
|     this.uses_arguments = false;
 | |
| });
 | |
| 
 | |
| AST_SymbolRef.DEFMETHOD("reference", function() {
 | |
|     var def = this.definition();
 | |
|     def.references.push(this);
 | |
|     var s = this.scope;
 | |
|     while (s) {
 | |
|         push_uniq(s.enclosed, def);
 | |
|         if (s === def.scope) break;
 | |
|         s = s.parent_scope;
 | |
|     }
 | |
|     this.frame = this.scope.nesting - def.scope.nesting;
 | |
| });
 | |
| 
 | |
| AST_Label.DEFMETHOD("init_scope_vars", function(){
 | |
|     this.references = [];
 | |
| });
 | |
| 
 | |
| AST_LabelRef.DEFMETHOD("reference", function(){
 | |
|     this.thedef.references.push(this);
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("find_variable", function(name){
 | |
|     if (name instanceof AST_Symbol) name = name.name;
 | |
|     return this.variables.get(name)
 | |
|         || (this.parent_scope && this.parent_scope.find_variable(name));
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("has_directive", function(value){
 | |
|     return this.parent_scope && this.parent_scope.has_directive(value)
 | |
|         || (this.directives.indexOf(value) >= 0 ? this : null);
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("def_function", function(symbol){
 | |
|     this.functions.set(symbol.name, this.def_variable(symbol));
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("def_variable", function(symbol){
 | |
|     var def;
 | |
|     if (!this.variables.has(symbol.name)) {
 | |
|         def = new SymbolDef(this, this.variables.size(), symbol);
 | |
|         this.variables.set(symbol.name, def);
 | |
|         def.global = !this.parent_scope;
 | |
|     } else {
 | |
|         def = this.variables.get(symbol.name);
 | |
|         def.orig.push(symbol);
 | |
|     }
 | |
|     return symbol.thedef = def;
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("next_mangled", function(options){
 | |
|     var ext = this.enclosed;
 | |
|     out: while (true) {
 | |
|         var m = base54(++this.cname);
 | |
|         if (!is_identifier(m)) continue; // skip over "do"
 | |
|         // we must ensure that the mangled name does not shadow a name
 | |
|         // from some parent scope that is referenced in this or in
 | |
|         // inner scopes.
 | |
|         for (var i = ext.length; --i >= 0;) {
 | |
|             var sym = ext[i];
 | |
|             var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
 | |
|             if (m == name) continue out;
 | |
|         }
 | |
|         return m;
 | |
|     }
 | |
| });
 | |
| 
 | |
| AST_Scope.DEFMETHOD("references", function(sym){
 | |
|     if (sym instanceof AST_Symbol) sym = sym.definition();
 | |
|     return this.enclosed.indexOf(sym) < 0 ? null : sym;
 | |
| });
 | |
| 
 | |
| AST_Symbol.DEFMETHOD("unmangleable", function(options){
 | |
|     return this.definition().unmangleable(options);
 | |
| });
 | |
| 
 | |
| // property accessors are not mangleable
 | |
| AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){
 | |
|     return true;
 | |
| });
 | |
| 
 | |
| // labels are always mangleable
 | |
| AST_Label.DEFMETHOD("unmangleable", function(){
 | |
|     return false;
 | |
| });
 | |
| 
 | |
| AST_Symbol.DEFMETHOD("unreferenced", function(){
 | |
|     return this.definition().references.length == 0
 | |
|         && !(this.scope.uses_eval || this.scope.uses_with);
 | |
| });
 | |
| 
 | |
| AST_Symbol.DEFMETHOD("undeclared", function(){
 | |
|     return this.definition().undeclared;
 | |
| });
 | |
| 
 | |
| AST_LabelRef.DEFMETHOD("undeclared", function(){
 | |
|     return false;
 | |
| });
 | |
| 
 | |
| AST_Label.DEFMETHOD("undeclared", function(){
 | |
|     return false;
 | |
| });
 | |
| 
 | |
| AST_Symbol.DEFMETHOD("definition", function(){
 | |
|     return this.thedef;
 | |
| });
 | |
| 
 | |
| AST_Symbol.DEFMETHOD("global", function(){
 | |
|     return this.definition().global;
 | |
| });
 | |
| 
 | |
| AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
 | |
|     return defaults(options, {
 | |
|         except   : [],
 | |
|         eval     : false,
 | |
|         sort     : false,
 | |
|         toplevel : false,
 | |
|         screw_ie8 : false
 | |
|     });
 | |
| });
 | |
| 
 | |
| AST_Toplevel.DEFMETHOD("mangle_names", function(options){
 | |
|     options = this._default_mangler_options(options);
 | |
|     // We only need to mangle declaration nodes.  Special logic wired
 | |
|     // into the code generator will display the mangled name if it's
 | |
|     // present (and for AST_SymbolRef-s it'll use the mangled name of
 | |
|     // the AST_SymbolDeclaration that it points to).
 | |
|     var lname = -1;
 | |
|     var to_mangle = [];
 | |
|     var tw = new TreeWalker(function(node, descend){
 | |
|         if (node instanceof AST_LabeledStatement) {
 | |
|             // lname is incremented when we get to the AST_Label
 | |
|             var save_nesting = lname;
 | |
|             descend();
 | |
|             lname = save_nesting;
 | |
|             return true;        // don't descend again in TreeWalker
 | |
|         }
 | |
|         if (node instanceof AST_Scope) {
 | |
|             var p = tw.parent(), a = [];
 | |
|             node.variables.each(function(symbol){
 | |
|                 if (options.except.indexOf(symbol.name) < 0) {
 | |
|                     a.push(symbol);
 | |
|                 }
 | |
|             });
 | |
|             if (options.sort) a.sort(function(a, b){
 | |
|                 return b.references.length - a.references.length;
 | |
|             });
 | |
|             to_mangle.push.apply(to_mangle, a);
 | |
|             return;
 | |
|         }
 | |
|         if (node instanceof AST_Label) {
 | |
|             var name;
 | |
|             do name = base54(++lname); while (!is_identifier(name));
 | |
|             node.mangled_name = name;
 | |
|             return true;
 | |
|         }
 | |
|     });
 | |
|     this.walk(tw);
 | |
|     to_mangle.forEach(function(def){ def.mangle(options) });
 | |
| });
 | |
| 
 | |
| AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
 | |
|     options = this._default_mangler_options(options);
 | |
|     var tw = new TreeWalker(function(node){
 | |
|         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) {
 | |
|             base54.consider("if");
 | |
|             if (node.alternative) base54.consider("else");
 | |
|         }
 | |
|         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);
 | |
|     });
 | |
|     this.walk(tw);
 | |
|     base54.sort();
 | |
| });
 | |
| 
 | |
| var base54 = (function() {
 | |
|     var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
 | |
|     var chars, frequency;
 | |
|     function reset() {
 | |
|         frequency = Object.create(null);
 | |
|         chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });
 | |
|         chars.forEach(function(ch){ frequency[ch] = 0 });
 | |
|     }
 | |
|     base54.consider = function(str){
 | |
|         for (var i = str.length; --i >= 0;) {
 | |
|             var code = str.charCodeAt(i);
 | |
|             if (code in frequency) ++frequency[code];
 | |
|         }
 | |
|     };
 | |
|     base54.sort = function() {
 | |
|         chars = mergeSort(chars, function(a, b){
 | |
|             if (is_digit(a) && !is_digit(b)) return 1;
 | |
|             if (is_digit(b) && !is_digit(a)) return -1;
 | |
|             return frequency[b] - frequency[a];
 | |
|         });
 | |
|     };
 | |
|     base54.reset = reset;
 | |
|     reset();
 | |
|     base54.get = function(){ return chars };
 | |
|     base54.freq = function(){ return frequency };
 | |
|     function base54(num) {
 | |
|         var ret = "", base = 54;
 | |
|         do {
 | |
|             ret += String.fromCharCode(chars[num % base]);
 | |
|             num = Math.floor(num / base);
 | |
|             base = 64;
 | |
|         } while (num > 0);
 | |
|         return ret;
 | |
|     };
 | |
|     return base54;
 | |
| })();
 | |
| 
 | |
| AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
 | |
|     options = defaults(options, {
 | |
|         undeclared       : false, // this makes a lot of noise
 | |
|         unreferenced     : true,
 | |
|         assign_to_global : true,
 | |
|         func_arguments   : true,
 | |
|         nested_defuns    : true,
 | |
|         eval             : true
 | |
|     });
 | |
|     var tw = new TreeWalker(function(node){
 | |
|         if (options.undeclared
 | |
|             && node instanceof AST_SymbolRef
 | |
|             && node.undeclared())
 | |
|         {
 | |
|             // XXX: this also warns about JS standard names,
 | |
|             // i.e. Object, Array, parseInt etc.  Should add a list of
 | |
|             // exceptions.
 | |
|             AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {
 | |
|                 name: node.name,
 | |
|                 file: node.start.file,
 | |
|                 line: node.start.line,
 | |
|                 col: node.start.col
 | |
|             });
 | |
|         }
 | |
|         if (options.assign_to_global)
 | |
|         {
 | |
|             var sym = null;
 | |
|             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;
 | |
|             if (sym
 | |
|                 && (sym.undeclared()
 | |
|                     || (sym.global() && sym.scope !== sym.definition().scope))) {
 | |
|                 AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {
 | |
|                     msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
 | |
|                     name: sym.name,
 | |
|                     file: sym.start.file,
 | |
|                     line: sym.start.line,
 | |
|                     col: sym.start.col
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|         if (options.eval
 | |
|             && node instanceof AST_SymbolRef
 | |
|             && node.undeclared()
 | |
|             && node.name == "eval") {
 | |
|             AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);
 | |
|         }
 | |
|         if (options.unreferenced
 | |
|             && (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)
 | |
|             && node.unreferenced()) {
 | |
|             AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
 | |
|                 type: node instanceof AST_Label ? "Label" : "Symbol",
 | |
|                 name: node.name,
 | |
|                 file: node.start.file,
 | |
|                 line: node.start.line,
 | |
|                 col: node.start.col
 | |
|             });
 | |
|         }
 | |
|         if (options.func_arguments
 | |
|             && node instanceof AST_Lambda
 | |
|             && node.uses_arguments) {
 | |
|             AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {
 | |
|                 name: node.name ? node.name.name : "anonymous",
 | |
|                 file: node.start.file,
 | |
|                 line: node.start.line,
 | |
|                 col: node.start.col
 | |
|             });
 | |
|         }
 | |
|         if (options.nested_defuns
 | |
|             && node instanceof AST_Defun
 | |
|             && !(tw.parent() instanceof AST_Scope)) {
 | |
|             AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {
 | |
|                 name: node.name.name,
 | |
|                 type: tw.parent().TYPE,
 | |
|                 file: node.start.file,
 | |
|                 line: node.start.line,
 | |
|                 col: node.start.col
 | |
|             });
 | |
|         }
 | |
|     });
 | |
|     this.walk(tw);
 | |
| });
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| function OutputStream(options) {
 | |
| 
 | |
|     options = defaults(options, {
 | |
|         indent_start  : 0,
 | |
|         indent_level  : 4,
 | |
|         quote_keys    : false,
 | |
|         space_colon   : true,
 | |
|         ascii_only    : false,
 | |
|         inline_script : false,
 | |
|         width         : 80,
 | |
|         max_line_len  : 32000,
 | |
|         beautify      : false,
 | |
|         source_map    : null,
 | |
|         bracketize    : false,
 | |
|         semicolons    : true,
 | |
|         comments      : false,
 | |
|         preserve_line : false,
 | |
|         screw_ie8     : false,
 | |
|     }, true);
 | |
| 
 | |
|     var indentation = 0;
 | |
|     var current_col = 0;
 | |
|     var current_line = 1;
 | |
|     var current_pos = 0;
 | |
|     var OUTPUT = "";
 | |
| 
 | |
|     function to_ascii(str, identifier) {
 | |
|         return str.replace(/[\u0080-\uffff]/g, function(ch) {
 | |
|             var code = ch.charCodeAt(0).toString(16);
 | |
|             if (code.length <= 2 && !identifier) {
 | |
|                 while (code.length < 2) code = "0" + code;
 | |
|                 return "\\x" + code;
 | |
|             } else {
 | |
|                 while (code.length < 4) code = "0" + code;
 | |
|                 return "\\u" + code;
 | |
|             }
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function make_string(str) {
 | |
|         var dq = 0, sq = 0;
 | |
|         str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){
 | |
|             switch (s) {
 | |
|               case "\\": return "\\\\";
 | |
|               case "\b": return "\\b";
 | |
|               case "\f": return "\\f";
 | |
|               case "\n": return "\\n";
 | |
|               case "\r": return "\\r";
 | |
|               case "\u2028": return "\\u2028";
 | |
|               case "\u2029": return "\\u2029";
 | |
|               case '"': ++dq; return '"';
 | |
|               case "'": ++sq; return "'";
 | |
|               case "\0": return "\\x00";
 | |
|             }
 | |
|             return s;
 | |
|         });
 | |
|         if (options.ascii_only) str = to_ascii(str);
 | |
|         if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'";
 | |
|         else return '"' + str.replace(/\x22/g, '\\"') + '"';
 | |
|     };
 | |
| 
 | |
|     function encode_string(str) {
 | |
|         var ret = make_string(str);
 | |
|         if (options.inline_script)
 | |
|             ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     function make_name(name) {
 | |
|         name = name.toString();
 | |
|         if (options.ascii_only)
 | |
|             name = to_ascii(name, true);
 | |
|         return name;
 | |
|     };
 | |
| 
 | |
|     function make_indent(back) {
 | |
|         return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
 | |
|     };
 | |
| 
 | |
|     /* -----[ beautification/minification ]----- */
 | |
| 
 | |
|     var might_need_space = false;
 | |
|     var might_need_semicolon = false;
 | |
|     var last = null;
 | |
| 
 | |
|     function last_char() {
 | |
|         return last.charAt(last.length - 1);
 | |
|     };
 | |
| 
 | |
|     function maybe_newline() {
 | |
|         if (options.max_line_len && current_col > options.max_line_len)
 | |
|             print("\n");
 | |
|     };
 | |
| 
 | |
|     var requireSemicolonChars = makePredicate("( [ + * / - , .");
 | |
| 
 | |
|     function print(str) {
 | |
|         str = String(str);
 | |
|         var ch = str.charAt(0);
 | |
|         if (might_need_semicolon) {
 | |
|             if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
 | |
|                 if (options.semicolons || requireSemicolonChars(ch)) {
 | |
|                     OUTPUT += ";";
 | |
|                     current_col++;
 | |
|                     current_pos++;
 | |
|                 } else {
 | |
|                     OUTPUT += "\n";
 | |
|                     current_pos++;
 | |
|                     current_line++;
 | |
|                     current_col = 0;
 | |
|                 }
 | |
|                 if (!options.beautify)
 | |
|                     might_need_space = false;
 | |
|             }
 | |
|             might_need_semicolon = false;
 | |
|             maybe_newline();
 | |
|         }
 | |
| 
 | |
|         if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
 | |
|             var target_line = stack[stack.length - 1].start.line;
 | |
|             while (current_line < target_line) {
 | |
|                 OUTPUT += "\n";
 | |
|                 current_pos++;
 | |
|                 current_line++;
 | |
|                 current_col = 0;
 | |
|                 might_need_space = false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (might_need_space) {
 | |
|             var prev = last_char();
 | |
|             if ((is_identifier_char(prev)
 | |
|                  && (is_identifier_char(ch) || ch == "\\"))
 | |
|                 || (/^[\+\-\/]$/.test(ch) && ch == prev))
 | |
|             {
 | |
|                 OUTPUT += " ";
 | |
|                 current_col++;
 | |
|                 current_pos++;
 | |
|             }
 | |
|             might_need_space = false;
 | |
|         }
 | |
|         var a = str.split(/\r?\n/), n = a.length - 1;
 | |
|         current_line += n;
 | |
|         if (n == 0) {
 | |
|             current_col += a[n].length;
 | |
|         } else {
 | |
|             current_col = a[n].length;
 | |
|         }
 | |
|         current_pos += str.length;
 | |
|         last = str;
 | |
|         OUTPUT += str;
 | |
|     };
 | |
| 
 | |
|     var space = options.beautify ? function() {
 | |
|         print(" ");
 | |
|     } : function() {
 | |
|         might_need_space = true;
 | |
|     };
 | |
| 
 | |
|     var indent = options.beautify ? function(half) {
 | |
|         if (options.beautify) {
 | |
|             print(make_indent(half ? 0.5 : 0));
 | |
|         }
 | |
|     } : noop;
 | |
| 
 | |
|     var with_indent = options.beautify ? function(col, cont) {
 | |
|         if (col === true) col = next_indent();
 | |
|         var save_indentation = indentation;
 | |
|         indentation = col;
 | |
|         var ret = cont();
 | |
|         indentation = save_indentation;
 | |
|         return ret;
 | |
|     } : function(col, cont) { return cont() };
 | |
| 
 | |
|     var newline = options.beautify ? function() {
 | |
|         print("\n");
 | |
|     } : noop;
 | |
| 
 | |
|     var semicolon = options.beautify ? function() {
 | |
|         print(";");
 | |
|     } : function() {
 | |
|         might_need_semicolon = true;
 | |
|     };
 | |
| 
 | |
|     function force_semicolon() {
 | |
|         might_need_semicolon = false;
 | |
|         print(";");
 | |
|     };
 | |
| 
 | |
|     function next_indent() {
 | |
|         return indentation + options.indent_level;
 | |
|     };
 | |
| 
 | |
|     function with_block(cont) {
 | |
|         var ret;
 | |
|         print("{");
 | |
|         newline();
 | |
|         with_indent(next_indent(), function(){
 | |
|             ret = cont();
 | |
|         });
 | |
|         indent();
 | |
|         print("}");
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     function with_parens(cont) {
 | |
|         print("(");
 | |
|         //XXX: still nice to have that for argument lists
 | |
|         //var ret = with_indent(current_col, cont);
 | |
|         var ret = cont();
 | |
|         print(")");
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     function with_square(cont) {
 | |
|         print("[");
 | |
|         //var ret = with_indent(current_col, cont);
 | |
|         var ret = cont();
 | |
|         print("]");
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     function comma() {
 | |
|         print(",");
 | |
|         space();
 | |
|     };
 | |
| 
 | |
|     function colon() {
 | |
|         print(":");
 | |
|         if (options.space_colon) space();
 | |
|     };
 | |
| 
 | |
|     var add_mapping = options.source_map ? function(token, name) {
 | |
|         try {
 | |
|             if (token) options.source_map.add(
 | |
|                 token.file || "?",
 | |
|                 current_line, current_col,
 | |
|                 token.line, token.col,
 | |
|                 (!name && token.type == "name") ? token.value : name
 | |
|             );
 | |
|         } catch(ex) {
 | |
|             AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
 | |
|                 file: token.file,
 | |
|                 line: token.line,
 | |
|                 col: token.col,
 | |
|                 cline: current_line,
 | |
|                 ccol: current_col,
 | |
|                 name: name || ""
 | |
|             })
 | |
|         }
 | |
|     } : noop;
 | |
| 
 | |
|     function get() {
 | |
|         return OUTPUT;
 | |
|     };
 | |
| 
 | |
|     var stack = [];
 | |
|     return {
 | |
|         get             : get,
 | |
|         toString        : get,
 | |
|         indent          : indent,
 | |
|         indentation     : function() { return indentation },
 | |
|         current_width   : function() { return current_col - indentation },
 | |
|         should_break    : function() { return options.width && this.current_width() >= options.width },
 | |
|         newline         : newline,
 | |
|         print           : print,
 | |
|         space           : space,
 | |
|         comma           : comma,
 | |
|         colon           : colon,
 | |
|         last            : function() { return last },
 | |
|         semicolon       : semicolon,
 | |
|         force_semicolon : force_semicolon,
 | |
|         to_ascii        : to_ascii,
 | |
|         print_name      : function(name) { print(make_name(name)) },
 | |
|         print_string    : function(str) { print(encode_string(str)) },
 | |
|         next_indent     : next_indent,
 | |
|         with_indent     : with_indent,
 | |
|         with_block      : with_block,
 | |
|         with_parens     : with_parens,
 | |
|         with_square     : with_square,
 | |
|         add_mapping     : add_mapping,
 | |
|         option          : function(opt) { return options[opt] },
 | |
|         line            : function() { return current_line },
 | |
|         col             : function() { return current_col },
 | |
|         pos             : function() { return current_pos },
 | |
|         push_node       : function(node) { stack.push(node) },
 | |
|         pop_node        : function() { return stack.pop() },
 | |
|         stack           : function() { return stack },
 | |
|         parent          : function(n) {
 | |
|             return stack[stack.length - 2 - (n || 0)];
 | |
|         }
 | |
|     };
 | |
| 
 | |
| };
 | |
| 
 | |
| /* -----[ code generators ]----- */
 | |
| 
 | |
| (function(){
 | |
| 
 | |
|     /* -----[ utils ]----- */
 | |
| 
 | |
|     function DEFPRINT(nodetype, generator) {
 | |
|         nodetype.DEFMETHOD("_codegen", generator);
 | |
|     };
 | |
| 
 | |
|     AST_Node.DEFMETHOD("print", function(stream, force_parens){
 | |
|         var self = this, generator = self._codegen;
 | |
|         function doit() {
 | |
|             self.add_comments(stream);
 | |
|             self.add_source_map(stream);
 | |
|             generator(self, stream);
 | |
|         }
 | |
|         stream.push_node(self);
 | |
|         if (force_parens || self.needs_parens(stream)) {
 | |
|             stream.with_parens(doit);
 | |
|         } else {
 | |
|             doit();
 | |
|         }
 | |
|         stream.pop_node();
 | |
|     });
 | |
| 
 | |
|     AST_Node.DEFMETHOD("print_to_string", function(options){
 | |
|         var s = OutputStream(options);
 | |
|         this.print(s);
 | |
|         return s.get();
 | |
|     });
 | |
| 
 | |
|     /* -----[ comments ]----- */
 | |
| 
 | |
|     AST_Node.DEFMETHOD("add_comments", function(output){
 | |
|         var c = output.option("comments"), self = this;
 | |
|         if (c) {
 | |
|             var start = self.start;
 | |
|             if (start && !start._comments_dumped) {
 | |
|                 start._comments_dumped = true;
 | |
|                 var comments = start.comments_before;
 | |
| 
 | |
|                 // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
 | |
|                 //      if this node is `return` or `throw`, we cannot allow comments before
 | |
|                 //      the returned or thrown value.
 | |
|                 if (self instanceof AST_Exit &&
 | |
|                     self.value && self.value.start.comments_before.length > 0) {
 | |
|                     comments = (comments || []).concat(self.value.start.comments_before);
 | |
|                     self.value.start.comments_before = [];
 | |
|                 }
 | |
| 
 | |
|                 if (c.test) {
 | |
|                     comments = comments.filter(function(comment){
 | |
|                         return c.test(comment.value);
 | |
|                     });
 | |
|                 } else if (typeof c == "function") {
 | |
|                     comments = comments.filter(function(comment){
 | |
|                         return c(self, comment);
 | |
|                     });
 | |
|                 }
 | |
|                 comments.forEach(function(c){
 | |
|                     if (c.type == "comment1") {
 | |
|                         output.print("//" + c.value + "\n");
 | |
|                         output.indent();
 | |
|                     }
 | |
|                     else if (c.type == "comment2") {
 | |
|                         output.print("/*" + c.value + "*/");
 | |
|                         if (start.nlb) {
 | |
|                             output.print("\n");
 | |
|                             output.indent();
 | |
|                         } else {
 | |
|                             output.space();
 | |
|                         }
 | |
|                     }
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     /* -----[ PARENTHESES ]----- */
 | |
| 
 | |
|     function PARENS(nodetype, func) {
 | |
|         nodetype.DEFMETHOD("needs_parens", func);
 | |
|     };
 | |
| 
 | |
|     PARENS(AST_Node, function(){
 | |
|         return false;
 | |
|     });
 | |
| 
 | |
|     // a function expression needs parens around it when it's provably
 | |
|     // the first token to appear in a statement.
 | |
|     PARENS(AST_Function, function(output){
 | |
|         return first_in_statement(output);
 | |
|     });
 | |
| 
 | |
|     // same goes for an object literal, because otherwise it would be
 | |
|     // interpreted as a block of code.
 | |
|     PARENS(AST_Object, function(output){
 | |
|         return first_in_statement(output);
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_Unary, function(output){
 | |
|         var p = output.parent();
 | |
|         return p instanceof AST_PropAccess && p.expression === this;
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_Seq, function(output){
 | |
|         var p = output.parent();
 | |
|         return p instanceof AST_Call             // (foo, bar)() or foo(1, (2, 3), 4)
 | |
|             || p instanceof AST_Unary            // !(foo, bar, baz)
 | |
|             || p instanceof AST_Binary           // 1 + (2, 3) + 4 ==> 8
 | |
|             || p instanceof AST_VarDef           // var a = (1, 2), b = a + a; ==> b == 4
 | |
|             || p instanceof AST_Dot              // (1, {foo:2}).foo ==> 2
 | |
|             || p instanceof AST_Array            // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
 | |
|             || p instanceof AST_ObjectProperty   // { foo: (1, 2) }.foo ==> 2
 | |
|             || p instanceof AST_Conditional      /* (false, true) ? (a = 10, b = 20) : (c = 30)
 | |
|                                                   * ==> 20 (side effect, set a := 10 and b := 20) */
 | |
|         ;
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_Binary, function(output){
 | |
|         var p = output.parent();
 | |
|         // (foo && bar)()
 | |
|         if (p instanceof AST_Call && p.expression === this)
 | |
|             return true;
 | |
|         // typeof (foo && bar)
 | |
|         if (p instanceof AST_Unary)
 | |
|             return true;
 | |
|         // (foo && bar)["prop"], (foo && bar).prop
 | |
|         if (p instanceof AST_PropAccess && p.expression === this)
 | |
|             return true;
 | |
|         // this deals with precedence: 3 * (2 + 1)
 | |
|         if (p instanceof AST_Binary) {
 | |
|             var po = p.operator, pp = PRECEDENCE[po];
 | |
|             var so = this.operator, sp = PRECEDENCE[so];
 | |
|             if (pp > sp
 | |
|                 || (pp == sp
 | |
|                     && this === p.right
 | |
|                     && !(so == po &&
 | |
|                          (so == "*" ||
 | |
|                           so == "&&" ||
 | |
|                           so == "||")))) {
 | |
|                 return true;
 | |
|             }
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_PropAccess, function(output){
 | |
|         var p = output.parent();
 | |
|         if (p instanceof AST_New && p.expression === this) {
 | |
|             // i.e. new (foo.bar().baz)
 | |
|             //
 | |
|             // if there's one call into this subtree, then we need
 | |
|             // parens around it too, otherwise the call will be
 | |
|             // interpreted as passing the arguments to the upper New
 | |
|             // expression.
 | |
|             try {
 | |
|                 this.walk(new TreeWalker(function(node){
 | |
|                     if (node instanceof AST_Call) throw p;
 | |
|                 }));
 | |
|             } catch(ex) {
 | |
|                 if (ex !== p) throw ex;
 | |
|                 return true;
 | |
|             }
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_Call, function(output){
 | |
|         var p = output.parent();
 | |
|         return p instanceof AST_New && p.expression === this;
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_New, function(output){
 | |
|         var p = output.parent();
 | |
|         if (no_constructor_parens(this, output)
 | |
|             && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
 | |
|                 || p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
 | |
|             return true;
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_Number, function(output){
 | |
|         var p = output.parent();
 | |
|         if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this)
 | |
|             return true;
 | |
|     });
 | |
| 
 | |
|     PARENS(AST_NaN, function(output){
 | |
|         var p = output.parent();
 | |
|         if (p instanceof AST_PropAccess && p.expression === this)
 | |
|             return true;
 | |
|     });
 | |
| 
 | |
|     function assign_and_conditional_paren_rules(output) {
 | |
|         var p = output.parent();
 | |
|         // !(a = false) → true
 | |
|         if (p instanceof AST_Unary)
 | |
|             return true;
 | |
|         // 1 + (a = 2) + 3 → 6, side effect setting a = 2
 | |
|         if (p instanceof AST_Binary && !(p instanceof AST_Assign))
 | |
|             return true;
 | |
|         // (a = func)() —or— new (a = Object)()
 | |
|         if (p instanceof AST_Call && p.expression === this)
 | |
|             return true;
 | |
|         // (a = foo) ? bar : baz
 | |
|         if (p instanceof AST_Conditional && p.condition === this)
 | |
|             return true;
 | |
|         // (a = foo)["prop"] —or— (a = foo).prop
 | |
|         if (p instanceof AST_PropAccess && p.expression === this)
 | |
|             return true;
 | |
|     };
 | |
| 
 | |
|     PARENS(AST_Assign, assign_and_conditional_paren_rules);
 | |
|     PARENS(AST_Conditional, assign_and_conditional_paren_rules);
 | |
| 
 | |
|     /* -----[ PRINTERS ]----- */
 | |
| 
 | |
|     DEFPRINT(AST_Directive, function(self, output){
 | |
|         output.print_string(self.value);
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Debugger, function(self, output){
 | |
|         output.print("debugger");
 | |
|         output.semicolon();
 | |
|     });
 | |
| 
 | |
|     /* -----[ statements ]----- */
 | |
| 
 | |
|     function display_body(body, is_toplevel, output) {
 | |
|         var last = body.length - 1;
 | |
|         body.forEach(function(stmt, i){
 | |
|             if (!(stmt instanceof AST_EmptyStatement)) {
 | |
|                 output.indent();
 | |
|                 stmt.print(output);
 | |
|                 if (!(i == last && is_toplevel)) {
 | |
|                     output.newline();
 | |
|                     if (is_toplevel) output.newline();
 | |
|                 }
 | |
|             }
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){
 | |
|         force_statement(this.body, output);
 | |
|     });
 | |
| 
 | |
|     DEFPRINT(AST_Statement, function(self, output){
 | |
|         self.body.print(output);
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Toplevel, function(self, output){
 | |
|         display_body(self.body, true, output);
 | |
|         output.print("");
 | |
|     });
 | |
|     DEFPRINT(AST_LabeledStatement, function(self, output){
 | |
|         self.label.print(output);
 | |
|         output.colon();
 | |
|         self.body.print(output);
 | |
|     });
 | |
|     DEFPRINT(AST_SimpleStatement, function(self, output){
 | |
|         self.body.print(output);
 | |
|         output.semicolon();
 | |
|     });
 | |
|     function print_bracketed(body, output) {
 | |
|         if (body.length > 0) output.with_block(function(){
 | |
|             display_body(body, false, output);
 | |
|         });
 | |
|         else output.print("{}");
 | |
|     };
 | |
|     DEFPRINT(AST_BlockStatement, function(self, output){
 | |
|         print_bracketed(self.body, output);
 | |
|     });
 | |
|     DEFPRINT(AST_EmptyStatement, function(self, output){
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Do, function(self, output){
 | |
|         output.print("do");
 | |
|         output.space();
 | |
|         self._do_print_body(output);
 | |
|         output.space();
 | |
|         output.print("while");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.condition.print(output);
 | |
|         });
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_While, function(self, output){
 | |
|         output.print("while");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.condition.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
|     DEFPRINT(AST_For, function(self, output){
 | |
|         output.print("for");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             if (self.init) {
 | |
|                 if (self.init instanceof AST_Definitions) {
 | |
|                     self.init.print(output);
 | |
|                 } else {
 | |
|                     parenthesize_for_noin(self.init, output, true);
 | |
|                 }
 | |
|                 output.print(";");
 | |
|                 output.space();
 | |
|             } else {
 | |
|                 output.print(";");
 | |
|             }
 | |
|             if (self.condition) {
 | |
|                 self.condition.print(output);
 | |
|                 output.print(";");
 | |
|                 output.space();
 | |
|             } else {
 | |
|                 output.print(";");
 | |
|             }
 | |
|             if (self.step) {
 | |
|                 self.step.print(output);
 | |
|             }
 | |
|         });
 | |
|         output.space();
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
|     DEFPRINT(AST_ForIn, function(self, output){
 | |
|         output.print("for");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.init.print(output);
 | |
|             output.space();
 | |
|             output.print("in");
 | |
|             output.space();
 | |
|             self.object.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
|     DEFPRINT(AST_With, function(self, output){
 | |
|         output.print("with");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.expression.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
| 
 | |
|     /* -----[ functions ]----- */
 | |
|     AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
 | |
|         var self = this;
 | |
|         if (!nokeyword) {
 | |
|             output.print("function");
 | |
|         }
 | |
|         if (self.name) {
 | |
|             output.space();
 | |
|             self.name.print(output);
 | |
|         }
 | |
|         output.with_parens(function(){
 | |
|             self.argnames.forEach(function(arg, i){
 | |
|                 if (i) output.comma();
 | |
|                 arg.print(output);
 | |
|             });
 | |
|         });
 | |
|         output.space();
 | |
|         print_bracketed(self.body, output);
 | |
|     });
 | |
|     DEFPRINT(AST_Lambda, function(self, output){
 | |
|         self._do_print(output);
 | |
|     });
 | |
| 
 | |
|     /* -----[ exits ]----- */
 | |
|     AST_Exit.DEFMETHOD("_do_print", function(output, kind){
 | |
|         output.print(kind);
 | |
|         if (this.value) {
 | |
|             output.space();
 | |
|             this.value.print(output);
 | |
|         }
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Return, function(self, output){
 | |
|         self._do_print(output, "return");
 | |
|     });
 | |
|     DEFPRINT(AST_Throw, function(self, output){
 | |
|         self._do_print(output, "throw");
 | |
|     });
 | |
| 
 | |
|     /* -----[ loop control ]----- */
 | |
|     AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
 | |
|         output.print(kind);
 | |
|         if (this.label) {
 | |
|             output.space();
 | |
|             this.label.print(output);
 | |
|         }
 | |
|         output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Break, function(self, output){
 | |
|         self._do_print(output, "break");
 | |
|     });
 | |
|     DEFPRINT(AST_Continue, function(self, output){
 | |
|         self._do_print(output, "continue");
 | |
|     });
 | |
| 
 | |
|     /* -----[ if ]----- */
 | |
|     function make_then(self, output) {
 | |
|         if (output.option("bracketize")) {
 | |
|             make_block(self.body, output);
 | |
|             return;
 | |
|         }
 | |
|         // The squeezer replaces "block"-s that contain only a single
 | |
|         // statement with the statement itself; technically, the AST
 | |
|         // is correct, but this can create problems when we output an
 | |
|         // IF having an ELSE clause where the THEN clause ends in an
 | |
|         // IF *without* an ELSE block (then the outer ELSE would refer
 | |
|         // to the inner IF).  This function checks for this case and
 | |
|         // adds the block brackets if needed.
 | |
|         if (!self.body)
 | |
|             return output.force_semicolon();
 | |
|         if (self.body instanceof AST_Do
 | |
|             && !output.option("screw_ie8")) {
 | |
|             // https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
 | |
|             // croaks with "syntax error" on code like this: if (foo)
 | |
|             // do ... while(cond); else ...  we need block brackets
 | |
|             // around do/while
 | |
|             make_block(self.body, output);
 | |
|             return;
 | |
|         }
 | |
|         var b = self.body;
 | |
|         while (true) {
 | |
|             if (b instanceof AST_If) {
 | |
|                 if (!b.alternative) {
 | |
|                     make_block(self.body, output);
 | |
|                     return;
 | |
|                 }
 | |
|                 b = b.alternative;
 | |
|             }
 | |
|             else if (b instanceof AST_StatementWithBody) {
 | |
|                 b = b.body;
 | |
|             }
 | |
|             else break;
 | |
|         }
 | |
|         force_statement(self.body, output);
 | |
|     };
 | |
|     DEFPRINT(AST_If, function(self, output){
 | |
|         output.print("if");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.condition.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         if (self.alternative) {
 | |
|             make_then(self, output);
 | |
|             output.space();
 | |
|             output.print("else");
 | |
|             output.space();
 | |
|             force_statement(self.alternative, output);
 | |
|         } else {
 | |
|             self._do_print_body(output);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     /* -----[ switch ]----- */
 | |
|     DEFPRINT(AST_Switch, function(self, output){
 | |
|         output.print("switch");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.expression.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         if (self.body.length > 0) output.with_block(function(){
 | |
|             self.body.forEach(function(stmt, i){
 | |
|                 if (i) output.newline();
 | |
|                 output.indent(true);
 | |
|                 stmt.print(output);
 | |
|             });
 | |
|         });
 | |
|         else output.print("{}");
 | |
|     });
 | |
|     AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){
 | |
|         if (this.body.length > 0) {
 | |
|             output.newline();
 | |
|             this.body.forEach(function(stmt){
 | |
|                 output.indent();
 | |
|                 stmt.print(output);
 | |
|                 output.newline();
 | |
|             });
 | |
|         }
 | |
|     });
 | |
|     DEFPRINT(AST_Default, function(self, output){
 | |
|         output.print("default:");
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
|     DEFPRINT(AST_Case, function(self, output){
 | |
|         output.print("case");
 | |
|         output.space();
 | |
|         self.expression.print(output);
 | |
|         output.print(":");
 | |
|         self._do_print_body(output);
 | |
|     });
 | |
| 
 | |
|     /* -----[ exceptions ]----- */
 | |
|     DEFPRINT(AST_Try, function(self, output){
 | |
|         output.print("try");
 | |
|         output.space();
 | |
|         print_bracketed(self.body, output);
 | |
|         if (self.bcatch) {
 | |
|             output.space();
 | |
|             self.bcatch.print(output);
 | |
|         }
 | |
|         if (self.bfinally) {
 | |
|             output.space();
 | |
|             self.bfinally.print(output);
 | |
|         }
 | |
|     });
 | |
|     DEFPRINT(AST_Catch, function(self, output){
 | |
|         output.print("catch");
 | |
|         output.space();
 | |
|         output.with_parens(function(){
 | |
|             self.argname.print(output);
 | |
|         });
 | |
|         output.space();
 | |
|         print_bracketed(self.body, output);
 | |
|     });
 | |
|     DEFPRINT(AST_Finally, function(self, output){
 | |
|         output.print("finally");
 | |
|         output.space();
 | |
|         print_bracketed(self.body, output);
 | |
|     });
 | |
| 
 | |
|     /* -----[ var/const ]----- */
 | |
|     AST_Definitions.DEFMETHOD("_do_print", function(output, kind){
 | |
|         output.print(kind);
 | |
|         output.space();
 | |
|         this.definitions.forEach(function(def, i){
 | |
|             if (i) output.comma();
 | |
|             def.print(output);
 | |
|         });
 | |
|         var p = output.parent();
 | |
|         var in_for = p instanceof AST_For || p instanceof AST_ForIn;
 | |
|         var avoid_semicolon = in_for && p.init === this;
 | |
|         if (!avoid_semicolon)
 | |
|             output.semicolon();
 | |
|     });
 | |
|     DEFPRINT(AST_Var, function(self, output){
 | |
|         self._do_print(output, "var");
 | |
|     });
 | |
|     DEFPRINT(AST_Const, function(self, output){
 | |
|         self._do_print(output, "const");
 | |
|     });
 | |
| 
 | |
|     function parenthesize_for_noin(node, output, noin) {
 | |
|         if (!noin) node.print(output);
 | |
|         else try {
 | |
|             // need to take some precautions here:
 | |
|             //    https://github.com/mishoo/UglifyJS2/issues/60
 | |
|             node.walk(new TreeWalker(function(node){
 | |
|                 if (node instanceof AST_Binary && node.operator == "in")
 | |
|                     throw output;
 | |
|             }));
 | |
|             node.print(output);
 | |
|         } catch(ex) {
 | |
|             if (ex !== output) throw ex;
 | |
|             node.print(output, true);
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     DEFPRINT(AST_VarDef, function(self, output){
 | |
|         self.name.print(output);
 | |
|         if (self.value) {
 | |
|             output.space();
 | |
|             output.print("=");
 | |
|             output.space();
 | |
|             var p = output.parent(1);
 | |
|             var noin = p instanceof AST_For || p instanceof AST_ForIn;
 | |
|             parenthesize_for_noin(self.value, output, noin);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     /* -----[ other expressions ]----- */
 | |
|     DEFPRINT(AST_Call, function(self, output){
 | |
|         self.expression.print(output);
 | |
|         if (self instanceof AST_New && no_constructor_parens(self, output))
 | |
|             return;
 | |
|         output.with_parens(function(){
 | |
|             self.args.forEach(function(expr, i){
 | |
|                 if (i) output.comma();
 | |
|                 expr.print(output);
 | |
|             });
 | |
|         });
 | |
|     });
 | |
|     DEFPRINT(AST_New, function(self, output){
 | |
|         output.print("new");
 | |
|         output.space();
 | |
|         AST_Call.prototype._codegen(self, output);
 | |
|     });
 | |
| 
 | |
|     AST_Seq.DEFMETHOD("_do_print", function(output){
 | |
|         this.car.print(output);
 | |
|         if (this.cdr) {
 | |
|             output.comma();
 | |
|             if (output.should_break()) {
 | |
|                 output.newline();
 | |
|                 output.indent();
 | |
|             }
 | |
|             this.cdr.print(output);
 | |
|         }
 | |
|     });
 | |
|     DEFPRINT(AST_Seq, function(self, output){
 | |
|         self._do_print(output);
 | |
|         // var p = output.parent();
 | |
|         // if (p instanceof AST_Statement) {
 | |
|         //     output.with_indent(output.next_indent(), function(){
 | |
|         //         self._do_print(output);
 | |
|         //     });
 | |
|         // } else {
 | |
|         //     self._do_print(output);
 | |
|         // }
 | |
|     });
 | |
|     DEFPRINT(AST_Dot, function(self, output){
 | |
|         var expr = self.expression;
 | |
|         expr.print(output);
 | |
|         if (expr instanceof AST_Number && expr.getValue() >= 0) {
 | |
|             if (!/[xa-f.]/i.test(output.last())) {
 | |
|                 output.print(".");
 | |
|             }
 | |
|         }
 | |
|         output.print(".");
 | |
|         // the name after dot would be mapped about here.
 | |
|         output.add_mapping(self.end);
 | |
|         output.print_name(self.property);
 | |
|     });
 | |
|     DEFPRINT(AST_Sub, function(self, output){
 | |
|         self.expression.print(output);
 | |
|         output.print("[");
 | |
|         self.property.print(output);
 | |
|         output.print("]");
 | |
|     });
 | |
|     DEFPRINT(AST_UnaryPrefix, function(self, output){
 | |
|         var op = self.operator;
 | |
|         output.print(op);
 | |
|         if (/^[a-z]/i.test(op))
 | |
|             output.space();
 | |
|         self.expression.print(output);
 | |
|     });
 | |
|     DEFPRINT(AST_UnaryPostfix, function(self, output){
 | |
|         self.expression.print(output);
 | |
|         output.print(self.operator);
 | |
|     });
 | |
|     DEFPRINT(AST_Binary, function(self, output){
 | |
|         self.left.print(output);
 | |
|         output.space();
 | |
|         output.print(self.operator);
 | |
|         output.space();
 | |
|         self.right.print(output);
 | |
|     });
 | |
|     DEFPRINT(AST_Conditional, function(self, output){
 | |
|         self.condition.print(output);
 | |
|         output.space();
 | |
|         output.print("?");
 | |
|         output.space();
 | |
|         self.consequent.print(output);
 | |
|         output.space();
 | |
|         output.colon();
 | |
|         self.alternative.print(output);
 | |
|     });
 | |
| 
 | |
|     /* -----[ literals ]----- */
 | |
|     DEFPRINT(AST_Array, function(self, output){
 | |
|         output.with_square(function(){
 | |
|             var a = self.elements, len = a.length;
 | |
|             if (len > 0) output.space();
 | |
|             a.forEach(function(exp, i){
 | |
|                 if (i) output.comma();
 | |
|                 exp.print(output);
 | |
|                 // If the final element is a hole, we need to make sure it
 | |
|                 // doesn't look like a trailing comma, by inserting an actual
 | |
|                 // trailing comma.
 | |
|                 if (i === len - 1 && exp instanceof AST_Hole)
 | |
|                   output.comma();
 | |
|             });
 | |
|             if (len > 0) output.space();
 | |
|         });
 | |
|     });
 | |
|     DEFPRINT(AST_Object, function(self, output){
 | |
|         if (self.properties.length > 0) output.with_block(function(){
 | |
|             self.properties.forEach(function(prop, i){
 | |
|                 if (i) {
 | |
|                     output.print(",");
 | |
|                     output.newline();
 | |
|                 }
 | |
|                 output.indent();
 | |
|                 prop.print(output);
 | |
|             });
 | |
|             output.newline();
 | |
|         });
 | |
|         else output.print("{}");
 | |
|     });
 | |
|     DEFPRINT(AST_ObjectKeyVal, function(self, output){
 | |
|         var key = self.key;
 | |
|         if (output.option("quote_keys")) {
 | |
|             output.print_string(key + "");
 | |
|         } else if ((typeof key == "number"
 | |
|                     || !output.option("beautify")
 | |
|                     && +key + "" == key)
 | |
|                    && parseFloat(key) >= 0) {
 | |
|             output.print(make_num(key));
 | |
|         } else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
 | |
|             output.print_name(key);
 | |
|         } else {
 | |
|             output.print_string(key);
 | |
|         }
 | |
|         output.colon();
 | |
|         self.value.print(output);
 | |
|     });
 | |
|     DEFPRINT(AST_ObjectSetter, function(self, output){
 | |
|         output.print("set");
 | |
|         self.value._do_print(output, true);
 | |
|     });
 | |
|     DEFPRINT(AST_ObjectGetter, function(self, output){
 | |
|         output.print("get");
 | |
|         self.value._do_print(output, true);
 | |
|     });
 | |
|     DEFPRINT(AST_Symbol, function(self, output){
 | |
|         var def = self.definition();
 | |
|         output.print_name(def ? def.mangled_name || def.name : self.name);
 | |
|     });
 | |
|     DEFPRINT(AST_Undefined, function(self, output){
 | |
|         output.print("void 0");
 | |
|     });
 | |
|     DEFPRINT(AST_Hole, noop);
 | |
|     DEFPRINT(AST_Infinity, function(self, output){
 | |
|         output.print("1/0");
 | |
|     });
 | |
|     DEFPRINT(AST_NaN, function(self, output){
 | |
|         output.print("0/0");
 | |
|     });
 | |
|     DEFPRINT(AST_This, function(self, output){
 | |
|         output.print("this");
 | |
|     });
 | |
|     DEFPRINT(AST_Constant, function(self, output){
 | |
|         output.print(self.getValue());
 | |
|     });
 | |
|     DEFPRINT(AST_String, function(self, output){
 | |
|         output.print_string(self.getValue());
 | |
|     });
 | |
|     DEFPRINT(AST_Number, function(self, output){
 | |
|         output.print(make_num(self.getValue()));
 | |
|     });
 | |
|     DEFPRINT(AST_RegExp, function(self, output){
 | |
|         var str = self.getValue().toString();
 | |
|         if (output.option("ascii_only"))
 | |
|             str = output.to_ascii(str);
 | |
|         output.print(str);
 | |
|         var p = output.parent();
 | |
|         if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
 | |
|             output.print(" ");
 | |
|     });
 | |
| 
 | |
|     function force_statement(stat, output) {
 | |
|         if (output.option("bracketize")) {
 | |
|             if (!stat || stat instanceof AST_EmptyStatement)
 | |
|                 output.print("{}");
 | |
|             else if (stat instanceof AST_BlockStatement)
 | |
|                 stat.print(output);
 | |
|             else output.with_block(function(){
 | |
|                 output.indent();
 | |
|                 stat.print(output);
 | |
|                 output.newline();
 | |
|             });
 | |
|         } else {
 | |
|             if (!stat || stat instanceof AST_EmptyStatement)
 | |
|                 output.force_semicolon();
 | |
|             else
 | |
|                 stat.print(output);
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     // return true if the node at the top of the stack (that means the
 | |
|     // innermost node in the current output) is lexically the first in
 | |
|     // a statement.
 | |
|     function first_in_statement(output) {
 | |
|         var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
 | |
|         while (i > 0) {
 | |
|             if (p instanceof AST_Statement && p.body === node)
 | |
|                 return true;
 | |
|             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 ))
 | |
|             {
 | |
|                 node = p;
 | |
|                 p = a[--i];
 | |
|             } else {
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     // self should be AST_New.  decide if we want to show parens or not.
 | |
|     function no_constructor_parens(self, output) {
 | |
|         return self.args.length == 0 && !output.option("beautify");
 | |
|     };
 | |
| 
 | |
|     function best_of(a) {
 | |
|         var best = a[0], len = best.length;
 | |
|         for (var i = 1; i < a.length; ++i) {
 | |
|             if (a[i].length < len) {
 | |
|                 best = a[i];
 | |
|                 len = best.length;
 | |
|             }
 | |
|         }
 | |
|         return best;
 | |
|     };
 | |
| 
 | |
|     function make_num(num) {
 | |
|         var str = num.toString(10), a = [ str.replace(/^0\./, ".").replace('e+', 'e') ], m;
 | |
|         if (Math.floor(num) === num) {
 | |
|             if (num >= 0) {
 | |
|                 a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
 | |
|                        "0" + num.toString(8)); // same.
 | |
|             } else {
 | |
|                 a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
 | |
|                        "-0" + (-num).toString(8)); // same.
 | |
|             }
 | |
|             if ((m = /^(.*?)(0+)$/.exec(num))) {
 | |
|                 a.push(m[1] + "e" + m[2].length);
 | |
|             }
 | |
|         } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
 | |
|             a.push(m[2] + "e-" + (m[1].length + m[2].length),
 | |
|                    str.substr(str.indexOf(".")));
 | |
|         }
 | |
|         return best_of(a);
 | |
|     };
 | |
| 
 | |
|     function make_block(stmt, output) {
 | |
|         if (stmt instanceof AST_BlockStatement) {
 | |
|             stmt.print(output);
 | |
|             return;
 | |
|         }
 | |
|         output.with_block(function(){
 | |
|             output.indent();
 | |
|             stmt.print(output);
 | |
|             output.newline();
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     /* -----[ source map generators ]----- */
 | |
| 
 | |
|     function DEFMAP(nodetype, generator) {
 | |
|         nodetype.DEFMETHOD("add_source_map", function(stream){
 | |
|             generator(this, stream);
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     // We could easily add info for ALL nodes, but it seems to me that
 | |
|     // would be quite wasteful, hence this noop in the base class.
 | |
|     DEFMAP(AST_Node, noop);
 | |
| 
 | |
|     function basic_sourcemap_gen(self, output) {
 | |
|         output.add_mapping(self.start);
 | |
|     };
 | |
| 
 | |
|     // XXX: I'm not exactly sure if we need it for all of these nodes,
 | |
|     // or if we should add even more.
 | |
| 
 | |
|     DEFMAP(AST_Directive, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Debugger, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Symbol, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Jump, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_StatementWithBody, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_LabeledStatement, noop); // since the label symbol will mark it
 | |
|     DEFMAP(AST_Lambda, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Switch, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_SwitchBranch, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_BlockStatement, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Toplevel, noop);
 | |
|     DEFMAP(AST_New, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Try, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Catch, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Finally, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Definitions, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_Constant, basic_sourcemap_gen);
 | |
|     DEFMAP(AST_ObjectProperty, function(self, output){
 | |
|         output.add_mapping(self.start, self.key);
 | |
|     });
 | |
| 
 | |
| })();
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| function Compressor(options, false_by_default) {
 | |
|     if (!(this instanceof Compressor))
 | |
|         return new Compressor(options, false_by_default);
 | |
|     TreeTransformer.call(this, this.before, this.after);
 | |
|     this.options = defaults(options, {
 | |
|         sequences     : !false_by_default,
 | |
|         properties    : !false_by_default,
 | |
|         dead_code     : !false_by_default,
 | |
|         drop_debugger : !false_by_default,
 | |
|         unsafe        : false,
 | |
|         unsafe_comps  : false,
 | |
|         conditionals  : !false_by_default,
 | |
|         comparisons   : !false_by_default,
 | |
|         evaluate      : !false_by_default,
 | |
|         booleans      : !false_by_default,
 | |
|         loops         : !false_by_default,
 | |
|         unused        : !false_by_default,
 | |
|         hoist_funs    : !false_by_default,
 | |
|         hoist_vars    : false,
 | |
|         if_return     : !false_by_default,
 | |
|         join_vars     : !false_by_default,
 | |
|         cascade       : !false_by_default,
 | |
|         side_effects  : !false_by_default,
 | |
|         negate_iife   : !false_by_default,
 | |
|         screw_ie8     : false,
 | |
| 
 | |
|         warnings      : true,
 | |
|         global_defs   : {}
 | |
|     }, true);
 | |
| };
 | |
| 
 | |
| Compressor.prototype = new TreeTransformer;
 | |
| merge(Compressor.prototype, {
 | |
|     option: function(key) { return this.options[key] },
 | |
|     warn: function() {
 | |
|         if (this.options.warnings)
 | |
|             AST_Node.warn.apply(AST_Node, arguments);
 | |
|     },
 | |
|     before: function(node, descend, in_list) {
 | |
|         if (node._squeezed) return node;
 | |
|         if (node instanceof AST_Scope) {
 | |
|             node.drop_unused(this);
 | |
|             node = node.hoist_declarations(this);
 | |
|         }
 | |
|         descend(node, this);
 | |
|         node = node.optimize(this);
 | |
|         if (node instanceof AST_Scope) {
 | |
|             // dead code removal might leave further unused declarations.
 | |
|             // this'll usually save very few bytes, but the performance
 | |
|             // hit seems negligible so I'll just drop it here.
 | |
| 
 | |
|             // no point to repeat warnings.
 | |
|             var save_warnings = this.options.warnings;
 | |
|             this.options.warnings = false;
 | |
|             node.drop_unused(this);
 | |
|             this.options.warnings = save_warnings;
 | |
|         }
 | |
|         node._squeezed = true;
 | |
|         return node;
 | |
|     }
 | |
| });
 | |
| 
 | |
| (function(){
 | |
| 
 | |
|     function OPT(node, optimizer) {
 | |
|         node.DEFMETHOD("optimize", function(compressor){
 | |
|             var self = this;
 | |
|             if (self._optimized) return self;
 | |
|             var opt = optimizer(self, compressor);
 | |
|             opt._optimized = true;
 | |
|             if (opt === self) return opt;
 | |
|             return opt.transform(compressor);
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     OPT(AST_Node, function(self, compressor){
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     AST_Node.DEFMETHOD("equivalent_to", function(node){
 | |
|         // XXX: this is a rather expensive way to test two node's equivalence:
 | |
|         return this.print_to_string() == node.print_to_string();
 | |
|     });
 | |
| 
 | |
|     function make_node(ctor, orig, props) {
 | |
|         if (!props) props = {};
 | |
|         if (orig) {
 | |
|             if (!props.start) props.start = orig.start;
 | |
|             if (!props.end) props.end = orig.end;
 | |
|         }
 | |
|         return new ctor(props);
 | |
|     };
 | |
| 
 | |
|     function make_node_from_constant(compressor, val, orig) {
 | |
|         // XXX: WIP.
 | |
|         // if (val instanceof AST_Node) return val.transform(new TreeTransformer(null, function(node){
 | |
|         //     if (node instanceof AST_SymbolRef) {
 | |
|         //         var scope = compressor.find_parent(AST_Scope);
 | |
|         //         var def = scope.find_variable(node);
 | |
|         //         node.thedef = def;
 | |
|         //         return node;
 | |
|         //     }
 | |
|         // })).transform(compressor);
 | |
| 
 | |
|         if (val instanceof AST_Node) return val.transform(compressor);
 | |
|         switch (typeof val) {
 | |
|           case "string":
 | |
|             return make_node(AST_String, orig, {
 | |
|                 value: val
 | |
|             }).optimize(compressor);
 | |
|           case "number":
 | |
|             return make_node(isNaN(val) ? AST_NaN : AST_Number, orig, {
 | |
|                 value: val
 | |
|             }).optimize(compressor);
 | |
|           case "boolean":
 | |
|             return make_node(val ? AST_True : AST_False, orig).optimize(compressor);
 | |
|           case "undefined":
 | |
|             return make_node(AST_Undefined, orig).optimize(compressor);
 | |
|           default:
 | |
|             if (val === null) {
 | |
|                 return make_node(AST_Null, orig).optimize(compressor);
 | |
|             }
 | |
|             if (val instanceof RegExp) {
 | |
|                 return make_node(AST_RegExp, orig).optimize(compressor);
 | |
|             }
 | |
|             throw new Error(string_template("Can't handle constant of type: {type}", {
 | |
|                 type: typeof val
 | |
|             }));
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function as_statement_array(thing) {
 | |
|         if (thing === null) return [];
 | |
|         if (thing instanceof AST_BlockStatement) return thing.body;
 | |
|         if (thing instanceof AST_EmptyStatement) return [];
 | |
|         if (thing instanceof AST_Statement) return [ thing ];
 | |
|         throw new Error("Can't convert thing to statement array");
 | |
|     };
 | |
| 
 | |
|     function is_empty(thing) {
 | |
|         if (thing === null) return true;
 | |
|         if (thing instanceof AST_EmptyStatement) return true;
 | |
|         if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
 | |
|         return false;
 | |
|     };
 | |
| 
 | |
|     function loop_body(x) {
 | |
|         if (x instanceof AST_Switch) return x;
 | |
|         if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
 | |
|             return (x.body instanceof AST_BlockStatement ? x.body : x);
 | |
|         }
 | |
|         return x;
 | |
|     };
 | |
| 
 | |
|     function tighten_body(statements, compressor) {
 | |
|         var CHANGED;
 | |
|         do {
 | |
|             CHANGED = false;
 | |
|             statements = eliminate_spurious_blocks(statements);
 | |
|             if (compressor.option("dead_code")) {
 | |
|                 statements = eliminate_dead_code(statements, compressor);
 | |
|             }
 | |
|             if (compressor.option("if_return")) {
 | |
|                 statements = handle_if_return(statements, compressor);
 | |
|             }
 | |
|             if (compressor.option("sequences")) {
 | |
|                 statements = sequencesize(statements, compressor);
 | |
|             }
 | |
|             if (compressor.option("join_vars")) {
 | |
|                 statements = join_consecutive_vars(statements, compressor);
 | |
|             }
 | |
|         } while (CHANGED);
 | |
| 
 | |
|         if (compressor.option("negate_iife")) {
 | |
|             negate_iifes(statements, compressor);
 | |
|         }
 | |
| 
 | |
|         return statements;
 | |
| 
 | |
|         function eliminate_spurious_blocks(statements) {
 | |
|             var seen_dirs = [];
 | |
|             return statements.reduce(function(a, stat){
 | |
|                 if (stat instanceof AST_BlockStatement) {
 | |
|                     CHANGED = true;
 | |
|                     a.push.apply(a, eliminate_spurious_blocks(stat.body));
 | |
|                 } else if (stat instanceof AST_EmptyStatement) {
 | |
|                     CHANGED = true;
 | |
|                 } else if (stat instanceof AST_Directive) {
 | |
|                     if (seen_dirs.indexOf(stat.value) < 0) {
 | |
|                         a.push(stat);
 | |
|                         seen_dirs.push(stat.value);
 | |
|                     } else {
 | |
|                         CHANGED = true;
 | |
|                     }
 | |
|                 } else {
 | |
|                     a.push(stat);
 | |
|                 }
 | |
|                 return a;
 | |
|             }, []);
 | |
|         };
 | |
| 
 | |
|         function handle_if_return(statements, compressor) {
 | |
|             var self = compressor.self();
 | |
|             var in_lambda = self instanceof AST_Lambda;
 | |
|             var ret = [];
 | |
|             loop: for (var i = statements.length; --i >= 0;) {
 | |
|                 var stat = statements[i];
 | |
|                 switch (true) {
 | |
|                   case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
 | |
|                     CHANGED = true;
 | |
|                     // note, ret.length is probably always zero
 | |
|                     // because we drop unreachable code before this
 | |
|                     // step.  nevertheless, it's good to check.
 | |
|                     continue loop;
 | |
|                   case stat instanceof AST_If:
 | |
|                     if (stat.body instanceof AST_Return) {
 | |
|                         //---
 | |
|                         // pretty silly case, but:
 | |
|                         // if (foo()) return; return; ==> foo(); return;
 | |
|                         if (((in_lambda && ret.length == 0)
 | |
|                              || (ret[0] instanceof AST_Return && !ret[0].value))
 | |
|                             && !stat.body.value && !stat.alternative) {
 | |
|                             CHANGED = true;
 | |
|                             var cond = make_node(AST_SimpleStatement, stat.condition, {
 | |
|                                 body: stat.condition
 | |
|                             });
 | |
|                             ret.unshift(cond);
 | |
|                             continue loop;
 | |
|                         }
 | |
|                         //---
 | |
|                         // if (foo()) return x; return y; ==> return foo() ? x : y;
 | |
|                         if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) {
 | |
|                             CHANGED = true;
 | |
|                             stat = stat.clone();
 | |
|                             stat.alternative = ret[0];
 | |
|                             ret[0] = stat.transform(compressor);
 | |
|                             continue loop;
 | |
|                         }
 | |
|                         //---
 | |
|                         // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
 | |
|                         if ((ret.length == 0 || ret[0] instanceof AST_Return) && stat.body.value && !stat.alternative && in_lambda) {
 | |
|                             CHANGED = true;
 | |
|                             stat = stat.clone();
 | |
|                             stat.alternative = ret[0] || make_node(AST_Return, stat, {
 | |
|                                 value: make_node(AST_Undefined, stat)
 | |
|                             });
 | |
|                             ret[0] = stat.transform(compressor);
 | |
|                             continue loop;
 | |
|                         }
 | |
|                         //---
 | |
|                         // if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
 | |
|                         if (!stat.body.value && in_lambda) {
 | |
|                             CHANGED = true;
 | |
|                             stat = stat.clone();
 | |
|                             stat.condition = stat.condition.negate(compressor);
 | |
|                             stat.body = make_node(AST_BlockStatement, stat, {
 | |
|                                 body: as_statement_array(stat.alternative).concat(ret)
 | |
|                             });
 | |
|                             stat.alternative = null;
 | |
|                             ret = [ stat.transform(compressor) ];
 | |
|                             continue loop;
 | |
|                         }
 | |
|                         //---
 | |
|                         if (ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
 | |
|                             && (!stat.alternative || stat.alternative instanceof AST_SimpleStatement)) {
 | |
|                             CHANGED = true;
 | |
|                             ret.push(make_node(AST_Return, ret[0], {
 | |
|                                 value: make_node(AST_Undefined, ret[0])
 | |
|                             }).transform(compressor));
 | |
|                             ret = as_statement_array(stat.alternative).concat(ret);
 | |
|                             ret.unshift(stat);
 | |
|                             continue loop;
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     var ab = aborts(stat.body);
 | |
|                     var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
 | |
|                     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))) {
 | |
|                         if (ab.label) {
 | |
|                             remove(ab.label.thedef.references, ab.label);
 | |
|                         }
 | |
|                         CHANGED = true;
 | |
|                         var body = as_statement_array(stat.body).slice(0, -1);
 | |
|                         stat = stat.clone();
 | |
|                         stat.condition = stat.condition.negate(compressor);
 | |
|                         stat.body = make_node(AST_BlockStatement, stat, {
 | |
|                             body: ret
 | |
|                         });
 | |
|                         stat.alternative = make_node(AST_BlockStatement, stat, {
 | |
|                             body: body
 | |
|                         });
 | |
|                         ret = [ stat.transform(compressor) ];
 | |
|                         continue loop;
 | |
|                     }
 | |
| 
 | |
|                     var ab = aborts(stat.alternative);
 | |
|                     var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
 | |
|                     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))) {
 | |
|                         if (ab.label) {
 | |
|                             remove(ab.label.thedef.references, ab.label);
 | |
|                         }
 | |
|                         CHANGED = true;
 | |
|                         stat = stat.clone();
 | |
|                         stat.body = make_node(AST_BlockStatement, stat.body, {
 | |
|                             body: as_statement_array(stat.body).concat(ret)
 | |
|                         });
 | |
|                         stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
 | |
|                             body: as_statement_array(stat.alternative).slice(0, -1)
 | |
|                         });
 | |
|                         ret = [ stat.transform(compressor) ];
 | |
|                         continue loop;
 | |
|                     }
 | |
| 
 | |
|                     ret.unshift(stat);
 | |
|                     break;
 | |
|                   default:
 | |
|                     ret.unshift(stat);
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             return ret;
 | |
|         };
 | |
| 
 | |
|         function eliminate_dead_code(statements, compressor) {
 | |
|             var has_quit = false;
 | |
|             var orig = statements.length;
 | |
|             var self = compressor.self();
 | |
|             statements = statements.reduce(function(a, stat){
 | |
|                 if (has_quit) {
 | |
|                     extract_declarations_from_unreachable_code(compressor, stat, a);
 | |
|                 } else {
 | |
|                     if (stat instanceof AST_LoopControl) {
 | |
|                         var lct = compressor.loopcontrol_target(stat.label);
 | |
|                         if ((stat instanceof AST_Break
 | |
|                              && lct instanceof AST_BlockStatement
 | |
|                              && loop_body(lct) === self) || (stat instanceof AST_Continue
 | |
|                                                              && loop_body(lct) === self)) {
 | |
|                             if (stat.label) {
 | |
|                                 remove(stat.label.thedef.references, stat.label);
 | |
|                             }
 | |
|                         } else {
 | |
|                             a.push(stat);
 | |
|                         }
 | |
|                     } else {
 | |
|                         a.push(stat);
 | |
|                     }
 | |
|                     if (aborts(stat)) has_quit = true;
 | |
|                 }
 | |
|                 return a;
 | |
|             }, []);
 | |
|             CHANGED = statements.length != orig;
 | |
|             return statements;
 | |
|         };
 | |
| 
 | |
|         function sequencesize(statements, compressor) {
 | |
|             if (statements.length < 2) return statements;
 | |
|             var seq = [], ret = [];
 | |
|             function push_seq() {
 | |
|                 seq = AST_Seq.from_array(seq);
 | |
|                 if (seq) ret.push(make_node(AST_SimpleStatement, seq, {
 | |
|                     body: seq
 | |
|                 }));
 | |
|                 seq = [];
 | |
|             };
 | |
|             statements.forEach(function(stat){
 | |
|                 if (stat instanceof AST_SimpleStatement) seq.push(stat.body);
 | |
|                 else push_seq(), ret.push(stat);
 | |
|             });
 | |
|             push_seq();
 | |
|             ret = sequencesize_2(ret, compressor);
 | |
|             CHANGED = ret.length != statements.length;
 | |
|             return ret;
 | |
|         };
 | |
| 
 | |
|         function sequencesize_2(statements, compressor) {
 | |
|             function cons_seq(right) {
 | |
|                 ret.pop();
 | |
|                 var left = prev.body;
 | |
|                 if (left instanceof AST_Seq) {
 | |
|                     left.add(right);
 | |
|                 } else {
 | |
|                     left = AST_Seq.cons(left, right);
 | |
|                 }
 | |
|                 return left.transform(compressor);
 | |
|             };
 | |
|             var ret = [], prev = null;
 | |
|             statements.forEach(function(stat){
 | |
|                 if (prev) {
 | |
|                     if (stat instanceof AST_For) {
 | |
|                         var opera = {};
 | |
|                         try {
 | |
|                             prev.body.walk(new TreeWalker(function(node){
 | |
|                                 if (node instanceof AST_Binary && node.operator == "in")
 | |
|                                     throw opera;
 | |
|                             }));
 | |
|                             if (stat.init && !(stat.init instanceof AST_Definitions)) {
 | |
|                                 stat.init = cons_seq(stat.init);
 | |
|                             }
 | |
|                             else if (!stat.init) {
 | |
|                                 stat.init = prev.body;
 | |
|                                 ret.pop();
 | |
|                             }
 | |
|                         } catch(ex) {
 | |
|                             if (ex !== opera) throw ex;
 | |
|                         }
 | |
|                     }
 | |
|                     else if (stat instanceof AST_If) {
 | |
|                         stat.condition = cons_seq(stat.condition);
 | |
|                     }
 | |
|                     else if (stat instanceof AST_With) {
 | |
|                         stat.expression = cons_seq(stat.expression);
 | |
|                     }
 | |
|                     else if (stat instanceof AST_Exit && stat.value) {
 | |
|                         stat.value = cons_seq(stat.value);
 | |
|                     }
 | |
|                     else if (stat instanceof AST_Exit) {
 | |
|                         stat.value = cons_seq(make_node(AST_Undefined, stat));
 | |
|                     }
 | |
|                     else if (stat instanceof AST_Switch) {
 | |
|                         stat.expression = cons_seq(stat.expression);
 | |
|                     }
 | |
|                 }
 | |
|                 ret.push(stat);
 | |
|                 prev = stat instanceof AST_SimpleStatement ? stat : null;
 | |
|             });
 | |
|             return ret;
 | |
|         };
 | |
| 
 | |
|         function join_consecutive_vars(statements, compressor) {
 | |
|             var prev = null;
 | |
|             return statements.reduce(function(a, stat){
 | |
|                 if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {
 | |
|                     prev.definitions = prev.definitions.concat(stat.definitions);
 | |
|                     CHANGED = true;
 | |
|                 }
 | |
|                 else if (stat instanceof AST_For
 | |
|                          && prev instanceof AST_Definitions
 | |
|                          && (!stat.init || stat.init.TYPE == prev.TYPE)) {
 | |
|                     CHANGED = true;
 | |
|                     a.pop();
 | |
|                     if (stat.init) {
 | |
|                         stat.init.definitions = prev.definitions.concat(stat.init.definitions);
 | |
|                     } else {
 | |
|                         stat.init = prev;
 | |
|                     }
 | |
|                     a.push(stat);
 | |
|                     prev = stat;
 | |
|                 }
 | |
|                 else {
 | |
|                     prev = stat;
 | |
|                     a.push(stat);
 | |
|                 }
 | |
|                 return a;
 | |
|             }, []);
 | |
|         };
 | |
| 
 | |
|         function negate_iifes(statements, compressor) {
 | |
|             statements.forEach(function(stat){
 | |
|                 if (stat instanceof AST_SimpleStatement) {
 | |
|                     stat.body = (function transform(thing) {
 | |
|                         return thing.transform(new TreeTransformer(function(node){
 | |
|                             if (node instanceof AST_Call && node.expression instanceof AST_Function) {
 | |
|                                 return make_node(AST_UnaryPrefix, node, {
 | |
|                                     operator: "!",
 | |
|                                     expression: node
 | |
|                                 });
 | |
|                             }
 | |
|                             else if (node instanceof AST_Call) {
 | |
|                                 node.expression = transform(node.expression);
 | |
|                             }
 | |
|                             else if (node instanceof AST_Seq) {
 | |
|                                 node.car = transform(node.car);
 | |
|                             }
 | |
|                             else if (node instanceof AST_Conditional) {
 | |
|                                 var expr = transform(node.condition);
 | |
|                                 if (expr !== node.condition) {
 | |
|                                     // it has been negated, reverse
 | |
|                                     node.condition = expr;
 | |
|                                     var tmp = node.consequent;
 | |
|                                     node.consequent = node.alternative;
 | |
|                                     node.alternative = tmp;
 | |
|                                 }
 | |
|                             }
 | |
|                             return node;
 | |
|                         }));
 | |
|                     })(stat.body);
 | |
|                 }
 | |
|             });
 | |
|         };
 | |
| 
 | |
|     };
 | |
| 
 | |
|     function extract_declarations_from_unreachable_code(compressor, stat, target) {
 | |
|         compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
 | |
|         stat.walk(new TreeWalker(function(node){
 | |
|             if (node instanceof AST_Definitions) {
 | |
|                 compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
 | |
|                 node.remove_initializers();
 | |
|                 target.push(node);
 | |
|                 return true;
 | |
|             }
 | |
|             if (node instanceof AST_Defun) {
 | |
|                 target.push(node);
 | |
|                 return true;
 | |
|             }
 | |
|             if (node instanceof AST_Scope) {
 | |
|                 return true;
 | |
|             }
 | |
|         }));
 | |
|     };
 | |
| 
 | |
|     /* -----[ boolean/negation helpers ]----- */
 | |
| 
 | |
|     // methods to determine whether an expression has a boolean result type
 | |
|     (function (def){
 | |
|         var unary_bool = [ "!", "delete" ];
 | |
|         var binary_bool = [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ];
 | |
|         def(AST_Node, function(){ return false });
 | |
|         def(AST_UnaryPrefix, function(){
 | |
|             return member(this.operator, unary_bool);
 | |
|         });
 | |
|         def(AST_Binary, function(){
 | |
|             return member(this.operator, binary_bool) ||
 | |
|                 ( (this.operator == "&&" || this.operator == "||") &&
 | |
|                   this.left.is_boolean() && this.right.is_boolean() );
 | |
|         });
 | |
|         def(AST_Conditional, function(){
 | |
|             return this.consequent.is_boolean() && this.alternative.is_boolean();
 | |
|         });
 | |
|         def(AST_Assign, function(){
 | |
|             return this.operator == "=" && this.right.is_boolean();
 | |
|         });
 | |
|         def(AST_Seq, function(){
 | |
|             return this.cdr.is_boolean();
 | |
|         });
 | |
|         def(AST_True, function(){ return true });
 | |
|         def(AST_False, function(){ return true });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("is_boolean", func);
 | |
|     });
 | |
| 
 | |
|     // methods to determine if an expression has a string result type
 | |
|     (function (def){
 | |
|         def(AST_Node, function(){ return false });
 | |
|         def(AST_String, function(){ return true });
 | |
|         def(AST_UnaryPrefix, function(){
 | |
|             return this.operator == "typeof";
 | |
|         });
 | |
|         def(AST_Binary, function(compressor){
 | |
|             return this.operator == "+" &&
 | |
|                 (this.left.is_string(compressor) || this.right.is_string(compressor));
 | |
|         });
 | |
|         def(AST_Assign, function(compressor){
 | |
|             return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
 | |
|         });
 | |
|         def(AST_Seq, function(compressor){
 | |
|             return this.cdr.is_string(compressor);
 | |
|         });
 | |
|         def(AST_Conditional, function(compressor){
 | |
|             return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
 | |
|         });
 | |
|         def(AST_Call, function(compressor){
 | |
|             return compressor.option("unsafe")
 | |
|                 && this.expression instanceof AST_SymbolRef
 | |
|                 && this.expression.name == "String"
 | |
|                 && this.expression.undeclared();
 | |
|         });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("is_string", func);
 | |
|     });
 | |
| 
 | |
|     function best_of(ast1, ast2) {
 | |
|         return ast1.print_to_string().length >
 | |
|             ast2.print_to_string().length
 | |
|             ? ast2 : ast1;
 | |
|     };
 | |
| 
 | |
|     // methods to evaluate a constant expression
 | |
|     (function (def){
 | |
|         // The evaluate method returns an array with one or two
 | |
|         // elements.  If the node has been successfully reduced to a
 | |
|         // constant, then the second element tells us the value;
 | |
|         // otherwise the second element is missing.  The first element
 | |
|         // of the array is always an AST_Node descendant; when
 | |
|         // evaluation was successful it's a node that represents the
 | |
|         // constant; otherwise it's the original node.
 | |
|         AST_Node.DEFMETHOD("evaluate", function(compressor){
 | |
|             if (!compressor.option("evaluate")) return [ this ];
 | |
|             try {
 | |
|                 var val = this._eval(), ast = make_node_from_constant(compressor, val, this);
 | |
|                 return [ best_of(ast, this), val ];
 | |
|             } catch(ex) {
 | |
|                 if (ex !== def) throw ex;
 | |
|                 return [ this ];
 | |
|             }
 | |
|         });
 | |
|         def(AST_Statement, function(){
 | |
|             throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
 | |
|         });
 | |
|         def(AST_Function, function(){
 | |
|             // XXX: AST_Function inherits from AST_Scope, which itself
 | |
|             // inherits from AST_Statement; however, an AST_Function
 | |
|             // isn't really a statement.  This could byte in other
 | |
|             // places too. :-( Wish JS had multiple inheritance.
 | |
|             throw def;
 | |
|         });
 | |
|         function ev(node) {
 | |
|             return node._eval();
 | |
|         };
 | |
|         def(AST_Node, function(){
 | |
|             throw def;          // not constant
 | |
|         });
 | |
|         def(AST_Constant, function(){
 | |
|             return this.getValue();
 | |
|         });
 | |
|         def(AST_UnaryPrefix, function(){
 | |
|             var e = this.expression;
 | |
|             switch (this.operator) {
 | |
|               case "!": return !ev(e);
 | |
|               case "typeof":
 | |
|                 // Function would be evaluated to an array and so typeof would
 | |
|                 // incorrectly return 'object'. Hence making is a special case.
 | |
|                 if (e instanceof AST_Function) return typeof function(){};
 | |
| 
 | |
|                 e = ev(e);
 | |
| 
 | |
|                 // typeof <RegExp> returns "object" or "function" on different platforms
 | |
|                 // so cannot evaluate reliably
 | |
|                 if (e instanceof RegExp) throw def;
 | |
| 
 | |
|                 return typeof e;
 | |
|               case "void": return void ev(e);
 | |
|               case "~": return ~ev(e);
 | |
|               case "-":
 | |
|                 e = ev(e);
 | |
|                 if (e === 0) throw def;
 | |
|                 return -e;
 | |
|               case "+": return +ev(e);
 | |
|             }
 | |
|             throw def;
 | |
|         });
 | |
|         def(AST_Binary, function(){
 | |
|             var left = this.left, right = this.right;
 | |
|             switch (this.operator) {
 | |
|               case "&&"         : return ev(left) &&         ev(right);
 | |
|               case "||"         : return ev(left) ||         ev(right);
 | |
|               case "|"          : return ev(left) |          ev(right);
 | |
|               case "&"          : return ev(left) &          ev(right);
 | |
|               case "^"          : return ev(left) ^          ev(right);
 | |
|               case "+"          : return ev(left) +          ev(right);
 | |
|               case "*"          : return ev(left) *          ev(right);
 | |
|               case "/"          : return ev(left) /          ev(right);
 | |
|               case "%"          : return ev(left) %          ev(right);
 | |
|               case "-"          : return ev(left) -          ev(right);
 | |
|               case "<<"         : return ev(left) <<         ev(right);
 | |
|               case ">>"         : return ev(left) >>         ev(right);
 | |
|               case ">>>"        : return ev(left) >>>        ev(right);
 | |
|               case "=="         : return ev(left) ==         ev(right);
 | |
|               case "==="        : return ev(left) ===        ev(right);
 | |
|               case "!="         : return ev(left) !=         ev(right);
 | |
|               case "!=="        : return ev(left) !==        ev(right);
 | |
|               case "<"          : return ev(left) <          ev(right);
 | |
|               case "<="         : return ev(left) <=         ev(right);
 | |
|               case ">"          : return ev(left) >          ev(right);
 | |
|               case ">="         : return ev(left) >=         ev(right);
 | |
|               case "in"         : return ev(left) in         ev(right);
 | |
|               case "instanceof" : return ev(left) instanceof ev(right);
 | |
|             }
 | |
|             throw def;
 | |
|         });
 | |
|         def(AST_Conditional, function(){
 | |
|             return ev(this.condition)
 | |
|                 ? ev(this.consequent)
 | |
|                 : ev(this.alternative);
 | |
|         });
 | |
|         def(AST_SymbolRef, function(){
 | |
|             var d = this.definition();
 | |
|             if (d && d.constant && d.init) return ev(d.init);
 | |
|             throw def;
 | |
|         });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("_eval", func);
 | |
|     });
 | |
| 
 | |
|     // method to negate an expression
 | |
|     (function(def){
 | |
|         function basic_negation(exp) {
 | |
|             return make_node(AST_UnaryPrefix, exp, {
 | |
|                 operator: "!",
 | |
|                 expression: exp
 | |
|             });
 | |
|         };
 | |
|         def(AST_Node, function(){
 | |
|             return basic_negation(this);
 | |
|         });
 | |
|         def(AST_Statement, function(){
 | |
|             throw new Error("Cannot negate a statement");
 | |
|         });
 | |
|         def(AST_Function, function(){
 | |
|             return basic_negation(this);
 | |
|         });
 | |
|         def(AST_UnaryPrefix, function(){
 | |
|             if (this.operator == "!")
 | |
|                 return this.expression;
 | |
|             return basic_negation(this);
 | |
|         });
 | |
|         def(AST_Seq, function(compressor){
 | |
|             var self = this.clone();
 | |
|             self.cdr = self.cdr.negate(compressor);
 | |
|             return self;
 | |
|         });
 | |
|         def(AST_Conditional, function(compressor){
 | |
|             var self = this.clone();
 | |
|             self.consequent = self.consequent.negate(compressor);
 | |
|             self.alternative = self.alternative.negate(compressor);
 | |
|             return best_of(basic_negation(this), self);
 | |
|         });
 | |
|         def(AST_Binary, function(compressor){
 | |
|             var self = this.clone(), op = this.operator;
 | |
|             if (compressor.option("unsafe_comps")) {
 | |
|                 switch (op) {
 | |
|                   case "<=" : self.operator = ">"  ; return self;
 | |
|                   case "<"  : self.operator = ">=" ; return self;
 | |
|                   case ">=" : self.operator = "<"  ; return self;
 | |
|                   case ">"  : self.operator = "<=" ; return self;
 | |
|                 }
 | |
|             }
 | |
|             switch (op) {
 | |
|               case "==" : self.operator = "!="; return self;
 | |
|               case "!=" : self.operator = "=="; return self;
 | |
|               case "===": self.operator = "!=="; return self;
 | |
|               case "!==": self.operator = "==="; return self;
 | |
|               case "&&":
 | |
|                 self.operator = "||";
 | |
|                 self.left = self.left.negate(compressor);
 | |
|                 self.right = self.right.negate(compressor);
 | |
|                 return best_of(basic_negation(this), self);
 | |
|               case "||":
 | |
|                 self.operator = "&&";
 | |
|                 self.left = self.left.negate(compressor);
 | |
|                 self.right = self.right.negate(compressor);
 | |
|                 return best_of(basic_negation(this), self);
 | |
|             }
 | |
|             return basic_negation(this);
 | |
|         });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("negate", function(compressor){
 | |
|             return func.call(this, compressor);
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     // determine if expression has side effects
 | |
|     (function(def){
 | |
|         def(AST_Node, function(){ return true });
 | |
| 
 | |
|         def(AST_EmptyStatement, function(){ return false });
 | |
|         def(AST_Constant, function(){ return false });
 | |
|         def(AST_This, function(){ return false });
 | |
| 
 | |
|         def(AST_Block, function(){
 | |
|             for (var i = this.body.length; --i >= 0;) {
 | |
|                 if (this.body[i].has_side_effects())
 | |
|                     return true;
 | |
|             }
 | |
|             return false;
 | |
|         });
 | |
| 
 | |
|         def(AST_SimpleStatement, function(){
 | |
|             return this.body.has_side_effects();
 | |
|         });
 | |
|         def(AST_Defun, function(){ return true });
 | |
|         def(AST_Function, function(){ return false });
 | |
|         def(AST_Binary, function(){
 | |
|             return this.left.has_side_effects()
 | |
|                 || this.right.has_side_effects();
 | |
|         });
 | |
|         def(AST_Assign, function(){ return true });
 | |
|         def(AST_Conditional, function(){
 | |
|             return this.condition.has_side_effects()
 | |
|                 || this.consequent.has_side_effects()
 | |
|                 || this.alternative.has_side_effects();
 | |
|         });
 | |
|         def(AST_Unary, function(){
 | |
|             return this.operator == "delete"
 | |
|                 || this.operator == "++"
 | |
|                 || this.operator == "--"
 | |
|                 || this.expression.has_side_effects();
 | |
|         });
 | |
|         def(AST_SymbolRef, function(){ return false });
 | |
|         def(AST_Object, function(){
 | |
|             for (var i = this.properties.length; --i >= 0;)
 | |
|                 if (this.properties[i].has_side_effects())
 | |
|                     return true;
 | |
|             return false;
 | |
|         });
 | |
|         def(AST_ObjectProperty, function(){
 | |
|             return this.value.has_side_effects();
 | |
|         });
 | |
|         def(AST_Array, function(){
 | |
|             for (var i = this.elements.length; --i >= 0;)
 | |
|                 if (this.elements[i].has_side_effects())
 | |
|                     return true;
 | |
|             return false;
 | |
|         });
 | |
|         // def(AST_Dot, function(){
 | |
|         //     return this.expression.has_side_effects();
 | |
|         // });
 | |
|         // def(AST_Sub, function(){
 | |
|         //     return this.expression.has_side_effects()
 | |
|         //         || this.property.has_side_effects();
 | |
|         // });
 | |
|         def(AST_PropAccess, function(){
 | |
|             return true;
 | |
|         });
 | |
|         def(AST_Seq, function(){
 | |
|             return this.car.has_side_effects()
 | |
|                 || this.cdr.has_side_effects();
 | |
|         });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("has_side_effects", func);
 | |
|     });
 | |
| 
 | |
|     // tell me if a statement aborts
 | |
|     function aborts(thing) {
 | |
|         return thing && thing.aborts();
 | |
|     };
 | |
|     (function(def){
 | |
|         def(AST_Statement, function(){ return null });
 | |
|         def(AST_Jump, function(){ return this });
 | |
|         function block_aborts(){
 | |
|             var n = this.body.length;
 | |
|             return n > 0 && aborts(this.body[n - 1]);
 | |
|         };
 | |
|         def(AST_BlockStatement, block_aborts);
 | |
|         def(AST_SwitchBranch, block_aborts);
 | |
|         def(AST_If, function(){
 | |
|             return this.alternative && aborts(this.body) && aborts(this.alternative);
 | |
|         });
 | |
|     })(function(node, func){
 | |
|         node.DEFMETHOD("aborts", func);
 | |
|     });
 | |
| 
 | |
|     /* -----[ optimizers ]----- */
 | |
| 
 | |
|     OPT(AST_Directive, function(self, compressor){
 | |
|         if (self.scope.has_directive(self.value) !== self.scope) {
 | |
|             return make_node(AST_EmptyStatement, self);
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Debugger, function(self, compressor){
 | |
|         if (compressor.option("drop_debugger"))
 | |
|             return make_node(AST_EmptyStatement, self);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_LabeledStatement, function(self, compressor){
 | |
|         if (self.body instanceof AST_Break
 | |
|             && compressor.loopcontrol_target(self.body.label) === self.body) {
 | |
|             return make_node(AST_EmptyStatement, self);
 | |
|         }
 | |
|         return self.label.references.length == 0 ? self.body : self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Block, function(self, compressor){
 | |
|         self.body = tighten_body(self.body, compressor);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_BlockStatement, function(self, compressor){
 | |
|         self.body = tighten_body(self.body, compressor);
 | |
|         switch (self.body.length) {
 | |
|           case 1: return self.body[0];
 | |
|           case 0: return make_node(AST_EmptyStatement, self);
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     AST_Scope.DEFMETHOD("drop_unused", function(compressor){
 | |
|         var self = this;
 | |
|         if (compressor.option("unused")
 | |
|             && !(self instanceof AST_Toplevel)
 | |
|             && !self.uses_eval
 | |
|            ) {
 | |
|             var in_use = [];
 | |
|             var initializations = new Dictionary();
 | |
|             // pass 1: find out which symbols are directly used in
 | |
|             // this scope (not in nested scopes).
 | |
|             var scope = this;
 | |
|             var tw = new TreeWalker(function(node, descend){
 | |
|                 if (node !== self) {
 | |
|                     if (node instanceof AST_Defun) {
 | |
|                         initializations.add(node.name.name, node);
 | |
|                         return true; // don't go in nested scopes
 | |
|                     }
 | |
|                     if (node instanceof AST_Definitions && scope === self) {
 | |
|                         node.definitions.forEach(function(def){
 | |
|                             if (def.value) {
 | |
|                                 initializations.add(def.name.name, def.value);
 | |
|                                 if (def.value.has_side_effects()) {
 | |
|                                     def.value.walk(tw);
 | |
|                                 }
 | |
|                             }
 | |
|                         });
 | |
|                         return true;
 | |
|                     }
 | |
|                     if (node instanceof AST_SymbolRef) {
 | |
|                         push_uniq(in_use, node.definition());
 | |
|                         return true;
 | |
|                     }
 | |
|                     if (node instanceof AST_Scope) {
 | |
|                         var save_scope = scope;
 | |
|                         scope = node;
 | |
|                         descend();
 | |
|                         scope = save_scope;
 | |
|                         return true;
 | |
|                     }
 | |
|                 }
 | |
|             });
 | |
|             self.walk(tw);
 | |
|             // pass 2: for every used symbol we need to walk its
 | |
|             // initialization code to figure out if it uses other
 | |
|             // symbols (that may not be in_use).
 | |
|             for (var i = 0; i < in_use.length; ++i) {
 | |
|                 in_use[i].orig.forEach(function(decl){
 | |
|                     // undeclared globals will be instanceof AST_SymbolRef
 | |
|                     var init = initializations.get(decl.name);
 | |
|                     if (init) init.forEach(function(init){
 | |
|                         var tw = new TreeWalker(function(node){
 | |
|                             if (node instanceof AST_SymbolRef) {
 | |
|                                 push_uniq(in_use, node.definition());
 | |
|                             }
 | |
|                         });
 | |
|                         init.walk(tw);
 | |
|                     });
 | |
|                 });
 | |
|             }
 | |
|             // pass 3: we should drop declarations not in_use
 | |
|             var tt = new TreeTransformer(
 | |
|                 function before(node, descend, in_list) {
 | |
|                     if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
 | |
|                         for (var a = node.argnames, i = a.length; --i >= 0;) {
 | |
|                             var sym = a[i];
 | |
|                             if (sym.unreferenced()) {
 | |
|                                 a.pop();
 | |
|                                 compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
 | |
|                                     name : sym.name,
 | |
|                                     file : sym.start.file,
 | |
|                                     line : sym.start.line,
 | |
|                                     col  : sym.start.col
 | |
|                                 });
 | |
|                             }
 | |
|                             else break;
 | |
|                         }
 | |
|                     }
 | |
|                     if (node instanceof AST_Defun && node !== self) {
 | |
|                         if (!member(node.name.definition(), in_use)) {
 | |
|                             compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
 | |
|                                 name : node.name.name,
 | |
|                                 file : node.name.start.file,
 | |
|                                 line : node.name.start.line,
 | |
|                                 col  : node.name.start.col
 | |
|                             });
 | |
|                             return make_node(AST_EmptyStatement, node);
 | |
|                         }
 | |
|                         return node;
 | |
|                     }
 | |
|                     if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
 | |
|                         var def = node.definitions.filter(function(def){
 | |
|                             if (member(def.name.definition(), in_use)) return true;
 | |
|                             var w = {
 | |
|                                 name : def.name.name,
 | |
|                                 file : def.name.start.file,
 | |
|                                 line : def.name.start.line,
 | |
|                                 col  : def.name.start.col
 | |
|                             };
 | |
|                             if (def.value && def.value.has_side_effects()) {
 | |
|                                 def._unused_side_effects = true;
 | |
|                                 compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
 | |
|                                 return true;
 | |
|                             }
 | |
|                             compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
 | |
|                             return false;
 | |
|                         });
 | |
|                         // place uninitialized names at the start
 | |
|                         def = mergeSort(def, function(a, b){
 | |
|                             if (!a.value && b.value) return -1;
 | |
|                             if (!b.value && a.value) return 1;
 | |
|                             return 0;
 | |
|                         });
 | |
|                         // for unused names whose initialization has
 | |
|                         // side effects, we can cascade the init. code
 | |
|                         // into the next one, or next statement.
 | |
|                         var side_effects = [];
 | |
|                         for (var i = 0; i < def.length;) {
 | |
|                             var x = def[i];
 | |
|                             if (x._unused_side_effects) {
 | |
|                                 side_effects.push(x.value);
 | |
|                                 def.splice(i, 1);
 | |
|                             } else {
 | |
|                                 if (side_effects.length > 0) {
 | |
|                                     side_effects.push(x.value);
 | |
|                                     x.value = AST_Seq.from_array(side_effects);
 | |
|                                     side_effects = [];
 | |
|                                 }
 | |
|                                 ++i;
 | |
|                             }
 | |
|                         }
 | |
|                         if (side_effects.length > 0) {
 | |
|                             side_effects = make_node(AST_BlockStatement, node, {
 | |
|                                 body: [ make_node(AST_SimpleStatement, node, {
 | |
|                                     body: AST_Seq.from_array(side_effects)
 | |
|                                 }) ]
 | |
|                             });
 | |
|                         } else {
 | |
|                             side_effects = null;
 | |
|                         }
 | |
|                         if (def.length == 0 && !side_effects) {
 | |
|                             return make_node(AST_EmptyStatement, node);
 | |
|                         }
 | |
|                         if (def.length == 0) {
 | |
|                             return side_effects;
 | |
|                         }
 | |
|                         node.definitions = def;
 | |
|                         if (side_effects) {
 | |
|                             side_effects.body.unshift(node);
 | |
|                             node = side_effects;
 | |
|                         }
 | |
|                         return node;
 | |
|                     }
 | |
|                     if (node instanceof AST_For && node.init instanceof AST_BlockStatement) {
 | |
|                         descend(node, this);
 | |
|                         // certain combination of unused name + side effect leads to:
 | |
|                         //    https://github.com/mishoo/UglifyJS2/issues/44
 | |
|                         // that's an invalid AST.
 | |
|                         // We fix it at this stage by moving the `var` outside the `for`.
 | |
|                         var body = node.init.body.slice(0, -1);
 | |
|                         node.init = node.init.body.slice(-1)[0].body;
 | |
|                         body.push(node);
 | |
|                         return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
 | |
|                             body: body
 | |
|                         });
 | |
|                     }
 | |
|                     if (node instanceof AST_Scope && node !== self)
 | |
|                         return node;
 | |
|                 }
 | |
|             );
 | |
|             self.transform(tt);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
 | |
|         var hoist_funs = compressor.option("hoist_funs");
 | |
|         var hoist_vars = compressor.option("hoist_vars");
 | |
|         var self = this;
 | |
|         if (hoist_funs || hoist_vars) {
 | |
|             var dirs = [];
 | |
|             var hoisted = [];
 | |
|             var vars = new Dictionary(), vars_found = 0, var_decl = 0;
 | |
|             // let's count var_decl first, we seem to waste a lot of
 | |
|             // space if we hoist `var` when there's only one.
 | |
|             self.walk(new TreeWalker(function(node){
 | |
|                 if (node instanceof AST_Scope && node !== self)
 | |
|                     return true;
 | |
|                 if (node instanceof AST_Var) {
 | |
|                     ++var_decl;
 | |
|                     return true;
 | |
|                 }
 | |
|             }));
 | |
|             hoist_vars = hoist_vars && var_decl > 1;
 | |
|             var tt = new TreeTransformer(
 | |
|                 function before(node) {
 | |
|                     if (node !== self) {
 | |
|                         if (node instanceof AST_Directive) {
 | |
|                             dirs.push(node);
 | |
|                             return make_node(AST_EmptyStatement, node);
 | |
|                         }
 | |
|                         if (node instanceof AST_Defun && hoist_funs) {
 | |
|                             hoisted.push(node);
 | |
|                             return make_node(AST_EmptyStatement, node);
 | |
|                         }
 | |
|                         if (node instanceof AST_Var && hoist_vars) {
 | |
|                             node.definitions.forEach(function(def){
 | |
|                                 vars.set(def.name.name, def);
 | |
|                                 ++vars_found;
 | |
|                             });
 | |
|                             var seq = node.to_assignments();
 | |
|                             var p = tt.parent();
 | |
|                             if (p instanceof AST_ForIn && p.init === node) {
 | |
|                                 if (seq == null) return node.definitions[0].name;
 | |
|                                 return seq;
 | |
|                             }
 | |
|                             if (p instanceof AST_For && p.init === node) {
 | |
|                                 return seq;
 | |
|                             }
 | |
|                             if (!seq) return make_node(AST_EmptyStatement, node);
 | |
|                             return make_node(AST_SimpleStatement, node, {
 | |
|                                 body: seq
 | |
|                             });
 | |
|                         }
 | |
|                         if (node instanceof AST_Scope)
 | |
|                             return node; // to avoid descending in nested scopes
 | |
|                     }
 | |
|                 }
 | |
|             );
 | |
|             self = self.transform(tt);
 | |
|             if (vars_found > 0) {
 | |
|                 // collect only vars which don't show up in self's arguments list
 | |
|                 var defs = [];
 | |
|                 vars.each(function(def, name){
 | |
|                     if (self instanceof AST_Lambda
 | |
|                         && find_if(function(x){ return x.name == def.name.name },
 | |
|                                    self.argnames)) {
 | |
|                         vars.del(name);
 | |
|                     } else {
 | |
|                         def = def.clone();
 | |
|                         def.value = null;
 | |
|                         defs.push(def);
 | |
|                         vars.set(name, def);
 | |
|                     }
 | |
|                 });
 | |
|                 if (defs.length > 0) {
 | |
|                     // try to merge in assignments
 | |
|                     for (var i = 0; i < self.body.length;) {
 | |
|                         if (self.body[i] instanceof AST_SimpleStatement) {
 | |
|                             var expr = self.body[i].body, sym, assign;
 | |
|                             if (expr instanceof AST_Assign
 | |
|                                 && expr.operator == "="
 | |
|                                 && (sym = expr.left) instanceof AST_Symbol
 | |
|                                 && vars.has(sym.name))
 | |
|                             {
 | |
|                                 var def = vars.get(sym.name);
 | |
|                                 if (def.value) break;
 | |
|                                 def.value = expr.right;
 | |
|                                 remove(defs, def);
 | |
|                                 defs.push(def);
 | |
|                                 self.body.splice(i, 1);
 | |
|                                 continue;
 | |
|                             }
 | |
|                             if (expr instanceof AST_Seq
 | |
|                                 && (assign = expr.car) instanceof AST_Assign
 | |
|                                 && assign.operator == "="
 | |
|                                 && (sym = assign.left) instanceof AST_Symbol
 | |
|                                 && vars.has(sym.name))
 | |
|                             {
 | |
|                                 var def = vars.get(sym.name);
 | |
|                                 if (def.value) break;
 | |
|                                 def.value = assign.right;
 | |
|                                 remove(defs, def);
 | |
|                                 defs.push(def);
 | |
|                                 self.body[i].body = expr.cdr;
 | |
|                                 continue;
 | |
|                             }
 | |
|                         }
 | |
|                         if (self.body[i] instanceof AST_EmptyStatement) {
 | |
|                             self.body.splice(i, 1);
 | |
|                             continue;
 | |
|                         }
 | |
|                         if (self.body[i] instanceof AST_BlockStatement) {
 | |
|                             var tmp = [ i, 1 ].concat(self.body[i].body);
 | |
|                             self.body.splice.apply(self.body, tmp);
 | |
|                             continue;
 | |
|                         }
 | |
|                         break;
 | |
|                     }
 | |
|                     defs = make_node(AST_Var, self, {
 | |
|                         definitions: defs
 | |
|                     });
 | |
|                     hoisted.push(defs);
 | |
|                 };
 | |
|             }
 | |
|             self.body = dirs.concat(hoisted, self.body);
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_SimpleStatement, function(self, compressor){
 | |
|         if (compressor.option("side_effects")) {
 | |
|             if (!self.body.has_side_effects()) {
 | |
|                 compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
 | |
|                 return make_node(AST_EmptyStatement, self);
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_DWLoop, function(self, compressor){
 | |
|         var cond = self.condition.evaluate(compressor);
 | |
|         self.condition = cond[0];
 | |
|         if (!compressor.option("loops")) return self;
 | |
|         if (cond.length > 1) {
 | |
|             if (cond[1]) {
 | |
|                 return make_node(AST_For, self, {
 | |
|                     body: self.body
 | |
|                 });
 | |
|             } else if (self instanceof AST_While) {
 | |
|                 if (compressor.option("dead_code")) {
 | |
|                     var a = [];
 | |
|                     extract_declarations_from_unreachable_code(compressor, self.body, a);
 | |
|                     return make_node(AST_BlockStatement, self, { body: a });
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     function if_break_in_loop(self, compressor) {
 | |
|         function drop_it(rest) {
 | |
|             rest = as_statement_array(rest);
 | |
|             if (self.body instanceof AST_BlockStatement) {
 | |
|                 self.body = self.body.clone();
 | |
|                 self.body.body = rest.concat(self.body.body.slice(1));
 | |
|                 self.body = self.body.transform(compressor);
 | |
|             } else {
 | |
|                 self.body = make_node(AST_BlockStatement, self.body, {
 | |
|                     body: rest
 | |
|                 }).transform(compressor);
 | |
|             }
 | |
|             if_break_in_loop(self, compressor);
 | |
|         }
 | |
|         var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
 | |
|         if (first instanceof AST_If) {
 | |
|             if (first.body instanceof AST_Break
 | |
|                 && compressor.loopcontrol_target(first.body.label) === self) {
 | |
|                 if (self.condition) {
 | |
|                     self.condition = make_node(AST_Binary, self.condition, {
 | |
|                         left: self.condition,
 | |
|                         operator: "&&",
 | |
|                         right: first.condition.negate(compressor),
 | |
|                     });
 | |
|                 } else {
 | |
|                     self.condition = first.condition.negate(compressor);
 | |
|                 }
 | |
|                 drop_it(first.alternative);
 | |
|             }
 | |
|             else if (first.alternative instanceof AST_Break
 | |
|                      && compressor.loopcontrol_target(first.alternative.label) === self) {
 | |
|                 if (self.condition) {
 | |
|                     self.condition = make_node(AST_Binary, self.condition, {
 | |
|                         left: self.condition,
 | |
|                         operator: "&&",
 | |
|                         right: first.condition,
 | |
|                     });
 | |
|                 } else {
 | |
|                     self.condition = first.condition;
 | |
|                 }
 | |
|                 drop_it(first.body);
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     OPT(AST_While, function(self, compressor) {
 | |
|         if (!compressor.option("loops")) return self;
 | |
|         self = AST_DWLoop.prototype.optimize.call(self, compressor);
 | |
|         if (self instanceof AST_While) {
 | |
|             if_break_in_loop(self, compressor);
 | |
|             self = make_node(AST_For, self, self).transform(compressor);
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_For, function(self, compressor){
 | |
|         var cond = self.condition;
 | |
|         if (cond) {
 | |
|             cond = cond.evaluate(compressor);
 | |
|             self.condition = cond[0];
 | |
|         }
 | |
|         if (!compressor.option("loops")) return self;
 | |
|         if (cond) {
 | |
|             if (cond.length > 1 && !cond[1]) {
 | |
|                 if (compressor.option("dead_code")) {
 | |
|                     var a = [];
 | |
|                     if (self.init instanceof AST_Statement) {
 | |
|                         a.push(self.init);
 | |
|                     }
 | |
|                     else if (self.init) {
 | |
|                         a.push(make_node(AST_SimpleStatement, self.init, {
 | |
|                             body: self.init
 | |
|                         }));
 | |
|                     }
 | |
|                     extract_declarations_from_unreachable_code(compressor, self.body, a);
 | |
|                     return make_node(AST_BlockStatement, self, { body: a });
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if_break_in_loop(self, compressor);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_If, function(self, compressor){
 | |
|         if (!compressor.option("conditionals")) return self;
 | |
|         // if condition can be statically determined, warn and drop
 | |
|         // one of the blocks.  note, statically determined implies
 | |
|         // “has no side effects”; also it doesn't work for cases like
 | |
|         // `x && true`, though it probably should.
 | |
|         var cond = self.condition.evaluate(compressor);
 | |
|         self.condition = cond[0];
 | |
|         if (cond.length > 1) {
 | |
|             if (cond[1]) {
 | |
|                 compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
 | |
|                 if (compressor.option("dead_code")) {
 | |
|                     var a = [];
 | |
|                     if (self.alternative) {
 | |
|                         extract_declarations_from_unreachable_code(compressor, self.alternative, a);
 | |
|                     }
 | |
|                     a.push(self.body);
 | |
|                     return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);
 | |
|                 }
 | |
|             } else {
 | |
|                 compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
 | |
|                 if (compressor.option("dead_code")) {
 | |
|                     var a = [];
 | |
|                     extract_declarations_from_unreachable_code(compressor, self.body, a);
 | |
|                     if (self.alternative) a.push(self.alternative);
 | |
|                     return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if (is_empty(self.alternative)) self.alternative = null;
 | |
|         var negated = self.condition.negate(compressor);
 | |
|         var negated_is_best = best_of(self.condition, negated) === negated;
 | |
|         if (self.alternative && negated_is_best) {
 | |
|             negated_is_best = false; // because we already do the switch here.
 | |
|             self.condition = negated;
 | |
|             var tmp = self.body;
 | |
|             self.body = self.alternative || make_node(AST_EmptyStatement);
 | |
|             self.alternative = tmp;
 | |
|         }
 | |
|         if (is_empty(self.body) && is_empty(self.alternative)) {
 | |
|             return make_node(AST_SimpleStatement, self.condition, {
 | |
|                 body: self.condition
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         if (self.body instanceof AST_SimpleStatement
 | |
|             && self.alternative instanceof AST_SimpleStatement) {
 | |
|             return make_node(AST_SimpleStatement, self, {
 | |
|                 body: make_node(AST_Conditional, self, {
 | |
|                     condition   : self.condition,
 | |
|                     consequent  : self.body.body,
 | |
|                     alternative : self.alternative.body
 | |
|                 })
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
 | |
|             if (negated_is_best) return make_node(AST_SimpleStatement, self, {
 | |
|                 body: make_node(AST_Binary, self, {
 | |
|                     operator : "||",
 | |
|                     left     : negated,
 | |
|                     right    : self.body.body
 | |
|                 })
 | |
|             }).transform(compressor);
 | |
|             return make_node(AST_SimpleStatement, self, {
 | |
|                 body: make_node(AST_Binary, self, {
 | |
|                     operator : "&&",
 | |
|                     left     : self.condition,
 | |
|                     right    : self.body.body
 | |
|                 })
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         if (self.body instanceof AST_EmptyStatement
 | |
|             && self.alternative
 | |
|             && self.alternative instanceof AST_SimpleStatement) {
 | |
|             return make_node(AST_SimpleStatement, self, {
 | |
|                 body: make_node(AST_Binary, self, {
 | |
|                     operator : "||",
 | |
|                     left     : self.condition,
 | |
|                     right    : self.alternative.body
 | |
|                 })
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         if (self.body instanceof AST_Exit
 | |
|             && self.alternative instanceof AST_Exit
 | |
|             && self.body.TYPE == self.alternative.TYPE) {
 | |
|             return make_node(self.body.CTOR, self, {
 | |
|                 value: make_node(AST_Conditional, self, {
 | |
|                     condition   : self.condition,
 | |
|                     consequent  : self.body.value || make_node(AST_Undefined, self.body).optimize(compressor),
 | |
|                     alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).optimize(compressor)
 | |
|                 })
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         if (self.body instanceof AST_If
 | |
|             && !self.body.alternative
 | |
|             && !self.alternative) {
 | |
|             self.condition = make_node(AST_Binary, self.condition, {
 | |
|                 operator: "&&",
 | |
|                 left: self.condition,
 | |
|                 right: self.body.condition
 | |
|             }).transform(compressor);
 | |
|             self.body = self.body.body;
 | |
|         }
 | |
|         if (aborts(self.body)) {
 | |
|             if (self.alternative) {
 | |
|                 var alt = self.alternative;
 | |
|                 self.alternative = null;
 | |
|                 return make_node(AST_BlockStatement, self, {
 | |
|                     body: [ self, alt ]
 | |
|                 }).transform(compressor);
 | |
|             }
 | |
|         }
 | |
|         if (aborts(self.alternative)) {
 | |
|             var body = self.body;
 | |
|             self.body = self.alternative;
 | |
|             self.condition = negated_is_best ? negated : self.condition.negate(compressor);
 | |
|             self.alternative = null;
 | |
|             return make_node(AST_BlockStatement, self, {
 | |
|                 body: [ self, body ]
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Switch, function(self, compressor){
 | |
|         if (self.body.length == 0 && compressor.option("conditionals")) {
 | |
|             return make_node(AST_SimpleStatement, self, {
 | |
|                 body: self.expression
 | |
|             }).transform(compressor);
 | |
|         }
 | |
|         for(;;) {
 | |
|             var last_branch = self.body[self.body.length - 1];
 | |
|             if (last_branch) {
 | |
|                 var stat = last_branch.body[last_branch.body.length - 1]; // last statement
 | |
|                 if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
 | |
|                     last_branch.body.pop();
 | |
|                 if (last_branch instanceof AST_Default && last_branch.body.length == 0) {
 | |
|                     self.body.pop();
 | |
|                     continue;
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         var exp = self.expression.evaluate(compressor);
 | |
|         out: if (exp.length == 2) try {
 | |
|             // constant expression
 | |
|             self.expression = exp[0];
 | |
|             if (!compressor.option("dead_code")) break out;
 | |
|             var value = exp[1];
 | |
|             var in_if = false;
 | |
|             var in_block = false;
 | |
|             var started = false;
 | |
|             var stopped = false;
 | |
|             var ruined = false;
 | |
|             var tt = new TreeTransformer(function(node, descend, in_list){
 | |
|                 if (node instanceof AST_Lambda || node instanceof AST_SimpleStatement) {
 | |
|                     // no need to descend these node types
 | |
|                     return node;
 | |
|                 }
 | |
|                 else if (node instanceof AST_Switch && node === self) {
 | |
|                     node = node.clone();
 | |
|                     descend(node, this);
 | |
|                     return ruined ? node : make_node(AST_BlockStatement, node, {
 | |
|                         body: node.body.reduce(function(a, branch){
 | |
|                             return a.concat(branch.body);
 | |
|                         }, [])
 | |
|                     }).transform(compressor);
 | |
|                 }
 | |
|                 else if (node instanceof AST_If || node instanceof AST_Try) {
 | |
|                     var save = in_if;
 | |
|                     in_if = !in_block;
 | |
|                     descend(node, this);
 | |
|                     in_if = save;
 | |
|                     return node;
 | |
|                 }
 | |
|                 else if (node instanceof AST_StatementWithBody || node instanceof AST_Switch) {
 | |
|                     var save = in_block;
 | |
|                     in_block = true;
 | |
|                     descend(node, this);
 | |
|                     in_block = save;
 | |
|                     return node;
 | |
|                 }
 | |
|                 else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) {
 | |
|                     if (in_if) {
 | |
|                         ruined = true;
 | |
|                         return node;
 | |
|                     }
 | |
|                     if (in_block) return node;
 | |
|                     stopped = true;
 | |
|                     return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
 | |
|                 }
 | |
|                 else if (node instanceof AST_SwitchBranch && this.parent() === self) {
 | |
|                     if (stopped) return MAP.skip;
 | |
|                     if (node instanceof AST_Case) {
 | |
|                         var exp = node.expression.evaluate(compressor);
 | |
|                         if (exp.length < 2) {
 | |
|                             // got a case with non-constant expression, baling out
 | |
|                             throw self;
 | |
|                         }
 | |
|                         if (exp[1] === value || started) {
 | |
|                             started = true;
 | |
|                             if (aborts(node)) stopped = true;
 | |
|                             descend(node, this);
 | |
|                             return node;
 | |
|                         }
 | |
|                         return MAP.skip;
 | |
|                     }
 | |
|                     descend(node, this);
 | |
|                     return node;
 | |
|                 }
 | |
|             });
 | |
|             tt.stack = compressor.stack.slice(); // so that's able to see parent nodes
 | |
|             self = self.transform(tt);
 | |
|         } catch(ex) {
 | |
|             if (ex !== self) throw ex;
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Case, function(self, compressor){
 | |
|         self.body = tighten_body(self.body, compressor);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Try, function(self, compressor){
 | |
|         self.body = tighten_body(self.body, compressor);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     AST_Definitions.DEFMETHOD("remove_initializers", function(){
 | |
|         this.definitions.forEach(function(def){ def.value = null });
 | |
|     });
 | |
| 
 | |
|     AST_Definitions.DEFMETHOD("to_assignments", function(){
 | |
|         var assignments = this.definitions.reduce(function(a, def){
 | |
|             if (def.value) {
 | |
|                 var name = make_node(AST_SymbolRef, def.name, def.name);
 | |
|                 a.push(make_node(AST_Assign, def, {
 | |
|                     operator : "=",
 | |
|                     left     : name,
 | |
|                     right    : def.value
 | |
|                 }));
 | |
|             }
 | |
|             return a;
 | |
|         }, []);
 | |
|         if (assignments.length == 0) return null;
 | |
|         return AST_Seq.from_array(assignments);
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Definitions, function(self, compressor){
 | |
|         if (self.definitions.length == 0)
 | |
|             return make_node(AST_EmptyStatement, self);
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Function, function(self, compressor){
 | |
|         self = AST_Lambda.prototype.optimize.call(self, compressor);
 | |
|         if (compressor.option("unused")) {
 | |
|             if (self.name && self.name.unreferenced()) {
 | |
|                 self.name = null;
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Call, function(self, compressor){
 | |
|         if (compressor.option("unsafe")) {
 | |
|             var exp = self.expression;
 | |
|             if (exp instanceof AST_SymbolRef && exp.undeclared()) {
 | |
|                 switch (exp.name) {
 | |
|                   case "Array":
 | |
|                     if (self.args.length != 1) {
 | |
|                         return make_node(AST_Array, self, {
 | |
|                             elements: self.args
 | |
|                         });
 | |
|                     }
 | |
|                     break;
 | |
|                   case "Object":
 | |
|                     if (self.args.length == 0) {
 | |
|                         return make_node(AST_Object, self, {
 | |
|                             properties: []
 | |
|                         });
 | |
|                     }
 | |
|                     break;
 | |
|                   case "String":
 | |
|                     if (self.args.length == 0) return make_node(AST_String, self, {
 | |
|                         value: ""
 | |
|                     });
 | |
|                     return make_node(AST_Binary, self, {
 | |
|                         left: self.args[0],
 | |
|                         operator: "+",
 | |
|                         right: make_node(AST_String, self, { value: "" })
 | |
|                     });
 | |
|                   case "Function":
 | |
|                     if (all(self.args, function(x){ return x instanceof AST_String })) {
 | |
|                         // quite a corner-case, but we can handle it:
 | |
|                         //   https://github.com/mishoo/UglifyJS2/issues/203
 | |
|                         // if the code argument is a constant, then we can minify it.
 | |
|                         try {
 | |
|                             var code = "(function(" + self.args.slice(0, -1).map(function(arg){
 | |
|                                 return arg.value;
 | |
|                             }).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
 | |
|                             var ast = parse(code);
 | |
|                             ast.figure_out_scope();
 | |
|                             var comp = new Compressor(compressor.options);
 | |
|                             ast = ast.transform(comp);
 | |
|                             ast.figure_out_scope();
 | |
|                             ast.mangle_names();
 | |
|                             var fun = ast.body[0].body.expression;
 | |
|                             var args = fun.argnames.map(function(arg, i){
 | |
|                                 return make_node(AST_String, self.args[i], {
 | |
|                                     value: arg.print_to_string()
 | |
|                                 });
 | |
|                             });
 | |
|                             var code = OutputStream();
 | |
|                             AST_BlockStatement.prototype._codegen.call(fun, fun, code);
 | |
|                             code = code.toString().replace(/^\{|\}$/g, "");
 | |
|                             args.push(make_node(AST_String, self.args[self.args.length - 1], {
 | |
|                                 value: code
 | |
|                             }));
 | |
|                             self.args = args;
 | |
|                             return self;
 | |
|                         } catch(ex) {
 | |
|                             if (ex instanceof JS_Parse_Error) {
 | |
|                                 compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
 | |
|                                 compressor.warn(ex.toString());
 | |
|                             } else {
 | |
|                                 console.log(ex);
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
 | |
|                 return make_node(AST_Binary, self, {
 | |
|                     left: make_node(AST_String, self, { value: "" }),
 | |
|                     operator: "+",
 | |
|                     right: exp.expression
 | |
|                 }).transform(compressor);
 | |
|             }
 | |
|         }
 | |
|         if (compressor.option("side_effects")) {
 | |
|             if (self.expression instanceof AST_Function
 | |
|                 && self.args.length == 0
 | |
|                 && !AST_Block.prototype.has_side_effects.call(self.expression)) {
 | |
|                 return make_node(AST_Undefined, self).transform(compressor);
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_New, function(self, compressor){
 | |
|         if (compressor.option("unsafe")) {
 | |
|             var exp = self.expression;
 | |
|             if (exp instanceof AST_SymbolRef && exp.undeclared()) {
 | |
|                 switch (exp.name) {
 | |
|                   case "Object":
 | |
|                   case "RegExp":
 | |
|                   case "Function":
 | |
|                   case "Error":
 | |
|                   case "Array":
 | |
|                     return make_node(AST_Call, self, self).transform(compressor);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Seq, function(self, compressor){
 | |
|         if (!compressor.option("side_effects"))
 | |
|             return self;
 | |
|         if (!self.car.has_side_effects()) {
 | |
|             // we shouldn't compress (1,eval)(something) to
 | |
|             // eval(something) because that changes the meaning of
 | |
|             // eval (becomes lexical instead of global).
 | |
|             var p;
 | |
|             if (!(self.cdr instanceof AST_SymbolRef
 | |
|                   && self.cdr.name == "eval"
 | |
|                   && self.cdr.undeclared()
 | |
|                   && (p = compressor.parent()) instanceof AST_Call
 | |
|                   && p.expression === self)) {
 | |
|                 return self.cdr;
 | |
|             }
 | |
|         }
 | |
|         if (compressor.option("cascade")) {
 | |
|             if (self.car instanceof AST_Assign
 | |
|                 && !self.car.left.has_side_effects()
 | |
|                 && self.car.left.equivalent_to(self.cdr)) {
 | |
|                 return self.car;
 | |
|             }
 | |
|             if (!self.car.has_side_effects()
 | |
|                 && !self.cdr.has_side_effects()
 | |
|                 && self.car.equivalent_to(self.cdr)) {
 | |
|                 return self.car;
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
 | |
|         if (compressor.option("sequences")) {
 | |
|             if (this.expression instanceof AST_Seq) {
 | |
|                 var seq = this.expression;
 | |
|                 var x = seq.to_array();
 | |
|                 this.expression = x.pop();
 | |
|                 x.push(this);
 | |
|                 seq = AST_Seq.from_array(x).transform(compressor);
 | |
|                 return seq;
 | |
|             }
 | |
|         }
 | |
|         return this;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_UnaryPostfix, function(self, compressor){
 | |
|         return self.lift_sequences(compressor);
 | |
|     });
 | |
| 
 | |
|     OPT(AST_UnaryPrefix, function(self, compressor){
 | |
|         self = self.lift_sequences(compressor);
 | |
|         var e = self.expression;
 | |
|         if (compressor.option("booleans") && compressor.in_boolean_context()) {
 | |
|             switch (self.operator) {
 | |
|               case "!":
 | |
|                 if (e instanceof AST_UnaryPrefix && e.operator == "!") {
 | |
|                     // !!foo ==> foo, if we're in boolean context
 | |
|                     return e.expression;
 | |
|                 }
 | |
|                 break;
 | |
|               case "typeof":
 | |
|                 // typeof always returns a non-empty string, thus it's
 | |
|                 // always true in booleans
 | |
|                 compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
 | |
|                 return make_node(AST_True, self);
 | |
|             }
 | |
|             if (e instanceof AST_Binary && self.operator == "!") {
 | |
|                 self = best_of(self, e.negate(compressor));
 | |
|             }
 | |
|         }
 | |
|         return self.evaluate(compressor)[0];
 | |
|     });
 | |
| 
 | |
|     AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
 | |
|         if (compressor.option("sequences")) {
 | |
|             if (this.left instanceof AST_Seq) {
 | |
|                 var seq = this.left;
 | |
|                 var x = seq.to_array();
 | |
|                 this.left = x.pop();
 | |
|                 x.push(this);
 | |
|                 seq = AST_Seq.from_array(x).transform(compressor);
 | |
|                 return seq;
 | |
|             }
 | |
|             if (this.right instanceof AST_Seq
 | |
|                 && !(this.operator == "||" || this.operator == "&&")
 | |
|                 && !this.left.has_side_effects()) {
 | |
|                 var seq = this.right;
 | |
|                 var x = seq.to_array();
 | |
|                 this.right = x.pop();
 | |
|                 x.push(this);
 | |
|                 seq = AST_Seq.from_array(x).transform(compressor);
 | |
|                 return seq;
 | |
|             }
 | |
|         }
 | |
|         return this;
 | |
|     });
 | |
| 
 | |
|     var commutativeOperators = makePredicate("== === != !== * & | ^");
 | |
| 
 | |
|     OPT(AST_Binary, function(self, compressor){
 | |
|         var reverse = compressor.has_directive("use asm") ? noop
 | |
|             : function(op, force) {
 | |
|                 if (force || !(self.left.has_side_effects() || self.right.has_side_effects())) {
 | |
|                     if (op) self.operator = op;
 | |
|                     var tmp = self.left;
 | |
|                     self.left = self.right;
 | |
|                     self.right = tmp;
 | |
|                 }
 | |
|             };
 | |
|         if (commutativeOperators(self.operator)) {
 | |
|             if (self.right instanceof AST_Constant
 | |
|                 && !(self.left instanceof AST_Constant)) {
 | |
|                 // if right is a constant, whatever side effects the
 | |
|                 // left side might have could not influence the
 | |
|                 // result.  hence, force switch.
 | |
|                 reverse(null, true);
 | |
|             }
 | |
|         }
 | |
|         self = self.lift_sequences(compressor);
 | |
|         if (compressor.option("comparisons")) switch (self.operator) {
 | |
|           case "===":
 | |
|           case "!==":
 | |
|             if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
 | |
|                 (self.left.is_boolean() && self.right.is_boolean())) {
 | |
|                 self.operator = self.operator.substr(0, 2);
 | |
|             }
 | |
|             // XXX: intentionally falling down to the next case
 | |
|           case "==":
 | |
|           case "!=":
 | |
|             if (self.left instanceof AST_String
 | |
|                 && self.left.value == "undefined"
 | |
|                 && self.right instanceof AST_UnaryPrefix
 | |
|                 && self.right.operator == "typeof"
 | |
|                 && compressor.option("unsafe")) {
 | |
|                 if (!(self.right.expression instanceof AST_SymbolRef)
 | |
|                     || !self.right.expression.undeclared()) {
 | |
|                     self.right = self.right.expression;
 | |
|                     self.left = make_node(AST_Undefined, self.left).optimize(compressor);
 | |
|                     if (self.operator.length == 2) self.operator += "=";
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
 | |
|           case "&&":
 | |
|             var ll = self.left.evaluate(compressor);
 | |
|             var rr = self.right.evaluate(compressor);
 | |
|             if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {
 | |
|                 compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
 | |
|                 return make_node(AST_False, self);
 | |
|             }
 | |
|             if (ll.length > 1 && ll[1]) {
 | |
|                 return rr[0];
 | |
|             }
 | |
|             if (rr.length > 1 && rr[1]) {
 | |
|                 return ll[0];
 | |
|             }
 | |
|             break;
 | |
|           case "||":
 | |
|             var ll = self.left.evaluate(compressor);
 | |
|             var rr = self.right.evaluate(compressor);
 | |
|             if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {
 | |
|                 compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
 | |
|                 return make_node(AST_True, self);
 | |
|             }
 | |
|             if (ll.length > 1 && !ll[1]) {
 | |
|                 return rr[0];
 | |
|             }
 | |
|             if (rr.length > 1 && !rr[1]) {
 | |
|                 return ll[0];
 | |
|             }
 | |
|             break;
 | |
|           case "+":
 | |
|             var ll = self.left.evaluate(compressor);
 | |
|             var rr = self.right.evaluate(compressor);
 | |
|             if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1]) ||
 | |
|                 (rr.length > 1 && rr[0] instanceof AST_String && rr[1])) {
 | |
|                 compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
 | |
|                 return make_node(AST_True, self);
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         var exp = self.evaluate(compressor);
 | |
|         if (exp.length > 1) {
 | |
|             if (best_of(exp[0], self) !== self)
 | |
|                 return exp[0];
 | |
|         }
 | |
|         if (compressor.option("comparisons")) {
 | |
|             if (!(compressor.parent() instanceof AST_Binary)
 | |
|                 || compressor.parent() instanceof AST_Assign) {
 | |
|                 var negated = make_node(AST_UnaryPrefix, self, {
 | |
|                     operator: "!",
 | |
|                     expression: self.negate(compressor)
 | |
|                 });
 | |
|                 self = best_of(self, negated);
 | |
|             }
 | |
|             switch (self.operator) {
 | |
|               case "<": reverse(">"); break;
 | |
|               case "<=": reverse(">="); break;
 | |
|             }
 | |
|         }
 | |
|         if (self.operator == "+" && self.right instanceof AST_String
 | |
|             && self.right.getValue() === "" && self.left instanceof AST_Binary
 | |
|             && self.left.operator == "+" && self.left.is_string(compressor)) {
 | |
|             return self.left;
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_SymbolRef, function(self, compressor){
 | |
|         if (self.undeclared()) {
 | |
|             var defines = compressor.option("global_defs");
 | |
|             if (defines && defines.hasOwnProperty(self.name)) {
 | |
|                 return make_node_from_constant(compressor, defines[self.name], self);
 | |
|             }
 | |
|             switch (self.name) {
 | |
|               case "undefined":
 | |
|                 return make_node(AST_Undefined, self);
 | |
|               case "NaN":
 | |
|                 return make_node(AST_NaN, self);
 | |
|               case "Infinity":
 | |
|                 return make_node(AST_Infinity, self);
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Undefined, function(self, compressor){
 | |
|         if (compressor.option("unsafe")) {
 | |
|             var scope = compressor.find_parent(AST_Scope);
 | |
|             var undef = scope.find_variable("undefined");
 | |
|             if (undef) {
 | |
|                 var ref = make_node(AST_SymbolRef, self, {
 | |
|                     name   : "undefined",
 | |
|                     scope  : scope,
 | |
|                     thedef : undef
 | |
|                 });
 | |
|                 ref.reference();
 | |
|                 return ref;
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
 | |
|     OPT(AST_Assign, function(self, compressor){
 | |
|         self = self.lift_sequences(compressor);
 | |
|         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)) {
 | |
|             self.operator = self.right.operator + "=";
 | |
|             self.right = self.right.right;
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Conditional, function(self, compressor){
 | |
|         if (!compressor.option("conditionals")) return self;
 | |
|         if (self.condition instanceof AST_Seq) {
 | |
|             var car = self.condition.car;
 | |
|             self.condition = self.condition.cdr;
 | |
|             return AST_Seq.cons(car, self);
 | |
|         }
 | |
|         var cond = self.condition.evaluate(compressor);
 | |
|         if (cond.length > 1) {
 | |
|             if (cond[1]) {
 | |
|                 compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
 | |
|                 return self.consequent;
 | |
|             } else {
 | |
|                 compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
 | |
|                 return self.alternative;
 | |
|             }
 | |
|         }
 | |
|         var negated = cond[0].negate(compressor);
 | |
|         if (best_of(cond[0], negated) === negated) {
 | |
|             self = make_node(AST_Conditional, self, {
 | |
|                 condition: negated,
 | |
|                 consequent: self.alternative,
 | |
|                 alternative: self.consequent
 | |
|             });
 | |
|         }
 | |
|         var consequent = self.consequent;
 | |
|         var alternative = self.alternative;
 | |
|         if (consequent instanceof AST_Assign
 | |
|             && alternative instanceof AST_Assign
 | |
|             && consequent.operator == alternative.operator
 | |
|             && consequent.left.equivalent_to(alternative.left)
 | |
|            ) {
 | |
|             /*
 | |
|              * Stuff like this:
 | |
|              * if (foo) exp = something; else exp = something_else;
 | |
|              * ==>
 | |
|              * exp = foo ? something : something_else;
 | |
|              */
 | |
|             self = make_node(AST_Assign, self, {
 | |
|                 operator: consequent.operator,
 | |
|                 left: consequent.left,
 | |
|                 right: make_node(AST_Conditional, self, {
 | |
|                     condition: self.condition,
 | |
|                     consequent: consequent.right,
 | |
|                     alternative: alternative.right
 | |
|                 })
 | |
|             });
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Boolean, function(self, compressor){
 | |
|         if (compressor.option("booleans")) {
 | |
|             var p = compressor.parent();
 | |
|             if (p instanceof AST_Binary && (p.operator == "=="
 | |
|                                             || p.operator == "!=")) {
 | |
|                 compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
 | |
|                     operator : p.operator,
 | |
|                     value    : self.value,
 | |
|                     file     : p.start.file,
 | |
|                     line     : p.start.line,
 | |
|                     col      : p.start.col,
 | |
|                 });
 | |
|                 return make_node(AST_Number, self, {
 | |
|                     value: +self.value
 | |
|                 });
 | |
|             }
 | |
|             return make_node(AST_UnaryPrefix, self, {
 | |
|                 operator: "!",
 | |
|                 expression: make_node(AST_Number, self, {
 | |
|                     value: 1 - self.value
 | |
|                 })
 | |
|             });
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     OPT(AST_Sub, function(self, compressor){
 | |
|         var prop = self.property;
 | |
|         if (prop instanceof AST_String && compressor.option("properties")) {
 | |
|             prop = prop.getValue();
 | |
|             if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) {
 | |
|                 return make_node(AST_Dot, self, {
 | |
|                     expression : self.expression,
 | |
|                     property   : prop
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|         return self;
 | |
|     });
 | |
| 
 | |
|     function literals_in_boolean_context(self, compressor) {
 | |
|         if (compressor.option("booleans") && compressor.in_boolean_context()) {
 | |
|             return make_node(AST_True, self);
 | |
|         }
 | |
|         return self;
 | |
|     };
 | |
|     OPT(AST_Array, literals_in_boolean_context);
 | |
|     OPT(AST_Object, literals_in_boolean_context);
 | |
|     OPT(AST_RegExp, literals_in_boolean_context);
 | |
| 
 | |
| })();
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| // a small wrapper around fitzgen's source-map library
 | |
| function SourceMap(options) {
 | |
|     options = defaults(options, {
 | |
|         file : null,
 | |
|         root : null,
 | |
|         orig : null,
 | |
|     });
 | |
|     var generator = new MOZ_SourceMap.SourceMapGenerator({
 | |
|         file       : options.file,
 | |
|         sourceRoot : options.root
 | |
|     });
 | |
|     var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig);
 | |
|     function add(source, gen_line, gen_col, orig_line, orig_col, name) {
 | |
|         if (orig_map) {
 | |
|             var info = orig_map.originalPositionFor({
 | |
|                 line: orig_line,
 | |
|                 column: orig_col
 | |
|             });
 | |
|             source = info.source;
 | |
|             orig_line = info.line;
 | |
|             orig_col = info.column;
 | |
|             name = info.name;
 | |
|         }
 | |
|         generator.addMapping({
 | |
|             generated : { line: gen_line, column: gen_col },
 | |
|             original  : { line: orig_line, column: orig_col },
 | |
|             source    : source,
 | |
|             name      : name
 | |
|         });
 | |
|     };
 | |
|     return {
 | |
|         add        : add,
 | |
|         get        : function() { return generator },
 | |
|         toString   : function() { return generator.toString() }
 | |
|     };
 | |
| };
 | |
| 
 | |
| /***********************************************************************
 | |
| 
 | |
|   A JavaScript tokenizer / parser / beautifier / compressor.
 | |
|   https://github.com/mishoo/UglifyJS2
 | |
| 
 | |
|   -------------------------------- (C) ---------------------------------
 | |
| 
 | |
|                            Author: Mihai Bazon
 | |
|                          <mihai.bazon@gmail.com>
 | |
|                        http://mihai.bazon.net/blog
 | |
| 
 | |
|   Distributed under the BSD license:
 | |
| 
 | |
|     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
| 
 | |
|         * Redistributions of source code must retain the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer.
 | |
| 
 | |
|         * Redistributions in binary form must reproduce the above
 | |
|           copyright notice, this list of conditions and the following
 | |
|           disclaimer in the documentation and/or other materials
 | |
|           provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 | |
|     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 | |
|     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
|     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 | |
|     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|     SUCH DAMAGE.
 | |
| 
 | |
|  ***********************************************************************/
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| (function(){
 | |
| 
 | |
|     var MOZ_TO_ME = {
 | |
|         TryStatement : function(M) {
 | |
|             return new AST_Try({
 | |
|                 start    : my_start_token(M),
 | |
|                 end      : my_end_token(M),
 | |
|                 body     : from_moz(M.block).body,
 | |
|                 bcatch   : from_moz(M.handlers[0]),
 | |
|                 bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
 | |
|             });
 | |
|         },
 | |
|         CatchClause : function(M) {
 | |
|             return new AST_Catch({
 | |
|                 start   : my_start_token(M),
 | |
|                 end     : my_end_token(M),
 | |
|                 argname : from_moz(M.param),
 | |
|                 body    : from_moz(M.body).body
 | |
|             });
 | |
|         },
 | |
|         ObjectExpression : function(M) {
 | |
|             return new AST_Object({
 | |
|                 start      : my_start_token(M),
 | |
|                 end        : my_end_token(M),
 | |
|                 properties : M.properties.map(function(prop){
 | |
|                     var key = prop.key;
 | |
|                     var name = key.type == "Identifier" ? key.name : key.value;
 | |
|                     var args = {
 | |
|                         start    : my_start_token(key),
 | |
|                         end      : my_end_token(prop.value),
 | |
|                         key      : name,
 | |
|                         value    : from_moz(prop.value)
 | |
|                     };
 | |
|                     switch (prop.kind) {
 | |
|                       case "init":
 | |
|                         return new AST_ObjectKeyVal(args);
 | |
|                       case "set":
 | |
|                         args.value.name = from_moz(key);
 | |
|                         return new AST_ObjectSetter(args);
 | |
|                       case "get":
 | |
|                         args.value.name = from_moz(key);
 | |
|                         return new AST_ObjectGetter(args);
 | |
|                     }
 | |
|                 })
 | |
|             });
 | |
|         },
 | |
|         SequenceExpression : function(M) {
 | |
|             return AST_Seq.from_array(M.expressions.map(from_moz));
 | |
|         },
 | |
|         MemberExpression : function(M) {
 | |
|             return new (M.computed ? AST_Sub : AST_Dot)({
 | |
|                 start      : my_start_token(M),
 | |
|                 end        : my_end_token(M),
 | |
|                 property   : M.computed ? from_moz(M.property) : M.property.name,
 | |
|                 expression : from_moz(M.object)
 | |
|             });
 | |
|         },
 | |
|         SwitchCase : function(M) {
 | |
|             return new (M.test ? AST_Case : AST_Default)({
 | |
|                 start      : my_start_token(M),
 | |
|                 end        : my_end_token(M),
 | |
|                 expression : from_moz(M.test),
 | |
|                 body       : M.consequent.map(from_moz)
 | |
|             });
 | |
|         },
 | |
|         Literal : function(M) {
 | |
|             var val = M.value, args = {
 | |
|                 start  : my_start_token(M),
 | |
|                 end    : my_end_token(M)
 | |
|             };
 | |
|             if (val === null) return new AST_Null(args);
 | |
|             switch (typeof val) {
 | |
|               case "string":
 | |
|                 args.value = val;
 | |
|                 return new AST_String(args);
 | |
|               case "number":
 | |
|                 args.value = val;
 | |
|                 return new AST_Number(args);
 | |
|               case "boolean":
 | |
|                 return new (val ? AST_True : AST_False)(args);
 | |
|               default:
 | |
|                 args.value = val;
 | |
|                 return new AST_RegExp(args);
 | |
|             }
 | |
|         },
 | |
|         UnaryExpression: From_Moz_Unary,
 | |
|         UpdateExpression: From_Moz_Unary,
 | |
|         Identifier: function(M) {
 | |
|             var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
 | |
|             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)({
 | |
|                             start : my_start_token(M),
 | |
|                             end   : my_end_token(M),
 | |
|                             name  : M.name
 | |
|                         });
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function From_Moz_Unary(M) {
 | |
|         var prefix = "prefix" in M ? M.prefix
 | |
|             : M.type == "UnaryExpression" ? true : false;
 | |
|         return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
 | |
|             start      : my_start_token(M),
 | |
|             end        : my_end_token(M),
 | |
|             operator   : M.operator,
 | |
|             expression : from_moz(M.argument)
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     var ME_TO_MOZ = {};
 | |
| 
 | |
|     map("Node", AST_Node);
 | |
|     map("Program", AST_Toplevel, "body@body");
 | |
|     map("Function", AST_Function, "id>name, params@argnames, body%body");
 | |
|     map("EmptyStatement", AST_EmptyStatement);
 | |
|     map("BlockStatement", AST_BlockStatement, "body@body");
 | |
|     map("ExpressionStatement", AST_SimpleStatement, "expression>body");
 | |
|     map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
 | |
|     map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
 | |
|     map("BreakStatement", AST_Break, "label>label");
 | |
|     map("ContinueStatement", AST_Continue, "label>label");
 | |
|     map("WithStatement", AST_With, "object>expression, body>body");
 | |
|     map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body");
 | |
|     map("ReturnStatement", AST_Return, "argument>value");
 | |
|     map("ThrowStatement", AST_Throw, "argument>value");
 | |
|     map("WhileStatement", AST_While, "test>condition, body>body");
 | |
|     map("DoWhileStatement", AST_Do, "test>condition, body>body");
 | |
|     map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body");
 | |
|     map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
 | |
|     map("DebuggerStatement", AST_Debugger);
 | |
|     map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body");
 | |
|     map("VariableDeclaration", AST_Var, "declarations@definitions");
 | |
|     map("VariableDeclarator", AST_VarDef, "id>name, init>value");
 | |
| 
 | |
|     map("ThisExpression", AST_This);
 | |
|     map("ArrayExpression", AST_Array, "elements@elements");
 | |
|     map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body");
 | |
|     map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
 | |
|     map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
 | |
|     map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
 | |
|     map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
 | |
|     map("NewExpression", AST_New, "callee>expression, arguments@args");
 | |
|     map("CallExpression", AST_Call, "callee>expression, arguments@args");
 | |
| 
 | |
|     /* -----[ tools ]----- */
 | |
| 
 | |
|     function my_start_token(moznode) {
 | |
|         return new AST_Token({
 | |
|             file   : moznode.loc && moznode.loc.source,
 | |
|             line   : moznode.loc && moznode.loc.start.line,
 | |
|             col    : moznode.loc && moznode.loc.start.column,
 | |
|             pos    : moznode.start,
 | |
|             endpos : moznode.start
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function my_end_token(moznode) {
 | |
|         return new AST_Token({
 | |
|             file   : moznode.loc && moznode.loc.source,
 | |
|             line   : moznode.loc && moznode.loc.end.line,
 | |
|             col    : moznode.loc && moznode.loc.end.column,
 | |
|             pos    : moznode.end,
 | |
|             endpos : moznode.end
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     function map(moztype, mytype, propmap) {
 | |
|         var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
 | |
|         moz_to_me += "return new mytype({\n" +
 | |
|             "start: my_start_token(M),\n" +
 | |
|             "end: my_end_token(M)";
 | |
| 
 | |
|         if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){
 | |
|             var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
 | |
|             if (!m) throw new Error("Can't understand property map: " + prop);
 | |
|             var moz = "M." + m[1], how = m[2], my = m[3];
 | |
|             moz_to_me += ",\n" + my + ": ";
 | |
|             if (how == "@") {
 | |
|                 moz_to_me += moz + ".map(from_moz)";
 | |
|             } else if (how == ">") {
 | |
|                 moz_to_me += "from_moz(" + moz + ")";
 | |
|             } else if (how == "=") {
 | |
|                 moz_to_me += moz;
 | |
|             } else if (how == "%") {
 | |
|                 moz_to_me += "from_moz(" + moz + ").body";
 | |
|             } else throw new Error("Can't understand operator in propmap: " + prop);
 | |
|         });
 | |
|         moz_to_me += "\n})}";
 | |
| 
 | |
|         // moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
 | |
|         // console.log(moz_to_me);
 | |
| 
 | |
|         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
 | |
|         );
 | |
|         return MOZ_TO_ME[moztype] = moz_to_me;
 | |
|     };
 | |
| 
 | |
|     var FROM_MOZ_STACK = null;
 | |
| 
 | |
|     function from_moz(node) {
 | |
|         FROM_MOZ_STACK.push(node);
 | |
|         var ret = node != null ? MOZ_TO_ME[node.type](node) : null;
 | |
|         FROM_MOZ_STACK.pop();
 | |
|         return ret;
 | |
|     };
 | |
| 
 | |
|     AST_Node.from_mozilla_ast = function(node){
 | |
|         var save_stack = FROM_MOZ_STACK;
 | |
|         FROM_MOZ_STACK = [];
 | |
|         var ast = from_moz(node);
 | |
|         FROM_MOZ_STACK = save_stack;
 | |
|         return ast;
 | |
|     };
 | |
| 
 | |
| })();
 | |
| 
 | |
| 
 | |
| exports.sys = sys;
 | |
| exports.MOZ_SourceMap = MOZ_SourceMap;
 | |
| exports.UglifyJS = UglifyJS;
 | |
| exports.array_to_hash = array_to_hash;
 | |
| exports.slice = slice;
 | |
| exports.characters = characters;
 | |
| exports.member = member;
 | |
| exports.find_if = find_if;
 | |
| exports.repeat_string = repeat_string;
 | |
| exports.DefaultsError = DefaultsError;
 | |
| exports.defaults = defaults;
 | |
| exports.merge = merge;
 | |
| exports.noop = noop;
 | |
| exports.MAP = MAP;
 | |
| exports.push_uniq = push_uniq;
 | |
| exports.string_template = string_template;
 | |
| exports.remove = remove;
 | |
| exports.mergeSort = mergeSort;
 | |
| exports.set_difference = set_difference;
 | |
| exports.set_intersection = set_intersection;
 | |
| exports.makePredicate = makePredicate;
 | |
| exports.all = all;
 | |
| exports.Dictionary = Dictionary;
 | |
| exports.DEFNODE = DEFNODE;
 | |
| exports.AST_Token = AST_Token;
 | |
| exports.AST_Node = AST_Node;
 | |
| exports.AST_Statement = AST_Statement;
 | |
| exports.AST_Debugger = AST_Debugger;
 | |
| exports.AST_Directive = AST_Directive;
 | |
| exports.AST_SimpleStatement = AST_SimpleStatement;
 | |
| exports.walk_body = walk_body;
 | |
| exports.AST_Block = AST_Block;
 | |
| exports.AST_BlockStatement = AST_BlockStatement;
 | |
| exports.AST_EmptyStatement = AST_EmptyStatement;
 | |
| exports.AST_StatementWithBody = AST_StatementWithBody;
 | |
| exports.AST_LabeledStatement = AST_LabeledStatement;
 | |
| exports.AST_DWLoop = AST_DWLoop;
 | |
| exports.AST_Do = AST_Do;
 | |
| exports.AST_While = AST_While;
 | |
| exports.AST_For = AST_For;
 | |
| exports.AST_ForIn = AST_ForIn;
 | |
| exports.AST_With = AST_With;
 | |
| exports.AST_Scope = AST_Scope;
 | |
| exports.AST_Toplevel = AST_Toplevel;
 | |
| exports.AST_Lambda = AST_Lambda;
 | |
| exports.AST_Accessor = AST_Accessor;
 | |
| exports.AST_Function = AST_Function;
 | |
| exports.AST_Defun = AST_Defun;
 | |
| exports.AST_Jump = AST_Jump;
 | |
| exports.AST_Exit = AST_Exit;
 | |
| exports.AST_Return = AST_Return;
 | |
| exports.AST_Throw = AST_Throw;
 | |
| exports.AST_LoopControl = AST_LoopControl;
 | |
| exports.AST_Break = AST_Break;
 | |
| exports.AST_Continue = AST_Continue;
 | |
| exports.AST_If = AST_If;
 | |
| exports.AST_Switch = AST_Switch;
 | |
| exports.AST_SwitchBranch = AST_SwitchBranch;
 | |
| exports.AST_Default = AST_Default;
 | |
| exports.AST_Case = AST_Case;
 | |
| exports.AST_Try = AST_Try;
 | |
| exports.AST_Catch = AST_Catch;
 | |
| exports.AST_Finally = AST_Finally;
 | |
| exports.AST_Definitions = AST_Definitions;
 | |
| exports.AST_Var = AST_Var;
 | |
| exports.AST_Const = AST_Const;
 | |
| exports.AST_VarDef = AST_VarDef;
 | |
| exports.AST_Call = AST_Call;
 | |
| exports.AST_New = AST_New;
 | |
| exports.AST_Seq = AST_Seq;
 | |
| exports.AST_PropAccess = AST_PropAccess;
 | |
| exports.AST_Dot = AST_Dot;
 | |
| exports.AST_Sub = AST_Sub;
 | |
| exports.AST_Unary = AST_Unary;
 | |
| exports.AST_UnaryPrefix = AST_UnaryPrefix;
 | |
| exports.AST_UnaryPostfix = AST_UnaryPostfix;
 | |
| exports.AST_Binary = AST_Binary;
 | |
| exports.AST_Conditional = AST_Conditional;
 | |
| exports.AST_Assign = AST_Assign;
 | |
| exports.AST_Array = AST_Array;
 | |
| exports.AST_Object = AST_Object;
 | |
| exports.AST_ObjectProperty = AST_ObjectProperty;
 | |
| exports.AST_ObjectKeyVal = AST_ObjectKeyVal;
 | |
| exports.AST_ObjectSetter = AST_ObjectSetter;
 | |
| exports.AST_ObjectGetter = AST_ObjectGetter;
 | |
| exports.AST_Symbol = AST_Symbol;
 | |
| exports.AST_SymbolAccessor = AST_SymbolAccessor;
 | |
| exports.AST_SymbolDeclaration = AST_SymbolDeclaration;
 | |
| exports.AST_SymbolVar = AST_SymbolVar;
 | |
| exports.AST_SymbolConst = AST_SymbolConst;
 | |
| exports.AST_SymbolFunarg = AST_SymbolFunarg;
 | |
| exports.AST_SymbolDefun = AST_SymbolDefun;
 | |
| exports.AST_SymbolLambda = AST_SymbolLambda;
 | |
| exports.AST_SymbolCatch = AST_SymbolCatch;
 | |
| exports.AST_Label = AST_Label;
 | |
| exports.AST_SymbolRef = AST_SymbolRef;
 | |
| exports.AST_LabelRef = AST_LabelRef;
 | |
| exports.AST_This = AST_This;
 | |
| exports.AST_Constant = AST_Constant;
 | |
| exports.AST_String = AST_String;
 | |
| exports.AST_Number = AST_Number;
 | |
| exports.AST_RegExp = AST_RegExp;
 | |
| exports.AST_Atom = AST_Atom;
 | |
| exports.AST_Null = AST_Null;
 | |
| exports.AST_NaN = AST_NaN;
 | |
| exports.AST_Undefined = AST_Undefined;
 | |
| exports.AST_Hole = AST_Hole;
 | |
| exports.AST_Infinity = AST_Infinity;
 | |
| exports.AST_Boolean = AST_Boolean;
 | |
| exports.AST_False = AST_False;
 | |
| exports.AST_True = AST_True;
 | |
| exports.TreeWalker = TreeWalker;
 | |
| exports.KEYWORDS = KEYWORDS;
 | |
| exports.KEYWORDS_ATOM = KEYWORDS_ATOM;
 | |
| exports.RESERVED_WORDS = RESERVED_WORDS;
 | |
| exports.KEYWORDS_BEFORE_EXPRESSION = KEYWORDS_BEFORE_EXPRESSION;
 | |
| exports.OPERATOR_CHARS = OPERATOR_CHARS;
 | |
| exports.RE_HEX_NUMBER = RE_HEX_NUMBER;
 | |
| exports.RE_OCT_NUMBER = RE_OCT_NUMBER;
 | |
| exports.RE_DEC_NUMBER = RE_DEC_NUMBER;
 | |
| exports.OPERATORS = OPERATORS;
 | |
| exports.WHITESPACE_CHARS = WHITESPACE_CHARS;
 | |
| exports.PUNC_BEFORE_EXPRESSION = PUNC_BEFORE_EXPRESSION;
 | |
| exports.PUNC_CHARS = PUNC_CHARS;
 | |
| exports.REGEXP_MODIFIERS = REGEXP_MODIFIERS;
 | |
| exports.UNICODE = UNICODE;
 | |
| exports.is_letter = is_letter;
 | |
| exports.is_digit = is_digit;
 | |
| exports.is_alphanumeric_char = is_alphanumeric_char;
 | |
| exports.is_unicode_combining_mark = is_unicode_combining_mark;
 | |
| exports.is_unicode_connector_punctuation = is_unicode_connector_punctuation;
 | |
| exports.is_identifier = is_identifier;
 | |
| exports.is_identifier_start = is_identifier_start;
 | |
| exports.is_identifier_char = is_identifier_char;
 | |
| exports.is_identifier_string = is_identifier_string;
 | |
| exports.parse_js_number = parse_js_number;
 | |
| exports.JS_Parse_Error = JS_Parse_Error;
 | |
| exports.js_error = js_error;
 | |
| exports.is_token = is_token;
 | |
| exports.EX_EOF = EX_EOF;
 | |
| exports.tokenizer = tokenizer;
 | |
| exports.UNARY_PREFIX = UNARY_PREFIX;
 | |
| exports.UNARY_POSTFIX = UNARY_POSTFIX;
 | |
| exports.ASSIGNMENT = ASSIGNMENT;
 | |
| exports.PRECEDENCE = PRECEDENCE;
 | |
| exports.STATEMENTS_WITH_LABELS = STATEMENTS_WITH_LABELS;
 | |
| exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;
 | |
| exports.parse = parse;
 | |
| exports.TreeTransformer = TreeTransformer;
 | |
| exports.SymbolDef = SymbolDef;
 | |
| exports.base54 = base54;
 | |
| exports.OutputStream = OutputStream;
 | |
| exports.Compressor = Compressor;
 | |
| exports.SourceMap = SourceMap;
 | |
| 
 | |
| exports.AST_Node.warn_function = function (txt) { if (typeof console != "undefined" && typeof console.warn === "function") console.warn(txt) }
 | |
| 
 | |
| exports.minify = function (files, options) {
 | |
|     options = UglifyJS.defaults(options, {
 | |
|         outSourceMap : null,
 | |
|         sourceRoot   : null,
 | |
|         inSourceMap  : null,
 | |
|         fromString   : false,
 | |
|         warnings     : false,
 | |
|         mangle       : {},
 | |
|         output       : null,
 | |
|         compress     : {}
 | |
|     });
 | |
|     if (typeof files == "string")
 | |
|         files = [ files ];
 | |
| 
 | |
|     UglifyJS.base54.reset();
 | |
| 
 | |
|     // 1. parse
 | |
|     var toplevel = null;
 | |
|     files.forEach(function(file){
 | |
|         var code = options.fromString
 | |
|             ? file
 | |
|             : fs.readFileSync(file, "utf8");
 | |
|         toplevel = UglifyJS.parse(code, {
 | |
|             filename: options.fromString ? "?" : file,
 | |
|             toplevel: toplevel
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     // 2. compress
 | |
|     if (options.compress) {
 | |
|         var compress = { warnings: options.warnings };
 | |
|         UglifyJS.merge(compress, options.compress);
 | |
|         toplevel.figure_out_scope();
 | |
|         var sq = UglifyJS.Compressor(compress);
 | |
|         toplevel = toplevel.transform(sq);
 | |
|     }
 | |
| 
 | |
|     // 3. mangle
 | |
|     if (options.mangle) {
 | |
|         toplevel.figure_out_scope();
 | |
|         toplevel.compute_char_frequency();
 | |
|         toplevel.mangle_names(options.mangle);
 | |
|     }
 | |
| 
 | |
|     // 4. output
 | |
|     var inMap = options.inSourceMap;
 | |
|     var output = {};
 | |
|     if (typeof options.inSourceMap == "string") {
 | |
|         inMap = fs.readFileSync(options.inSourceMap, "utf8");
 | |
|     }
 | |
|     if (options.outSourceMap) {
 | |
|         output.source_map = UglifyJS.SourceMap({
 | |
|             file: options.outSourceMap,
 | |
|             orig: inMap,
 | |
|             root: options.sourceRoot
 | |
|         });
 | |
|     }
 | |
|     if (options.output) {
 | |
|         UglifyJS.merge(output, options.output);
 | |
|     }
 | |
|     var stream = UglifyJS.OutputStream(output);
 | |
|     toplevel.print(stream);
 | |
|     return {
 | |
|         code : stream + "",
 | |
|         map  : output.source_map + ""
 | |
|     };
 | |
| };
 | |
| 
 | |
| exports.describe_ast = function () {
 | |
|     var out = UglifyJS.OutputStream({ beautify: true });
 | |
|     function doitem(ctor) {
 | |
|         out.print("AST_" + ctor.TYPE);
 | |
|         var props = ctor.SELF_PROPS.filter(function(prop){
 | |
|             return !/^\$/.test(prop);
 | |
|         });
 | |
|         if (props.length > 0) {
 | |
|             out.space();
 | |
|             out.with_parens(function(){
 | |
|                 props.forEach(function(prop, i){
 | |
|                     if (i) out.space();
 | |
|                     out.print(prop);
 | |
|                 });
 | |
|             });
 | |
|         }
 | |
|         if (ctor.documentation) {
 | |
|             out.space();
 | |
|             out.print_string(ctor.documentation);
 | |
|         }
 | |
|         if (ctor.SUBCLASSES.length > 0) {
 | |
|             out.space();
 | |
|             out.with_block(function(){
 | |
|                 ctor.SUBCLASSES.forEach(function(ctor, i){
 | |
|                     out.indent();
 | |
|                     doitem(ctor);
 | |
|                     out.newline();
 | |
|                 });
 | |
|             });
 | |
|         }
 | |
|     };
 | |
|     doitem(UglifyJS.AST_Node);
 | |
|     return out + "";
 | |
| };
 | |
| },{"source-map":47,"util":32}],58:[function(require,module,exports){
 | |
| // jshint -W001
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| // Identifiers provided by the ECMAScript standard.
 | |
| 
 | |
| exports.reservedVars = {
 | |
|   arguments : false,
 | |
|   NaN       : false
 | |
| };
 | |
| 
 | |
| exports.ecmaIdentifiers = {
 | |
|   Array              : false,
 | |
|   Boolean            : false,
 | |
|   Date               : false,
 | |
|   decodeURI          : false,
 | |
|   decodeURIComponent : false,
 | |
|   encodeURI          : false,
 | |
|   encodeURIComponent : false,
 | |
|   Error              : false,
 | |
|   "eval"             : false,
 | |
|   EvalError          : false,
 | |
|   Function           : false,
 | |
|   hasOwnProperty     : false,
 | |
|   isFinite           : false,
 | |
|   isNaN              : false,
 | |
|   JSON               : false,
 | |
|   Math               : false,
 | |
|   Map                : false,
 | |
|   Number             : false,
 | |
|   Object             : false,
 | |
|   parseInt           : false,
 | |
|   parseFloat         : false,
 | |
|   RangeError         : false,
 | |
|   ReferenceError     : false,
 | |
|   RegExp             : false,
 | |
|   Set                : false,
 | |
|   String             : false,
 | |
|   SyntaxError        : false,
 | |
|   TypeError          : false,
 | |
|   URIError           : false,
 | |
|   WeakMap            : false
 | |
| };
 | |
| 
 | |
| // Global variables commonly provided by a web browser environment.
 | |
| 
 | |
| exports.browser = {
 | |
|   Audio                : false,
 | |
|   Blob                 : false,
 | |
|   addEventListener     : false,
 | |
|   applicationCache     : false,
 | |
|   atob                 : false,
 | |
|   blur                 : false,
 | |
|   btoa                 : false,
 | |
|   clearInterval        : false,
 | |
|   clearTimeout         : false,
 | |
|   close                : false,
 | |
|   closed               : false,
 | |
|   CustomEvent          : false,
 | |
|   DOMParser            : false,
 | |
|   defaultStatus        : false,
 | |
|   document             : false,
 | |
|   Element              : false,
 | |
|   ElementTimeControl   : false,
 | |
|   event                : false,
 | |
|   FileReader           : false,
 | |
|   FormData             : false,
 | |
|   focus                : false,
 | |
|   frames               : false,
 | |
|   getComputedStyle     : false,
 | |
|   HTMLElement          : false,
 | |
|   HTMLAnchorElement    : false,
 | |
|   HTMLBaseElement      : false,
 | |
|   HTMLBlockquoteElement: false,
 | |
|   HTMLBodyElement      : false,
 | |
|   HTMLBRElement        : false,
 | |
|   HTMLButtonElement    : false,
 | |
|   HTMLCanvasElement    : false,
 | |
|   HTMLDirectoryElement : false,
 | |
|   HTMLDivElement       : false,
 | |
|   HTMLDListElement     : false,
 | |
|   HTMLFieldSetElement  : false,
 | |
|   HTMLFontElement      : false,
 | |
|   HTMLFormElement      : false,
 | |
|   HTMLFrameElement     : false,
 | |
|   HTMLFrameSetElement  : false,
 | |
|   HTMLHeadElement      : false,
 | |
|   HTMLHeadingElement   : false,
 | |
|   HTMLHRElement        : false,
 | |
|   HTMLHtmlElement      : false,
 | |
|   HTMLIFrameElement    : false,
 | |
|   HTMLImageElement     : false,
 | |
|   HTMLInputElement     : false,
 | |
|   HTMLIsIndexElement   : false,
 | |
|   HTMLLabelElement     : false,
 | |
|   HTMLLayerElement     : false,
 | |
|   HTMLLegendElement    : false,
 | |
|   HTMLLIElement        : false,
 | |
|   HTMLLinkElement      : false,
 | |
|   HTMLMapElement       : false,
 | |
|   HTMLMenuElement      : false,
 | |
|   HTMLMetaElement      : false,
 | |
|   HTMLModElement       : false,
 | |
|   HTMLObjectElement    : false,
 | |
|   HTMLOListElement     : false,
 | |
|   HTMLOptGroupElement  : false,
 | |
|   HTMLOptionElement    : false,
 | |
|   HTMLParagraphElement : false,
 | |
|   HTMLParamElement     : false,
 | |
|   HTMLPreElement       : false,
 | |
|   HTMLQuoteElement     : false,
 | |
|   HTMLScriptElement    : false,
 | |
|   HTMLSelectElement    : false,
 | |
|   HTMLStyleElement     : false,
 | |
|   HTMLTableCaptionElement: false,
 | |
|   HTMLTableCellElement : false,
 | |
|   HTMLTableColElement  : false,
 | |
|   HTMLTableElement     : false,
 | |
|   HTMLTableRowElement  : false,
 | |
|   HTMLTableSectionElement: false,
 | |
|   HTMLTextAreaElement  : false,
 | |
|   HTMLTitleElement     : false,
 | |
|   HTMLUListElement     : false,
 | |
|   HTMLVideoElement     : false,
 | |
|   history              : false,
 | |
|   Image                : false,
 | |
|   length               : false,
 | |
|   localStorage         : false,
 | |
|   location             : false,
 | |
|   MessageChannel       : false,
 | |
|   MessageEvent         : false,
 | |
|   MessagePort          : false,
 | |
|   MouseEvent           : false,
 | |
|   moveBy               : false,
 | |
|   moveTo               : false,
 | |
|   MutationObserver     : false,
 | |
|   name                 : false,
 | |
|   Node                 : false,
 | |
|   NodeFilter           : false,
 | |
|   navigator            : false,
 | |
|   onbeforeunload       : true,
 | |
|   onblur               : true,
 | |
|   onerror              : true,
 | |
|   onfocus              : true,
 | |
|   onload               : true,
 | |
|   onresize             : true,
 | |
|   onunload             : true,
 | |
|   open                 : false,
 | |
|   openDatabase         : false,
 | |
|   opener               : false,
 | |
|   Option               : false,
 | |
|   parent               : false,
 | |
|   print                : false,
 | |
|   removeEventListener  : false,
 | |
|   resizeBy             : false,
 | |
|   resizeTo             : false,
 | |
|   screen               : false,
 | |
|   scroll               : false,
 | |
|   scrollBy             : false,
 | |
|   scrollTo             : false,
 | |
|   sessionStorage       : false,
 | |
|   setInterval          : false,
 | |
|   setTimeout           : false,
 | |
|   SharedWorker         : false,
 | |
|   status               : false,
 | |
|   SVGAElement          : false,
 | |
|   SVGAltGlyphDefElement: false,
 | |
|   SVGAltGlyphElement   : false,
 | |
|   SVGAltGlyphItemElement: false,
 | |
|   SVGAngle             : false,
 | |
|   SVGAnimateColorElement: false,
 | |
|   SVGAnimateElement    : false,
 | |
|   SVGAnimateMotionElement: false,
 | |
|   SVGAnimateTransformElement: false,
 | |
|   SVGAnimatedAngle     : false,
 | |
|   SVGAnimatedBoolean   : false,
 | |
|   SVGAnimatedEnumeration: false,
 | |
|   SVGAnimatedInteger   : false,
 | |
|   SVGAnimatedLength    : false,
 | |
|   SVGAnimatedLengthList: false,
 | |
|   SVGAnimatedNumber    : false,
 | |
|   SVGAnimatedNumberList: false,
 | |
|   SVGAnimatedPathData  : false,
 | |
|   SVGAnimatedPoints    : false,
 | |
|   SVGAnimatedPreserveAspectRatio: false,
 | |
|   SVGAnimatedRect      : false,
 | |
|   SVGAnimatedString    : false,
 | |
|   SVGAnimatedTransformList: false,
 | |
|   SVGAnimationElement  : false,
 | |
|   SVGCSSRule           : false,
 | |
|   SVGCircleElement     : false,
 | |
|   SVGClipPathElement   : false,
 | |
|   SVGColor             : false,
 | |
|   SVGColorProfileElement: false,
 | |
|   SVGColorProfileRule  : false,
 | |
|   SVGComponentTransferFunctionElement: false,
 | |
|   SVGCursorElement     : false,
 | |
|   SVGDefsElement       : false,
 | |
|   SVGDescElement       : false,
 | |
|   SVGDocument          : false,
 | |
|   SVGElement           : false,
 | |
|   SVGElementInstance   : false,
 | |
|   SVGElementInstanceList: false,
 | |
|   SVGEllipseElement    : false,
 | |
|   SVGExternalResourcesRequired: false,
 | |
|   SVGFEBlendElement    : false,
 | |
|   SVGFEColorMatrixElement: false,
 | |
|   SVGFEComponentTransferElement: false,
 | |
|   SVGFECompositeElement: false,
 | |
|   SVGFEConvolveMatrixElement: false,
 | |
|   SVGFEDiffuseLightingElement: false,
 | |
|   SVGFEDisplacementMapElement: false,
 | |
|   SVGFEDistantLightElement: false,
 | |
|   SVGFEFloodElement    : false,
 | |
|   SVGFEFuncAElement    : false,
 | |
|   SVGFEFuncBElement    : false,
 | |
|   SVGFEFuncGElement    : false,
 | |
|   SVGFEFuncRElement    : false,
 | |
|   SVGFEGaussianBlurElement: false,
 | |
|   SVGFEImageElement    : false,
 | |
|   SVGFEMergeElement    : false,
 | |
|   SVGFEMergeNodeElement: false,
 | |
|   SVGFEMorphologyElement: false,
 | |
|   SVGFEOffsetElement   : false,
 | |
|   SVGFEPointLightElement: false,
 | |
|   SVGFESpecularLightingElement: false,
 | |
|   SVGFESpotLightElement: false,
 | |
|   SVGFETileElement     : false,
 | |
|   SVGFETurbulenceElement: false,
 | |
|   SVGFilterElement     : false,
 | |
|   SVGFilterPrimitiveStandardAttributes: false,
 | |
|   SVGFitToViewBox      : false,
 | |
|   SVGFontElement       : false,
 | |
|   SVGFontFaceElement   : false,
 | |
|   SVGFontFaceFormatElement: false,
 | |
|   SVGFontFaceNameElement: false,
 | |
|   SVGFontFaceSrcElement: false,
 | |
|   SVGFontFaceUriElement: false,
 | |
|   SVGForeignObjectElement: false,
 | |
|   SVGGElement          : false,
 | |
|   SVGGlyphElement      : false,
 | |
|   SVGGlyphRefElement   : false,
 | |
|   SVGGradientElement   : false,
 | |
|   SVGHKernElement      : false,
 | |
|   SVGICCColor          : false,
 | |
|   SVGImageElement      : false,
 | |
|   SVGLangSpace         : false,
 | |
|   SVGLength            : false,
 | |
|   SVGLengthList        : false,
 | |
|   SVGLineElement       : false,
 | |
|   SVGLinearGradientElement: false,
 | |
|   SVGLocatable         : false,
 | |
|   SVGMPathElement      : false,
 | |
|   SVGMarkerElement     : false,
 | |
|   SVGMaskElement       : false,
 | |
|   SVGMatrix            : false,
 | |
|   SVGMetadataElement   : false,
 | |
|   SVGMissingGlyphElement: false,
 | |
|   SVGNumber            : false,
 | |
|   SVGNumberList        : false,
 | |
|   SVGPaint             : false,
 | |
|   SVGPathElement       : false,
 | |
|   SVGPathSeg           : false,
 | |
|   SVGPathSegArcAbs     : false,
 | |
|   SVGPathSegArcRel     : false,
 | |
|   SVGPathSegClosePath  : false,
 | |
|   SVGPathSegCurvetoCubicAbs: false,
 | |
|   SVGPathSegCurvetoCubicRel: false,
 | |
|   SVGPathSegCurvetoCubicSmoothAbs: false,
 | |
|   SVGPathSegCurvetoCubicSmoothRel: false,
 | |
|   SVGPathSegCurvetoQuadraticAbs: false,
 | |
|   SVGPathSegCurvetoQuadraticRel: false,
 | |
|   SVGPathSegCurvetoQuadraticSmoothAbs: false,
 | |
|   SVGPathSegCurvetoQuadraticSmoothRel: false,
 | |
|   SVGPathSegLinetoAbs  : false,
 | |
|   SVGPathSegLinetoHorizontalAbs: false,
 | |
|   SVGPathSegLinetoHorizontalRel: false,
 | |
|   SVGPathSegLinetoRel  : false,
 | |
|   SVGPathSegLinetoVerticalAbs: false,
 | |
|   SVGPathSegLinetoVerticalRel: false,
 | |
|   SVGPathSegList       : false,
 | |
|   SVGPathSegMovetoAbs  : false,
 | |
|   SVGPathSegMovetoRel  : false,
 | |
|   SVGPatternElement    : false,
 | |
|   SVGPoint             : false,
 | |
|   SVGPointList         : false,
 | |
|   SVGPolygonElement    : false,
 | |
|   SVGPolylineElement   : false,
 | |
|   SVGPreserveAspectRatio: false,
 | |
|   SVGRadialGradientElement: false,
 | |
|   SVGRect              : false,
 | |
|   SVGRectElement       : false,
 | |
|   SVGRenderingIntent   : false,
 | |
|   SVGSVGElement        : false,
 | |
|   SVGScriptElement     : false,
 | |
|   SVGSetElement        : false,
 | |
|   SVGStopElement       : false,
 | |
|   SVGStringList        : false,
 | |
|   SVGStylable          : false,
 | |
|   SVGStyleElement      : false,
 | |
|   SVGSwitchElement     : false,
 | |
|   SVGSymbolElement     : false,
 | |
|   SVGTRefElement       : false,
 | |
|   SVGTSpanElement      : false,
 | |
|   SVGTests             : false,
 | |
|   SVGTextContentElement: false,
 | |
|   SVGTextElement       : false,
 | |
|   SVGTextPathElement   : false,
 | |
|   SVGTextPositioningElement: false,
 | |
|   SVGTitleElement      : false,
 | |
|   SVGTransform         : false,
 | |
|   SVGTransformList     : false,
 | |
|   SVGTransformable     : false,
 | |
|   SVGURIReference      : false,
 | |
|   SVGUnitTypes         : false,
 | |
|   SVGUseElement        : false,
 | |
|   SVGVKernElement      : false,
 | |
|   SVGViewElement       : false,
 | |
|   SVGViewSpec          : false,
 | |
|   SVGZoomAndPan        : false,
 | |
|   TimeEvent            : false,
 | |
|   top                  : false,
 | |
|   WebSocket            : false,
 | |
|   window               : false,
 | |
|   Worker               : false,
 | |
|   XMLHttpRequest       : false,
 | |
|   XMLSerializer        : false,
 | |
|   XPathEvaluator       : false,
 | |
|   XPathException       : false,
 | |
|   XPathExpression      : false,
 | |
|   XPathNamespace       : false,
 | |
|   XPathNSResolver      : false,
 | |
|   XPathResult          : false
 | |
| };
 | |
| 
 | |
| exports.devel = {
 | |
|   alert  : false,
 | |
|   confirm: false,
 | |
|   console: false,
 | |
|   Debug  : false,
 | |
|   opera  : false,
 | |
|   prompt : false
 | |
| };
 | |
| 
 | |
| exports.worker = {
 | |
|   importScripts: true,
 | |
|   postMessage  : true,
 | |
|   self         : true
 | |
| };
 | |
| 
 | |
| // Widely adopted global names that are not part of ECMAScript standard
 | |
| exports.nonstandard = {
 | |
|   escape  : false,
 | |
|   unescape: false
 | |
| };
 | |
| 
 | |
| // Globals provided by popular JavaScript environments.
 | |
| 
 | |
| exports.couch = {
 | |
|   "require" : false,
 | |
|   respond   : false,
 | |
|   getRow    : false,
 | |
|   emit      : false,
 | |
|   send      : false,
 | |
|   start     : false,
 | |
|   sum       : false,
 | |
|   log       : false,
 | |
|   exports   : false,
 | |
|   module    : false,
 | |
|   provides  : false
 | |
| };
 | |
| 
 | |
| exports.node = {
 | |
|   __filename    : false,
 | |
|   __dirname     : false,
 | |
|   Buffer        : false,
 | |
|   console       : false,
 | |
|   exports       : true,  // In Node it is ok to exports = module.exports = foo();
 | |
|   GLOBAL        : false,
 | |
|   global        : false,
 | |
|   module        : false,
 | |
|   process       : false,
 | |
|   require       : false,
 | |
|   setTimeout    : false,
 | |
|   clearTimeout  : false,
 | |
|   setInterval   : false,
 | |
|   clearInterval : false,
 | |
|   setImmediate  : false, // v0.9.1+
 | |
|   clearImmediate: false  // v0.9.1+
 | |
| };
 | |
| 
 | |
| exports.phantom = {
 | |
|   phantom      : true,
 | |
|   require      : true,
 | |
|   WebPage      : true,
 | |
|   console      : true, // in examples, but undocumented
 | |
|   exports      : true  // v1.7+
 | |
| };
 | |
| 
 | |
| exports.rhino = {
 | |
|   defineClass  : false,
 | |
|   deserialize  : false,
 | |
|   gc           : false,
 | |
|   help         : false,
 | |
|   importPackage: false,
 | |
|   "java"       : false,
 | |
|   load         : false,
 | |
|   loadClass    : false,
 | |
|   print        : false,
 | |
|   quit         : false,
 | |
|   readFile     : false,
 | |
|   readUrl      : false,
 | |
|   runCommand   : false,
 | |
|   seal         : false,
 | |
|   serialize    : false,
 | |
|   spawn        : false,
 | |
|   sync         : false,
 | |
|   toint32      : false,
 | |
|   version      : false
 | |
| };
 | |
| 
 | |
| exports.shelljs = {
 | |
|   target       : false,
 | |
|   echo         : false,
 | |
|   exit         : false,
 | |
|   cd           : false,
 | |
|   pwd          : false,
 | |
|   ls           : false,
 | |
|   find         : false,
 | |
|   cp           : false,
 | |
|   rm           : false,
 | |
|   mv           : false,
 | |
|   mkdir        : false,
 | |
|   test         : false,
 | |
|   cat          : false,
 | |
|   sed          : false,
 | |
|   grep         : false,
 | |
|   which        : false,
 | |
|   dirs         : false,
 | |
|   pushd        : false,
 | |
|   popd         : false,
 | |
|   env          : false,
 | |
|   exec         : false,
 | |
|   chmod        : false,
 | |
|   config       : false,
 | |
|   error        : false,
 | |
|   tempdir      : false
 | |
| };
 | |
| 
 | |
| exports.typed = {
 | |
|   ArrayBuffer         : false,
 | |
|   ArrayBufferView     : false,
 | |
|   DataView            : false,
 | |
|   Float32Array        : false,
 | |
|   Float64Array        : false,
 | |
|   Int16Array          : false,
 | |
|   Int32Array          : false,
 | |
|   Int8Array           : false,
 | |
|   Uint16Array         : false,
 | |
|   Uint32Array         : false,
 | |
|   Uint8Array          : false,
 | |
|   Uint8ClampedArray   : false
 | |
| };
 | |
| 
 | |
| exports.wsh = {
 | |
|   ActiveXObject            : true,
 | |
|   Enumerator               : true,
 | |
|   GetObject                : true,
 | |
|   ScriptEngine             : true,
 | |
|   ScriptEngineBuildVersion : true,
 | |
|   ScriptEngineMajorVersion : true,
 | |
|   ScriptEngineMinorVersion : true,
 | |
|   VBArray                  : true,
 | |
|   WSH                      : true,
 | |
|   WScript                  : true,
 | |
|   XDomainRequest           : true
 | |
| };
 | |
| 
 | |
| // Globals provided by popular JavaScript libraries.
 | |
| 
 | |
| exports.dojo = {
 | |
|   dojo     : false,
 | |
|   dijit    : false,
 | |
|   dojox    : false,
 | |
|   define   : false,
 | |
|   "require": false
 | |
| };
 | |
| 
 | |
| exports.jquery = {
 | |
|   "$"    : false,
 | |
|   jQuery : false
 | |
| };
 | |
| 
 | |
| exports.mootools = {
 | |
|   "$"           : false,
 | |
|   "$$"          : false,
 | |
|   Asset         : false,
 | |
|   Browser       : false,
 | |
|   Chain         : false,
 | |
|   Class         : false,
 | |
|   Color         : false,
 | |
|   Cookie        : false,
 | |
|   Core          : false,
 | |
|   Document      : false,
 | |
|   DomReady      : false,
 | |
|   DOMEvent      : false,
 | |
|   DOMReady      : false,
 | |
|   Drag          : false,
 | |
|   Element       : false,
 | |
|   Elements      : false,
 | |
|   Event         : false,
 | |
|   Events        : false,
 | |
|   Fx            : false,
 | |
|   Group         : false,
 | |
|   Hash          : false,
 | |
|   HtmlTable     : false,
 | |
|   Iframe        : false,
 | |
|   IframeShim    : false,
 | |
|   InputValidator: false,
 | |
|   instanceOf    : false,
 | |
|   Keyboard      : false,
 | |
|   Locale        : false,
 | |
|   Mask          : false,
 | |
|   MooTools      : false,
 | |
|   Native        : false,
 | |
|   Options       : false,
 | |
|   OverText      : false,
 | |
|   Request       : false,
 | |
|   Scroller      : false,
 | |
|   Slick         : false,
 | |
|   Slider        : false,
 | |
|   Sortables     : false,
 | |
|   Spinner       : false,
 | |
|   Swiff         : false,
 | |
|   Tips          : false,
 | |
|   Type          : false,
 | |
|   typeOf        : false,
 | |
|   URI           : false,
 | |
|   Window        : false
 | |
| };
 | |
| 
 | |
| exports.prototypejs = {
 | |
|   "$"               : false,
 | |
|   "$$"              : false,
 | |
|   "$A"              : false,
 | |
|   "$F"              : false,
 | |
|   "$H"              : false,
 | |
|   "$R"              : false,
 | |
|   "$break"          : false,
 | |
|   "$continue"       : false,
 | |
|   "$w"              : false,
 | |
|   Abstract          : false,
 | |
|   Ajax              : false,
 | |
|   Class             : false,
 | |
|   Enumerable        : false,
 | |
|   Element           : false,
 | |
|   Event             : false,
 | |
|   Field             : false,
 | |
|   Form              : false,
 | |
|   Hash              : false,
 | |
|   Insertion         : false,
 | |
|   ObjectRange       : false,
 | |
|   PeriodicalExecuter: false,
 | |
|   Position          : false,
 | |
|   Prototype         : false,
 | |
|   Selector          : false,
 | |
|   Template          : false,
 | |
|   Toggle            : false,
 | |
|   Try               : false,
 | |
|   Autocompleter     : false,
 | |
|   Builder           : false,
 | |
|   Control           : false,
 | |
|   Draggable         : false,
 | |
|   Draggables        : false,
 | |
|   Droppables        : false,
 | |
|   Effect            : false,
 | |
|   Sortable          : false,
 | |
|   SortableObserver  : false,
 | |
|   Sound             : false,
 | |
|   Scriptaculous     : false
 | |
| };
 | |
| 
 | |
| exports.yui = {
 | |
|   YUI       : false,
 | |
|   Y         : false,
 | |
|   YUI_config: false
 | |
| };
 | |
| 
 | |
| },{}]},{},[5])
 | |
| (5)
 | |
| }); |